diff --git a/utilities/tools/ConvertTo-ARMTemplate.ps1 b/utilities/tools/ConvertTo-ARMTemplate.ps1 index ca5f4ae519..769ee7fb82 100644 --- a/utilities/tools/ConvertTo-ARMTemplate.ps1 +++ b/utilities/tools/ConvertTo-ARMTemplate.ps1 @@ -5,140 +5,180 @@ This script converts the module library from bicep to json based ARM templates. .DESCRIPTION The script finds all 'deploy.bicep' files and tries to convert them to json based ARM templates by using the following steps. -1. Remove existing deploy.json files (to avoid collision) -2.a Convert deploy.bicep to deploy.json using bicep build -2.b Clean up the json file, i.e removing the metadata property -3. Remove bicep files and folders -4. Replace deploy.bicep with deploy.json in workflow files +1. Remove existing deploy.json files +2. Convert bicep files to json +3. Remove Bicep metadata from json +4. Remove bicep files and folders +5. Update workflow files - Replace .bicep with .json in workflow files .PARAMETER Path -Mandatory. The path to the root of the repo. +Optional. The path to the root of the repo. -.PARAMETER CleanUp -Optional. Perform cleanup of bicep files after conversion. +.PARAMETER ConvertChildren +Optional. Convert child resource modules to bicep. + +.PARAMETER SkipMetadataCleanup +Optional. Skip Cleanup of Bicep metadata from json files + +.PARAMETER SkipBicepCleanUp +Optional. Skip removal of bicep files and folders + +.PARAMETER SkipWorkflowUpdate +Optional. Skip replacing .bicep with .json in workflow files .EXAMPLE -. .\utilities\tools\ConvertTo-ARMTemplate.ps1 -Path . -CleanUp -Verbose +. .\utilities\tools\ConvertTo-ARMTemplate.ps1 + +Converts top level bicep modules to json based ARM template, cleaning up all bicep files and folders and updating the workflow files to use the json files. + +.EXAMPLE +. .\utilities\tools\ConvertTo-ARMTemplate.ps1 -ConvertChildren -SkipMetadataCleanup -SkipBicepCleanUp -SkipWorkflowUpdate + +Only converts top level bicep modules to json based ARM template, keeping metadata in json, keeping all bicep files and folders, and not updating workflows. -Converts bicep modules to json based ARM template, cleaning up all bicep files after conversion. #> [CmdletBinding(SupportsShouldProcess)] param ( - [Parameter(Mandatory)] - [string] $Path, + [Parameter(Mandatory = $false)] + [string] $Path = (Get-Location).Path, + + [Parameter(Mandatory = $false)] + [switch] $ConvertChildren, + + [Parameter(Mandatory = $false)] + [switch] $SkipMetadataCleanup, - [Parameter()] - [switch] $CleanUp + [Parameter(Mandatory = $false)] + [switch] $SkipBicepCleanUp, + + [Parameter(Mandatory = $false)] + [switch] $SkipWorkflowUpdate ) -#region Helper functions +$rootPath = Get-Item -Path $Path | Select-Object -ExpandProperty 'FullName' +$armFolderPath = Join-Path -Path $rootPath -ChildPath 'arm' +if ($ConvertChildren) { + $BicepFilesToConvert = Get-ChildItem -Path $armFolderPath -Filter 'deploy.bicep' -Recurse -Force +} else { + $BicepFilesToConvert = Get-ChildItem -Path $armFolderPath -Filter 'deploy.bicep' -Recurse -Force -Depth 2 +} -<# -.SYNOPSIS -A function to recursively remove 'metadata' property from a provided object. -This object is expected to be an ARM template converted to a PowerShell custom object. -The function uses the object reference rather than recreating/copying the object. +#region Remove existing deploy.json files +Write-Verbose 'Remove existing deploy.json files' -.PARAMETER TemplateObject -Mandatory. The ARM template converted to a PowerShell custom object. +$JsonFilesToRemove = Get-ChildItem -Path $armFolderPath -Filter 'deploy.json' -Recurse -Force -File +Write-Verbose "Remove existing deploy.json files - Remove [$($JsonFilesToRemove.count)] file(s)" +if ($PSCmdlet.ShouldProcess("[$($JsonFilesToRemove.count)] deploy.json files(s) in path [$armFolderPath]", 'Remove-Item')) { + $JsonFilesToRemove | Remove-Item -Force +} -.EXAMPLE -$JSONFileContent = Get-Content -Path $JSONFilePath -$JSONObj = $JSONFileContent | ConvertFrom-Json -Remove-JSONMetadata -TemplateObject $JSONObj +Write-Verbose 'Remove existing deploy.json files - Done' +#endregion -Reads content from a ARM/JSON file, converts it to a PSCustomObject and removes 'metadata' property under the template and recursively on all nested deployments. +#region Convert bicep files to json +Write-Verbose 'Convert bicep files to json' -#> -function Remove-JSONMetadata { - [CmdletBinding()] - param ( - [Parameter(Mandatory)] - [psobject] $TemplateObject - ) - $TemplateObject.PSObject.Properties.Remove('metadata') - $TemplateObject.resources | Where-Object { $_.type -eq 'Microsoft.Resources/deployments' } | ForEach-Object { - Remove-JSONMetadata -TemplateObject $_.properties.template +Write-Verbose "Convert bicep files to json - Processing [$($BicepFilesToConvert.count)] file(s)" +if ($PSCmdlet.ShouldProcess("[$($BicepFilesToConvert.count)] deploy.bicep file(s) in path [$armFolderPath]", 'az bicep build')) { + $BicepFilesToConvert | ForEach-Object -ThrottleLimit $env:NUMBER_OF_PROCESSORS -Parallel { + Invoke-Expression -Command "az bicep build --file '$_'" } } +Write-Verbose 'Convert bicep files to json - Done' #endregion -$rootPath = Get-Item -Path $Path | Select-Object -ExpandProperty 'FullName' -$armFolderPath = Join-Path -Path $rootPath -ChildPath 'arm' +#region Remove Bicep metadata from json +if (-not $SkipMetadataCleanup) { + Write-Verbose 'Remove Bicep metadata from json' + + Write-Verbose "Remove Bicep metadata from json - Processing [$($BicepFilesToConvert.count)] file(s)" + if ($PSCmdlet.ShouldProcess("[$($BicepFilesToConvert.count)] deploy.bicep file(s) in path [$armFolderPath]", 'Set-Content')) { + $BicepFilesToConvert | ForEach-Object -ThrottleLimit $env:NUMBER_OF_PROCESSORS -Parallel { + + function Remove-JSONMetadata { + <# + .SYNOPSIS + A function to recursively remove 'metadata' property from a provided object. + This object is expected to be an ARM template converted to a PowerShell custom object. + The function uses the object reference rather than recreating/copying the object. + + .PARAMETER TemplateObject + Mandatory. The ARM template converted to a PowerShell custom object. + + .EXAMPLE + $JSONFileContent = Get-Content -Path $JSONFilePath + $JSONObj = $JSONFileContent | ConvertFrom-Json + Remove-JSONMetadata -TemplateObject $JSONObj + + Reads content from a ARM/JSON file, converts it to a PSCustomObject and removes 'metadata' property under the template and recursively on all nested deployments. + + #> + + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [psobject] $TemplateObject + ) + $TemplateObject.PSObject.Properties.Remove('metadata') + $TemplateObject.resources | Where-Object { $_.type -eq 'Microsoft.Resources/deployments' } | ForEach-Object { + Remove-JSONMetadata -TemplateObject $_.properties.template + } + } -# Get all bicep files -$bicepFiles = Get-ChildItem -Path $armFolderPath -Filter 'deploy.bicep' -Recurse -Force -Write-Verbose "Convert bicep to json - $($bicepFiles.count) files" -foreach ($bicepFile in $bicepFiles) { - $bicepFilePath = $bicepFile.FullName - Write-Verbose "$bicepFilePath - Processing" - $moduleFolderPath = $bicepFile.Directory.FullName - Write-Verbose "$bicepFilePath - ModuleFolderPath - $moduleFolderPath" - $JSONFilePath = Join-Path -Path $moduleFolderPath -ChildPath 'deploy.json' - Write-Verbose "$bicepFilePath - JSONFilePath - $JSONFilePath" - $bicepFolderPath = Join-Path -Path $moduleFolderPath -ChildPath '.bicep' - - # Remove existing json files - Write-Verbose "$bicepFilePath - Removing deploy.json" - if (Test-Path -Path $JSONFilePath) { - if ($PSCmdlet.ShouldProcess("File in path [$bicepFilePath]", 'Remove')) { - Remove-Item -Path $JSONFilePath -Force -Verbose + $moduleFolderPath = $_.Directory.FullName + $JSONFilePath = Join-Path -Path $moduleFolderPath -ChildPath 'deploy.json' + if (Test-Path -Path $JSONFilePath) { + $JSONFileContent = Get-Content -Path $JSONFilePath + $JSONObj = $JSONFileContent | ConvertFrom-Json + Remove-JSONMetadata -TemplateObject $JSONObj + $JSONFileContent = $JSONObj | ConvertTo-Json -Depth 100 + Set-Content -Value $JSONFileContent -Path $JSONFilePath + } else { + Write-Warning "Remove Bicep metadata from json - Skipped $JSONFilePath - File not found (deploy.json)" + } } - Write-Verbose "$bicepFilePath - Removing deploy.json - Done" - } else { - Write-Verbose "$bicepFilePath - Removing deploy.json - Skipped - Nothing to delete" } - # Convert bicep to json - Write-Verbose "$bicepFilePath - Convert to json" - if ($PSCmdlet.ShouldProcess("File in path [$bicepFilePath]", 'Convert')) { - az bicep build --file $bicepFilePath --outfile $JSONFilePath - } - Write-Verbose "$bicepFilePath - Convert to json - Done" - - # Cleanup json, remove metadata property in json - Write-Verbose "$bicepFilePath - Clean up json" - if (Test-Path -Path $JSONFilePath) { - $JSONFileContent = Get-Content -Path $JSONFilePath - $JSONObj = $JSONFileContent | ConvertFrom-Json - Remove-JSONMetadata -TemplateObject $JSONObj - $JSONFileContent = $JSONObj | ConvertTo-Json -Depth 100 - if ($PSCmdlet.ShouldProcess("File in path [$JSONFilePath]", 'Overwrite')) { - Set-Content -Value $JSONFileContent -Path $JSONFilePath - } - Write-Verbose "$bicepFilePath - Clean up json - Done" - } else { - Write-Verbose "$bicepFilePath - Clean up json - Skipped - File not found (deploy.json)" + Write-Verbose 'Remove Bicep metadata from json - Done' +} +#endregion + +#region Remove bicep files and folders +if (-not $SkipBicepCleanUp) { + Write-Verbose 'Remove bicep files and folders' + + $dotBicepFoldersToRemove = Get-ChildItem -Path $armFolderPath -Filter '.bicep' -Recurse -Force -Directory + Write-Verbose "Remove bicep files and folders - Remove [$($dotBicepFoldersToRemove.count)] .bicep folder(s)" + if ($PSCmdlet.ShouldProcess("[$($dotBicepFoldersToRemove.count)] .bicep folder(s) in path [$armFolderPath]", 'Remove-Item')) { + $dotBicepFoldersToRemove | Remove-Item -Recurse -Force } - # Remove bicep files and folders - Write-Verbose "$bicepFilePath - Clean up bicep files" - if ($CleanUp) { - if ($PSCmdlet.ShouldProcess("File in path [$bicepFilePath]", 'Remove')) { - Remove-Item -Path $bicepFilePath -Force - } - Write-Verbose "$bicepFilePath - Clean up bicep files - Removed deploy.bicep" - if (Test-Path -Path $bicepFolderPath) { - if ($PSCmdlet.ShouldProcess("File in path [$bicepFolderPath]", 'Remove')) { - Remove-Item -Path $bicepFolderPath -Recurse -Force - } - Write-Verbose "$bicepFilePath - Clean up bicep files - Removed .bicep folder" - } + $BicepFilesToRemove = Get-ChildItem -Path $armFolderPath -Filter '*.bicep' -Recurse -Force -File + Write-Verbose "Remove bicep files and folders - Remove [$($BicepFilesToRemove.count)] *.bicep file(s)" + if ($PSCmdlet.ShouldProcess("[$($BicepFilesToRemove.count)] *.bicep file(s) in path [$armFolderPath]", 'Remove-Item')) { + $BicepFilesToRemove | Remove-Item -Force } + + Write-Verbose 'Remove bicep files and folders - Done' } +#endregion -# Replace .bicep with .json in workflow files -$workflowFolderPath = Join-Path -Path $rootPath -ChildPath '.github\workflows' -$workflowFiles = Get-ChildItem -Path $workflowFolderPath -Filter 'ms.*.yml' -File -Force -Write-Verbose "Update workflow files - $($workflowFiles.count) files" -foreach ($workflowFile in $workflowFiles) { - Write-Verbose "$workflowFile - Processing" - $content = Get-Content -Path $workflowFile - $content = $content.Replace("deploy.bicep'", "deploy.json'") - if ($PSCmdlet.ShouldProcess("File in path [$workflowFile]", 'Overwrite')) { - Set-Content -Value $content -Path $workflowFile +#region Update workflow files - Replace .bicep with .json in workflow files +if (-not $SkipWorkflowUpdate) { + Write-Verbose 'Update workflow files' + + $workflowFolderPath = Join-Path -Path $rootPath -ChildPath '.github\workflows' + $workflowFilesToUpdate = Get-ChildItem -Path $workflowFolderPath -Filter 'ms.*.yml' -File -Force + Write-Verbose "Update workflow files - Processing [$($workflowFilesToUpdate.count)] file(s)" + if ($PSCmdlet.ShouldProcess("[$($workflowFilesToUpdate.count)] ms.*.yml file(s) in path [$armFolderPath]", 'Set-Content')) { + $workflowFilesToUpdate | ForEach-Object -ThrottleLimit $env:NUMBER_OF_PROCESSORS -Parallel { + $content = $_ | Get-Content + $content = $content.Replace('deploy.bicep', 'deploy.json') + $_ | Set-Content -Value $content + } } - Write-Verbose "$workflowFile - Processing - Done" + + Write-Verbose 'Update workflow files - Done' } -Write-Verbose "Update workflow files - $($workflowFiles.count) files - Done" +#endregion