alz/azuredevops/pipelines/bicep/templates/helpers/bicep-deploy.yaml (153 lines of code) (raw):

--- parameters: - name: serviceConnection type: string - name: whatIfEnabled type: boolean default: true - name: scriptFiles type: object default: - displayName: "Example Deployment" templateFilePath: "./infra-as-code/bicep/modules/example/example.bicep" templateParametersFilePath: "./config/custom-parameters/example.parameters.all.json" managementGroupId: "00000000-0000-0000-0000-000000000000" subscriptionId: "00000000-0000-0000-0000-000000000000" resourceGroupName: "example-rg" location: "uksouth" deploymentType: "subscription" # tenant | managementGroup | subscription | resourceGroup firstRunWhatIf: true runStep: true steps: - task: AzurePowerShell@5 displayName: Check for First Deployment condition: eq($${{ parameters.whatIfEnabled }}, true) inputs: azureSubscription: $${{ parameters.serviceConnection }} pwsh: true azurePowerShellVersion: LatestVersion ScriptType: "InlineScript" Inline: | $managementGroupId = $env:MANAGEMENT_GROUP_ID $managementGroups = Get-AzManagementGroup $managementGroup = $managementGroups | Where-Object { $_.Name -eq $managementGroupId } $firstDeployment = $true if($managementGroup -eq $null) { Write-Warning "Cannot find the $managementGroupId Management Group, so assuming this is the first deployment. We must skip checking some deployments since their dependent resources do not exist yet." } else { Write-Host "Found the $managementGroupId Management Group, so assuming this is not the first deployment." $firstDeployment = $false } Write-Host "##vso[task.setvariable variable=FIRST_DEPLOYMENT;]$firstDeployment" - $${{ each scriptFile in parameters.scriptFiles }}: - task: AzurePowerShell@5 displayName: $${{ scriptFile.displayName }} condition: and(succeeded(), eq($${{ scriptFile.runStep }}, true)) inputs: azureSubscription: $${{ parameters.serviceConnection }} pwsh: true azurePowerShellVersion: LatestVersion ScriptType: "InlineScript" Inline: | $whatIf = [System.Convert]::ToBoolean("$${{ parameters.whatIfEnabled }}") $firstDeploymentString = $env:FIRST_DEPLOYMENT $firstDeployment = $true if($firstDeploymentString -eq "") { $firstDeployment = $false } else { $firstDeployment = [System.Convert]::ToBoolean($firstDeploymentString) } $firstRunWhatIf = [System.Convert]::ToBoolean("$${{ scriptFile.firstRunWhatIf }}") if($whatIf -and $firstDeployment -and !$firstRunWhatIf) { Write-Warning "Skipping the WhatIf check as the deployment is dependent on resources that do not exist yet..." exit 0 } $deploymentType = "$${{ scriptFile.deploymentType }}" $deploymentPrefix = $env:PREFIX $deploymentName = "$${{ scriptFile.displayName }}".Replace(" ", "-") $deploymentTimeStamp = Get-Date -Format 'yyyyMMddHHmmss' $prefixPostFixAndHythenLength = $deploymentPrefix.Length + $deploymentTimeStamp.Length + 2 $deploymentNameMaxLength = 61 - $prefixPostFixAndHythenLength if($deploymentName.Length -gt $deploymentNameMaxLength) { $deploymentName = $deploymentName.Substring(0, $deploymentNameMaxLength) } $deploymentName = "$deploymentPrefix-$deploymentName-$deploymentTimeStamp" Write-Host "Deployment Name: $deploymentName" $inputObject = @{ TemplateFile = "$${{ scriptFile.templateFilePath }}" TemplateParameterFile = "$${{ scriptFile.templateParametersFilePath }}" WhatIf = $whatIf Verbose = $true } $retryCount = 0 $retryMax = 10 $initialRetryDelay = 20 $retryDelayIncrement = 10 $finalSuccess = $false while ($retryCount -lt $retryMax) { $retryAttempt = '{0:d2}' -f ($retryCount + 1) if($retryCount -gt 0) { $retryDelay = $initialRetryDelay + ($retryCount * $retryDelayIncrement) Write-Host "Retrying deployment with attempt number $retryAttempt after $retryDelay seconds..." -ForegroundColor Green Start-Sleep -Seconds $retryDelay Write-Host "Retrying deployment..." -ForegroundColor Green } $inputObject.DeploymentName = "$deploymentName-$retryAttempt" $result = $null try { if ($deploymentType -eq "tenant") { $inputObject.Location = "$${{ scriptFile.location }}" $result = New-AzTenantDeployment @inputObject } if ($deploymentType -eq "managementGroup") { $inputObject.Location = "$${{ scriptFile.location }}" $inputObject.ManagementGroupId = "$${{ scriptFile.managementGroupId }}" if ($inputObject.ManagementGroupId -eq "") { $inputObject.ManagementGroupId = (Get-AzContext).Tenant.TenantId } $result = New-AzManagementGroupDeployment @inputObject } if ($deploymentType -eq "subscription") { $inputObject.Location = "$${{ scriptFile.location }}" Select-AzSubscription -SubscriptionId "$${{ scriptFile.subscriptionId }}" $result = New-AzSubscriptionDeployment @inputObject } if ($deploymentType -eq "resourceGroup") { $inputObject.ResourceGroupName = "$${{ scriptFile.resourceGroupName }}" Select-AzSubscription -SubscriptionId "$${{ scriptFile.subscriptionId }}" $result = New-AzResourceGroupDeployment @inputObject } } catch { Write-Host $_ -ForegroundColor Red Write-Host "Deployment failed with exception, this is likely an intermittent failure so entering retry loop..." -ForegroundColor Red $retryCount++ continue } if ($whatIf) { $result | Format-List | Out-Host exit 0 } $resultId = "" if($deploymentType -eq "resourceGroup") { $resultId = "/subscriptions/$${{ scriptFile.subscriptionId }}/resourceGroups/$${{ scriptFile.resourceGroupName }}/providers/Microsoft.Resources/deployments/$deploymentName" } else { $resultId = $result.Id } $resultIdEscaped = $resultId.Replace("/", "%2F") $resultUrl = "https://portal.azure.com/#view/HubsExtension/DeploymentDetailsBlade/~/overview/id/$resultIdEscaped" Write-Host "Deployment Name: $deploymentName" Write-Host "Deployment ID: $resultId" Write-Host "Deployment Url: $resultUrl" $result | Format-List | Out-Host if($result.ProvisioningState -ne "Succeeded") { Write-Host "Deployment failed with unsuccessful provisioning state, this is likely an intermittent failure so entering retry loop..." -ForegroundColor Red $retryCount++ } else { $finalSuccess = $true break } } if($finalSuccess -eq $false) { Write-Error "Deployment failed after $retryMax attempts..." exit 1 }