internal/functions/New-AzOpsDeployment.ps1
function New-AzOpsDeployment { <# .SYNOPSIS Deploys a full state into azure. .DESCRIPTION Deploys a full state into azure. .PARAMETER DeploymentName Name under which to deploy the state. .PARAMETER TemplateFilePath Path where the ARM templates can be found. .PARAMETER TemplateParameterFilePath Path where the parameters of the ARM templates can be found. .PARAMETER Mode Mode in which to process the templates. Defaults to incremental. .PARAMETER StatePath The root folder under which to find the resource json. .PARAMETER Confirm If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. .PARAMETER WhatIf If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. .EXAMPLE > $AzOpsDeploymentList | Select-Object $uniqueProperties -Unique | Sort-Object -Property TemplateParameterFilePath | New-Deployment Deploy all unique deployments provided from $AzOpsDeploymentList #> [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter(ValueFromPipelineByPropertyName = $true)] [string] $DeploymentName = "azops-template-deployment", [Parameter(ValueFromPipelineByPropertyName = $true)] [string] $TemplateFilePath = (Get-PSFConfigValue -FullName 'AzOps.Core.MainTemplate'), [Parameter(ValueFromPipelineByPropertyName = $true)] [AllowEmptyString()] [AllowNull()] [string] $TemplateParameterFilePath, [string] $Mode = "Incremental", [string] $StatePath = (Get-PSFConfigValue -FullName 'AzOps.Core.State'), [string[]] $WhatifExcludedChangeTypes = (Get-PSFConfigValue -FullName 'AzOps.Core.WhatifExcludedChangeTypes') ) process { Write-PSFMessage -Level Important -String 'New-AzOpsDeployment.Processing' -StringValues $DeploymentName, $TemplateFilePath, $TemplateParameterFilePath, $Mode -Target $TemplateFilePath #region Resolve Scope try { if ($TemplateParameterFilePath) { $scopeObject = New-AzOpsScope -Path $TemplateParameterFilePath -StatePath $StatePath -ErrorAction Stop -WhatIf:$false } else { $scopeObject = New-AzOpsScope -Path $TemplateFilePath -StatePath $StatePath -ErrorAction Stop -WhatIf:$false } } catch { Write-PSFMessage -Level Warning -String 'New-AzOpsDeployment.Scope.Failed' -Target $TemplateFilePath -StringValues $TemplateFilePath, $TemplateParameterFilePath -ErrorRecord $_ return } if (-not $scopeObject) { Write-PSFMessage -Level Warning -String 'New-AzOpsDeployment.Scope.Empty' -Target $TemplateFilePath -StringValues $TemplateFilePath, $TemplateParameterFilePath return } #endregion Resolve Scope #region Parse Content $templateContent = Get-Content $TemplateFilePath | ConvertFrom-Json -AsHashtable if ($templateContent.metadata._generator.name -eq 'bicep') { # Detect bicep templates $bicepTemplate = $true } #endregion #region Process Scope #region Resource Group if ($scopeObject.resourcegroup) { Set-AzOpsContext -ScopeObject $scopeObject if ($templateContent.resources[0].type -eq 'Microsoft.Resources/resourceGroups') { # Since this is a deployment for resource group, it must be invoked at subscription scope $defaultDeploymentRegion = Get-PSFConfigValue -FullName 'AzOps.Core.DefaultDeploymentRegion' Write-PSFMessage -Level Verbose -String 'New-AzOpsDeployment.Subscription.Processing' -StringValues $defaultDeploymentRegion, $scopeObject -Target $scopeObject $parameters = @{ 'TemplateFile' = $TemplateFilePath 'Location' = $defaultDeploymentRegion 'SkipTemplateParameterPrompt' = $true } if ($TemplateParameterFilePath) { $parameters.TemplateParameterFile = $TemplateParameterFilePath } if ((Get-AzContext).Subscription.Id -ne $scopeObject.subscription) { Set-AzOpsContext -ScopeObject $scopeObject } if ($WhatifExcludedChangeTypes) { $parameters.ExcludeChangeType = $WhatifExcludedChangeTypes } # Validate Template $results = Get-AzSubscriptionDeploymentWhatIfResult @parameters -ErrorAction Continue -ErrorVariable resultsError if ($parameters.ExcludeChangeType) { $parameters.Remove('ExcludeChangeType') } if ($resultsError) { if ($resultsError.exception.InnerException.Message -match 'https://aka.ms/resource-manager-parameter-files' -and $true -eq $bicepTemplate) { Write-PSFMessage -Level Warning -String 'New-AzOpsDeployment.TemplateParameterError' -Target $scopeObject $invalidTemplate = $true } else { Write-PSFMessage -Level Warning -String 'New-AzOpsDeployment.WhatIfWarning' -Target $scopeObject -Tag Error -StringValues $resultsError.exception.InnerException.Message throw $resultsError.exception.InnerException.Message } } elseif ($results.Error) { Write-PSFMessage -Level Warning -String 'New-AzOpsDeployment.TemplateError' -StringValues $TemplateFilePath -Target $scopeObject return } else { Write-PSFMessage -Level Verbose -String 'Set-AzOpsWhatIfOutput.WhatIfResults' -StringValues ($results | Out-String) -Target $scopeObject Write-PSFMessage -Level Verbose -String 'Set-AzOpsWhatIfOutput.WhatIfFile' -Target $scopeObject Set-AzOpsWhatIfOutput -TemplatePath $TemplateFilePath -Results $results } $parameters.Name = $DeploymentName if ($PSCmdlet.ShouldProcess("Start Subscription Deployment?")) { if (-not $invalidTemplate) { New-AzSubscriptionDeployment @parameters } } else { # Exit deployment Write-PSFMessage -Level Verbose -String 'New-AzOpsDeployment.SkipDueToWhatIf' } } else { Write-PSFMessage -Level Verbose -String 'New-AzOpsDeployment.ResourceGroup.Processing' -StringValues $scopeObject -Target $scopeObject $parameters = @{ 'TemplateFile' = $TemplateFilePath 'SkipTemplateParameterPrompt' = $true 'ResourceGroupName' = $scopeObject.resourcegroup } if ($TemplateParameterFilePath) { $parameters.TemplateParameterFile = $TemplateParameterFilePath } if ($WhatifExcludedChangeTypes) { $parameters.ExcludeChangeType = $WhatifExcludedChangeTypes } $results = Get-AzResourceGroupDeploymentWhatIfResult @parameters -ErrorAction Continue -ErrorVariable resultsError if ($parameters.ExcludeChangeType) { $parameters.Remove('ExcludeChangeType') } if ($resultsError) { $resultsErrorMessage = $resultsError.exception.InnerException.Message if ($resultsErrorMessage -match 'https://aka.ms/resource-manager-parameter-files' -and $true -eq $bicepTemplate) { Write-PSFMessage -Level Warning -String 'New-AzOpsDeployment.TemplateParameterError' -Target $scopeObject $invalidTemplate = $true } elseif ($resultsErrorMessage -match 'DeploymentWhatIfResourceError' -and $resultsErrorMessage -match "The request to predict template deployment") { Write-PSFMessage -Level Warning -String 'New-AzOpsDeployment.WhatIfWarning' -Target $scopeObject -Tag Error -StringValues $resultsErrorMessage Set-AzOpsWhatIfOutput -TemplatePath $TemplateFilePath -Results ('{0}WhatIf prediction failed with error - validate changes manually before merging:{0}{1}' -f [environment]::NewLine, $resultsErrorMessage) } else { Write-PSFMessage -Level Warning -String 'New-AzOpsDeployment.WhatIfWarning' -Target $scopeObject -Tag Error -StringValues $resultsErrorMessage throw $resultsErrorMessage } } elseif ($results.Error) { Write-PSFMessage -Level Warning -String 'New-AzOpsDeployment.TemplateError' -StringValues $TemplateFilePath -Target $scopeObject return } else { Write-PSFMessage -Level Verbose -String 'New-AzOpsDeployment.WhatIfResults' -StringValues ($results | Out-String) -Target $scopeObject Write-PSFMessage -Level Verbose -String 'New-AzOpsDeployment.WhatIfFile' -Target $scopeObject Set-AzOpsWhatIfOutput -TemplatePath $TemplateFilePath -Results $results } $parameters.Name = $DeploymentName if ($PSCmdlet.ShouldProcess("Start ResourceGroup Deployment?")) { if (-not $invalidTemplate) { New-AzResourceGroupDeployment @parameters } } else { # Exit deployment Write-PSFMessage -Level Verbose -String 'New-AzOpsDeployment.SkipDueToWhatIf' } } } #endregion Resource Group #region Subscription elseif ($scopeObject.subscription) { $defaultDeploymentRegion = Get-PSFConfigValue -FullName 'AzOps.Core.DefaultDeploymentRegion' Write-PSFMessage -Level Verbose -String 'New-AzOpsDeployment.Subscription.Processing' -StringValues $defaultDeploymentRegion, $scopeObject -Target $scopeObject if ((Get-AzContext).Subscription.Id -ne $scopeObject.subscription) { Set-AzOpsContext -ScopeObject $scopeObject } $parameters = @{ 'TemplateFile' = $TemplateFilePath 'Location' = $defaultDeploymentRegion 'SkipTemplateParameterPrompt' = $true } if ($TemplateParameterFilePath) { $parameters.TemplateParameterFile = $TemplateParameterFilePath } if ($WhatifExcludedChangeTypes) { $parameters.ExcludeChangeType = $WhatifExcludedChangeTypes } $results = Get-AzSubscriptionDeploymentWhatIfResult @parameters -ErrorAction Continue -ErrorVariable resultsError if ($parameters.ExcludeChangeType) { $parameters.Remove('ExcludeChangeType') } if ($resultsError) { $resultsErrorMessage = $resultsError.exception.InnerException.Message if ($resultsErrorMessage -match 'https://aka.ms/resource-manager-parameter-files' -and $true -eq $bicepTemplate) { Write-PSFMessage -Level Warning -String 'New-AzOpsDeployment.TemplateParameterError' -Target $scopeObject $invalidTemplate = $true } elseif ($resultsErrorMessage -match 'DeploymentWhatIfResourceError' -and $resultsErrorMessage -match "The request to predict template deployment") { Write-PSFMessage -Level Warning -String 'New-AzOpsDeployment.WhatIfWarning' -Target $scopeObject -Tag Error -StringValues $resultsErrorMessage Set-AzOpsWhatIfOutput -TemplatePath $TemplateFilePath -Results ('{0}WhatIf prediction failed with error - validate changes manually before merging:{0}{1}' -f [environment]::NewLine, $resultsErrorMessage) } else { Write-PSFMessage -Level Warning -String 'New-AzOpsDeployment.WhatIfWarning' -Target $scopeObject -Tag Error -StringValues $resultsErrorMessage throw $resultsErrorMessage } } elseif ($results.Error) { Write-PSFMessage -Level Warning -String 'New-AzOpsDeployment.TemplateError' -StringValues $TemplateFilePath -Target $scopeObject return } else { Write-PSFMessage -Level Verbose -String 'New-AzOpsDeployment.WhatIfResults' -StringValues ($results | Out-String) -Target $scopeObject Write-PSFMessage -Level Verbose -String 'New-AzOpsDeployment.WhatIfFile' -Target $scopeObject Set-AzOpsWhatIfOutput -TemplatePath $TemplateFilePath -Results $results } $parameters.Name = $DeploymentName if ($PSCmdlet.ShouldProcess("Start Subscription Deployment?")) { if (-not $invalidTemplate) { New-AzSubscriptionDeployment @parameters } } else { # Exit deployment Write-PSFMessage -Level Verbose -String 'New-AzOpsDeployment.SkipDueToWhatIf' } } #endregion Subscription #region Management Group elseif ($scopeObject.managementGroup) { $defaultDeploymentRegion = Get-PSFConfigValue -FullName 'AzOps.Core.DefaultDeploymentRegion' Write-PSFMessage -Level Verbose -String 'New-AzOpsDeployment.ManagementGroup.Processing' -StringValues $defaultDeploymentRegion, $scopeObject -Target $scopeObject $parameters = @{ 'TemplateFile' = $TemplateFilePath 'Location' = $defaultDeploymentRegion 'ManagementGroupId' = $scopeObject.managementgroup 'SkipTemplateParameterPrompt' = $true } if ($TemplateParameterFilePath) { $parameters.TemplateParameterFile = $TemplateParameterFilePath } if ($WhatifExcludedChangeTypes) { $parameters.ExcludeChangeType = $WhatifExcludedChangeTypes } $results = Get-AzManagementGroupDeploymentWhatIfResult @parameters -ErrorAction SilentlyContinue -ErrorVariable resultsError if ($parameters.ExcludeChangeType) { $parameters.Remove('ExcludeChangeType') } if ($resultsError) { $resultsErrorMessage = $resultsError.exception.InnerException.Message if ($resultsErrorMessage -match 'https://aka.ms/resource-manager-parameter-files' -and $true -eq $bicepTemplate) { Write-PSFMessage -Level Warning -String 'New-AzOpsDeployment.TemplateParameterError' -Target $scopeObject $invalidTemplate = $true } elseif ($resultsErrorMessage -match 'DeploymentWhatIfResourceError' -and $resultsErrorMessage -match "The request to predict template deployment") { Write-PSFMessage -Level Warning -String 'New-AzOpsDeployment.WhatIfWarning' -Target $scopeObject -Tag Error -StringValues $resultsErrorMessage Set-AzOpsWhatIfOutput -TemplatePath $TemplateFilePath -Results ('{0}WhatIf prediction failed with error - validate changes manually before merging:{0}{1}' -f [environment]::NewLine, $resultsErrorMessage) } else { Write-PSFMessage -Level Warning -String 'New-AzOpsDeployment.WhatIfWarning' -Target $scopeObject -Tag Error -StringValues $resultsErrorMe throw $resultsErrorMessage } } elseif ($results.Error) { Write-PSFMessage -Level Warning -String 'New-AzOpsDeployment.TemplateError' -StringValues $TemplateFilePath -Target $scopeObject return } else { Write-PSFMessage -Level Verbose -String 'New-AzOpsDeployment.WhatIfResults' -StringValues ($results | Out-String) -Target $scopeObject Write-PSFMessage -Level Verbose -String 'New-AzOpsDeployment.WhatIfFile' -Target $scopeObject Set-AzOpsWhatIfOutput -TemplatePath $TemplateFilePath -Results $results } $parameters.Name = $DeploymentName if ($PSCmdlet.ShouldProcess("Start ManagementGroup Deployment?")) { if (-not $invalidTemplate) { New-AzManagementGroupDeployment @parameters } } else { # Exit deployment Write-PSFMessage -Level Verbose -String 'New-AzOpsDeployment.SkipDueToWhatIf' } } #endregion Management Group #region Root elseif ($scopeObject.type -eq 'root' -and $scopeObject.scope -eq '/') { $defaultDeploymentRegion = Get-PSFConfigValue -FullName 'AzOps.Core.DefaultDeploymentRegion' Write-PSFMessage -Level Verbose -String 'New-AzOpsDeployment.Root.Processing' -StringValues $defaultDeploymentRegion, $scopeObject -Target $scopeObject $parameters = @{ 'TemplateFile' = $TemplateFilePath 'location' = $defaultDeploymentRegion 'SkipTemplateParameterPrompt' = $true } if ($TemplateParameterFilePath) { $parameters.TemplateParameterFile = $TemplateParameterFilePath } if ($WhatifExcludedChangeTypes) { $parameters.ExcludeChangeType = $WhatifExcludedChangeTypes } $results = Get-AzTenantDeploymentWhatIfResult @parameters -ErrorAction Continue -ErrorVariable resultsError if ($parameters.ExcludeChangeType) { $parameters.Remove('ExcludeChangeType') } if ($resultsError) { Write-PSFMessage -Level Warning -String 'New-AzOpsDeployment.WhatIfWarning' -StringValues $resultsError.Exception.Message -Target $scopeObject } elseif ($results.Error) { Write-PSFMessage -Level Warning -String 'New-AzOpsDeployment.TemplateError' -StringValues $TemplateFilePath -Target $scopeObject return } else { Write-PSFMessage -Level Verbose -String 'New-AzOpsDeployment.WhatIfResults' -StringValues ($results | Out-String) -Target $scopeObject Write-PSFMessage -Level Verbose -String 'New-AzOpsDeployment.WhatIfFile' -Target $scopeObject Set-AzOpsWhatIfOutput -TemplatePath $TemplateFilePath -Results $results } $parameters.Name = $DeploymentName if ($PSCmdlet.ShouldProcess("Start Tenant Deployment?")) { New-AzTenantDeployment @parameters } else { # Exit deployment Write-PSFMessage -Level Verbose -String 'New-AzOpsDeployment.SkipDueToWhatIf' } } #endregion Root #region Unidentified else { Write-PSFMessage -Level Warning -String 'New-AzOpsDeployment.Scope.Unidentified' -Target $scopeObject -StringValues $scopeObject } #endregion Unidentified #endregion Process Scope } } |