Skip to content

Commit

Permalink
Adding support for .bicepparam and always export WhatIf results (#820)
Browse files Browse the repository at this point in the history
* UpdateRequiredModules

* BicepParamLogic

* UpdateBicepParamLogic

* UpdateBicepParamLogic

* AlwaysExportJsonResults

* UpdateAlwaysExportJsonResults

* UpdateAlwaysExportJsonResults

* UpdateAlwaysExportJsonResults
  • Loading branch information
Jefajers committed Aug 25, 2023
1 parent 061d024 commit 5f103bb
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 47 deletions.
10 changes: 5 additions & 5 deletions src/AzOps.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#
# Generated by: Customer Architecture Team (CAT)
#
# Generated on: 07/03/2023
# Generated on: 8/22/2023
#

@{
Expand Down Expand Up @@ -51,11 +51,11 @@ PowerShellVersion = '7.2'
# ProcessorArchitecture = ''

# Modules that must be imported into the global environment prior to importing this module
RequiredModules = @(@{ModuleName = 'PSFramework'; RequiredVersion = '1.8.289'; },
@{ModuleName = 'Az.Accounts'; RequiredVersion = '2.12.3'; },
@{ModuleName = 'Az.Billing'; RequiredVersion = '2.0.2'; },
RequiredModules = @(@{ModuleName = 'PSFramework'; RequiredVersion = '1.8.291'; },
@{ModuleName = 'Az.Accounts'; RequiredVersion = '2.12.5'; },
@{ModuleName = 'Az.Billing'; RequiredVersion = '2.0.3'; },
@{ModuleName = 'Az.ResourceGraph'; RequiredVersion = '0.13.0'; },
@{ModuleName = 'Az.Resources'; RequiredVersion = '6.7.0'; })
@{ModuleName = 'Az.Resources'; RequiredVersion = '6.9.1'; })

# Assemblies that must be loaded prior to importing this module
# RequiredAssemblies = @()
Expand Down
58 changes: 37 additions & 21 deletions src/functions/Invoke-AzOpsPush.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
}

$fileItem = Get-Item -Path $FilePath
if ($fileItem.Extension -notin '.json' , '.bicep') {
if ($fileItem.Extension -notin '.json' , '.bicep', '.bicepparam') {
Write-PSFMessage -Level Warning -String 'Invoke-AzOpsPush.Resolve.NoJson' -StringValues $fileItem.FullName -Tag pwsh -FunctionName 'Invoke-AzOpsPush' -Target $ScopeObject
return
}
Expand All @@ -83,31 +83,42 @@
#endregion Initialization Prep

#region Case: Parameters File
if ($fileItem.Name.EndsWith('.parameters.json')) {
if (($fileItem.Name.EndsWith('.parameters.json')) -or ($fileItem.Name.EndsWith('.bicepparam'))) {
$result.TemplateParameterFilePath = $fileItem.FullName
$deploymentName = $fileItem.Name -replace (Get-PSFConfigValue -FullName 'AzOps.Core.TemplateParameterFileSuffix'), '' -replace ' ', '_'
$deploymentName = $fileItem.Name -replace (Get-PSFConfigValue -FullName 'AzOps.Core.TemplateParameterFileSuffix'), '' -replace ' ', '_' -replace '\.bicepparam', ''
if ($deploymentName.Length -gt 53) { $deploymentName = $deploymentName.SubString(0, 53) }
$result.DeploymentName = 'AzOps-{0}-{1}' -f $deploymentName, $deploymentRegionId

#region Directly Associated Template file exists
$templatePath = $fileItem.FullName -replace '.parameters.json', (Get-PSFConfigValue -FullName 'AzOps.Core.TemplateParameterFileSuffix')
if (Test-Path $templatePath) {
Write-PSFMessage -Level Verbose @common -String 'Invoke-AzOpsPush.Resolve.FoundTemplate' -StringValues $FilePath, $templatePath
$result.TemplateFilePath = $templatePath
return $result
switch ($fileItem.Name) {
{ $_.EndsWith('.parameters.json') } {
$templatePath = $fileItem.FullName -replace '\.parameters.json', (Get-PSFConfigValue -FullName 'AzOps.Core.TemplateParameterFileSuffix')
$bicepTemplatePath = $fileItem.FullName -replace '.parameters.json', '.bicep'
if (Test-Path $templatePath) {
Write-PSFMessage -Level Verbose @common -String 'Invoke-AzOpsPush.Resolve.FoundTemplate' -StringValues $FilePath, $templatePath
$result.TemplateFilePath = $templatePath
return $result
}
elseif (Test-Path $bicepTemplatePath) {
Write-PSFMessage -Level Verbose @common -String 'Invoke-AzOpsPush.Resolve.FoundBicepTemplate' -StringValues $FilePath, $bicepTemplatePath
$transpiledTemplatePaths = ConvertFrom-AzOpsBicepTemplate -BicepTemplatePath $bicepTemplatePath
$result.TemplateFilePath = $transpiledTemplatePaths.transpiledTemplatePath
return $result
}
}
{ $_.EndsWith('.bicepparam') } {
$bicepTemplatePath = $fileItem.FullName -replace '\.bicepparam', '.bicep'
if (Test-Path $bicepTemplatePath) {
Write-PSFMessage -Level Verbose @common -String 'Invoke-AzOpsPush.Resolve.FoundBicepTemplate' -StringValues $FilePath, $bicepTemplatePath
$transpiledTemplatePaths = ConvertFrom-AzOpsBicepTemplate -BicepTemplatePath $bicepTemplatePath
$result.TemplateFilePath = $transpiledTemplatePaths.transpiledTemplatePath
$result.TemplateParameterFilePath = $transpiledTemplatePaths.transpiledParametersPath
return $result
}
}
}
#endregion Directly Associated Template file exists

#region Directly Associated bicep template exists
$bicepTemplatePath = $fileItem.FullName -replace '.parameters.json', '.bicep'
if (Test-Path $bicepTemplatePath) {
Write-PSFMessage -Level Verbose @common -String 'Invoke-AzOpsPush.Resolve.FoundBicepTemplate' -StringValues $FilePath, $bicepTemplatePath
$transpiledTemplatePath = ConvertFrom-AzOpsBicepTemplate -BicepTemplatePath $bicepTemplatePath
$result.TemplateFilePath = $transpiledTemplatePath
return $result
}
#endregion Directly Associated bicep template exists

#region Check in the main template file for a match
Write-PSFMessage -Level Important @common -String 'Invoke-AzOpsPush.Resolve.NotFoundTemplate' -StringValues $FilePath, $templatePath
$mainTemplateItem = Get-Item $AzOpsMainTemplate
Expand Down Expand Up @@ -174,7 +185,7 @@

# Remove lingering files from previous run
$tempPath = [System.IO.Path]::GetTempPath()
if (Test-Path -Path ($tempPath + 'OUTPUT.md')) {
if ((Test-Path -Path ($tempPath + 'OUTPUT.md')) -or (Test-Path -Path ($tempPath + 'OUTPUT.json'))) {
Write-PSFMessage -Level Verbose -String 'Set-AzOpsWhatIfOutput.WhatIfFile.Remove'
Remove-Item -Path ($tempPath + 'OUTPUT.md') -Force -ErrorAction SilentlyContinue
Remove-Item -Path ($tempPath + 'OUTPUT.json') -Force -ErrorAction SilentlyContinue
Expand Down Expand Up @@ -276,11 +287,16 @@
continue
}
}
if ($addition.EndsWith(".bicepparam")) {
if ($addModifySet -contains $addition.Replace(".bicepparam", ".bicep")) {
continue
}
}

# Handle Bicep templates
if ($addition.EndsWith(".bicep")) {
$transpiledTemplatePath = ConvertFrom-AzOpsBicepTemplate -BicepTemplatePath $addition
$addition = $transpiledTemplatePath
$transpiledTemplatePaths = ConvertFrom-AzOpsBicepTemplate -BicepTemplatePath $addition | Select-Object transpiledTemplatePath
$addition = $transpiledTemplatePaths.transpiledTemplatePath
}

try {
Expand Down
32 changes: 29 additions & 3 deletions src/internal/functions/ConvertFrom-AzOpsBicepTemplate.ps1
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
function ConvertFrom-AzOpsBicepTemplate {
<#
.SYNOPSIS
Transpiles bicep template to Azure Resource Manager (ARM) template.
Transpiles bicep template and associated bicepparam to Azure Resource Manager (ARM) template.
The json file will be created in the same folder as the bicep file.
.PARAMETER BicepTemplatePath
BicepTemplatePath
.EXAMPLE
ConvertFrom-AzOpsBicepTemplate -BicepTemplatePath "root/tenant root group (xxxx-xxxx-xxxx-xxxx-xxxx)/es (es)/subscription (xxxx-xxxx-xxxx-xxxx)/resource-rg/main.bicep"
transpiledTemplatePath : root/tenant root group (xxxx-xxxx-xxxx-xxxx-xxxx)/es (es)/subscription (xxxx-xxxx-xxxx-xxxx)/resource-rg/main.json
transpiledParametersPath : root/tenant root group (xxxx-xxxx-xxxx-xxxx-xxxx)/es (es)/subscription (xxxx-xxxx-xxxx-xxxx)/resource-rg/main.parameters.json
#>

[CmdletBinding()]
Expand All @@ -29,7 +33,29 @@
Write-PSFMessage -Level Error -String 'ConvertFrom-AzOpsBicepTemplate.Resolve.ConvertBicepTemplate.Error' -StringValues $BicepTemplatePath
throw
}
# Return transpiled ARM json path
return $transpiledTemplatePath
# Check if bicep template has associated bicepparam file
$bicepParametersPath = $BicepTemplatePath -replace '\.bicep', '.bicepparam'
Write-PSFMessage -Level Verbose -String 'ConvertFrom-AzOpsBicepTemplate.Resolve.BicepParam' -StringValues $BicepTemplatePath, $bicepParametersPath
if (Test-Path $bicepParametersPath) {
# Convert bicepparam to ARM parameter file
$transpiledParametersPath = $bicepParametersPath -replace '\.bicepparam', ('.parameters' + (Get-PSFConfigValue -FullName 'AzOps.Core.TemplateParameterFileSuffix'))
Write-PSFMessage -Level Verbose -String 'ConvertFrom-AzOpsBicepTemplate.Resolve.ConvertBicepParam' -StringValues $bicepParametersPath, $transpiledParametersPath
Invoke-AzOpsNativeCommand -ScriptBlock { bicep build-params $bicepParametersPath --outfile $transpiledParametersPath }
# Check if bicep build-params created (ARM) parameters
if (-not (Test-Path $transpiledParametersPath)) {
# If bicep build-params did not produce file exit with error
Write-PSFMessage -Level Error -String 'ConvertFrom-AzOpsBicepTemplate.Resolve.ConvertBicepParam.Error' -StringValues $bicepParametersPath
throw
}
}
else {
Write-PSFMessage -Level Verbose -String 'ConvertFrom-AzOpsBicepTemplate.Resolve.BicepParam.NotFound' -StringValues $BicepTemplatePath
}
# Return transpiled (ARM) template paths
$return = [PSCustomObject]@{
transpiledTemplatePath = $transpiledTemplatePath
transpiledParametersPath = $transpiledParametersPath
}
return $return
}
}
27 changes: 16 additions & 11 deletions src/internal/functions/Set-AzOpsWhatIfOutput.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,14 @@
process {
Write-PSFMessage -Level Verbose -String 'Set-AzOpsWhatIfOutput.WhatIfFile'
$tempPath = [System.IO.Path]::GetTempPath()
if (-not (Test-Path -Path ($tempPath + 'OUTPUT.md'))) {
if ((-not (Test-Path -Path ($tempPath + 'OUTPUT.md'))) -or (-not (Test-Path -Path ($tempPath + 'OUTPUT.json')))) {
New-Item -Path ($tempPath + 'OUTPUT.md') -WhatIf:$false
New-Item -Path ($tempPath + 'OUTPUT.json') -WhatIf:$false
}

$resultHeadline = $FilePath.split([System.IO.Path]::DirectorySeparatorChar)[-1]

# Measure input $Results.Changes content
$resultJson = ($Results.Changes | ConvertTo-Json -Depth 100)
$resultString = $Results | Out-String
$resultStringMeasure = $resultString | Measure-Object -Line -Character -Word
# Measure current OUTPUT.md content
Expand All @@ -59,6 +58,21 @@
$existingContentStringMeasureMd = $existingContentStringMd | Measure-Object -Line -Character -Word
# Gather current OUTPUT.json content
$existingContent = @(Get-Content -Path ($tempPath + 'OUTPUT.json') -Raw | ConvertFrom-Json -Depth 100)
# Export results to json file
Write-PSFMessage -Level Verbose -String 'Set-AzOpsWhatIfOutput.WhatIfFileAddingJson'
if ($RemoveAzOpsFlag) {
$resultJson = [PSCustomObject]@{
WhatIfResult = $Results
TemplateFile = $resultHeadline
}
}
else {
$resultJson = $results.Changes
$resultJson | Add-Member -Name "TemplateFile" -Value $resultHeadline -MemberType NoteProperty -Force
}
$existingContent += $resultJson
$existingContent = $existingContent | ConvertTo-Json -Depth 100
Set-Content -Path ($tempPath + 'OUTPUT.json') -Value $existingContent -WhatIf:$false
# Check if $existingContentStringMeasureMd and $resultStringMeasure exceed allowed size in $ResultSizeLimit
if (($existingContentStringMeasureMd.Characters + $resultStringMeasure.Characters) -gt $ResultSizeLimit) {
Write-PSFMessage -Level Warning -String 'Set-AzOpsWhatIfOutput.WhatIfFileMax' -StringValues $ResultSizeLimit
Expand All @@ -77,16 +91,7 @@
}
}
else {
if ($existingContent.count -gt 0) {
$existingContent += $results.Changes
$existingContent = $existingContent | ConvertTo-Json -Depth 100
}
else {
$existingContent = $resultJson
}
$mdOutput = 'WhatIf Results for {2}:{0}```{0}{1}{0}```{0}' -f [environment]::NewLine, $resultString, $resultHeadline
Write-PSFMessage -Level Verbose -String 'Set-AzOpsWhatIfOutput.WhatIfFileAddingJson'
Set-Content -Path ($tempPath + 'OUTPUT.json') -Value $existingContent -WhatIf:$false
}
}
if ((($mdOutput | Measure-Object -Line -Character -Word).Characters + $existingContentStringMeasureMd.Characters) -le $ResultSizeMaxLimit) {
Expand Down
10 changes: 7 additions & 3 deletions src/localized/en-us/Strings.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,12 @@
'AzOpsScope.InitializeMemberVariablesFromFile.resource' = 'Determine scope based on ResourceType {0} and Resource Name {1}' # ResourceType and #Resource Name
'AzOpsScope.ChildResource.InitializeMemberVariables' = 'Determine scope of Child Resource based on ResourceType {0}, Resource Name {1} and Parent ResourceID {2}' # ResourceType, Resource Name, Parent ResourceId

'ConvertFrom-AzOpsBicepTemplate.Resolve.ConvertBicepTemplate' = 'Converting Bicep template ({0}) to standard ARM Template JSON ({1})' # $BicepTemplatePath, $transpiledTemplatePath
'ConvertFrom-AzOpsBicepTemplate.Resolve.ConvertBicepTemplate.Error' = 'Failed to convert Bicep template ({0}) to standard ARM Template JSON' # $BicepTemplatePath
'ConvertFrom-AzOpsBicepTemplate.Resolve.ConvertBicepTemplate' = 'Converting Bicep template ({0}) to ARM Template JSON ({1})' # $BicepTemplatePath, $transpiledTemplatePath
'ConvertFrom-AzOpsBicepTemplate.Resolve.ConvertBicepTemplate.Error' = 'Failed to convert Bicep template ({0}) to ARM Template JSON' # $BicepTemplatePath
'ConvertFrom-AzOpsBicepTemplate.Resolve.BicepParam' = 'Determine if Bicep template ({0}) has a bicepparam file at ({1})' # $BicepTemplatePath, $bicepParametersPath
'ConvertFrom-AzOpsBicepTemplate.Resolve.BicepParam.NotFound' = 'No bicepparam file found for ({0})' # $BicepTemplatePath
'ConvertFrom-AzOpsBicepTemplate.Resolve.ConvertBicepParam' = 'Found bicepparam file ({0}), converting to ARM parameters JSON ({1})' # $bicepParametersPath, $transpiledParametersPath
'ConvertFrom-AzOpsBicepTemplate.Resolve.ConvertBicepParam.Error' = 'Failed to convert bicepparam file ({0}) to ARM Template JSON' # $bicepParametersPath

'ConvertTo-AzOpsState.Exporting' = 'Exporting AzOpsState to {0}' # $resourceData.ObjectFilePath
'ConvertTo-AzOpsState.Exporting.Default' = 'Exporting input resource to AzOpsState to {0}' # $resourceData.ObjectFilePath
Expand Down Expand Up @@ -189,7 +193,7 @@
'Invoke-AzOpsPush.Resolve.FromMainTemplate' = 'Determining template from main template file: {0}' # $mainTemplateItem.FullName
'Invoke-AzOpsPush.Resolve.MainTemplate.NotSupported' = 'effectiveResourceType: {0} AzOpsMainTemplate does NOT supports resource type {0} in {1}. Deployment will be ignored' # $effectiveResourceType, $AzOpsMainTemplate.FullName
'Invoke-AzOpsPush.Resolve.MainTemplate.Supported' = 'effectiveResourceType: {0} - AzOpsMainTemplate supports resource type {0} in {1}' # $effectiveResourceType, $AzOpsMainTemplate.FullName
'Invoke-AzOpsPush.Resolve.NoJson' = 'The specified file is not a json file at all! Skipping {0}' # $fileItem.FullName
'Invoke-AzOpsPush.Resolve.NoJson' = 'The specified file is not a json or bicep file! Skipping {0}' # $fileItem.FullName
'Invoke-AzOpsPush.Resolve.NotFoundTemplate' = 'Did NOT find template {1} for parameters {0}' # $FilePath, $templatePath
'Invoke-AzOpsPush.Resolve.ParameterFound' = 'Found parameter file for template {0} : {1}' # $FilePath, $parameterPath
'Invoke-AzOpsPush.Resolve.ParameterNotFound' = 'No parameter file found for template {0} : {1}' # $FilePath, $parameterPath
Expand Down
12 changes: 11 additions & 1 deletion src/tests/integration/Repository.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ Describe "Repository" {
try {
New-AzSubscriptionDeployment -Name 'AzOps-Tests-rbacdep' -Location northeurope -TemplateFile "$($global:testRoot)/templates/rbactest.bicep" -TemplateParameterFile "$($global:testRoot)/templates/rbactest.parameters.json"
New-AzManagementGroupDeployment @params
New-AzResourceGroupDeployment -Name 'AzOps-Tests-policyuam' -ResourceGroupName App1-azopsrg -TemplateFile "$($global:testRoot)/templates/policywithuam.bicep"
New-AzResourceGroupDeployment -Name 'AzOps-Tests-policyuam' -ResourceGroupName App1-azopsrg -TemplateFile "$($global:testRoot)/templates/policywithuam.bicep" -TemplateParameterFile "$($global:testRoot)/templates/policywithuam.bicepparam"
# Pause for resource consistency
Start-Sleep -Seconds 120
}
Expand Down Expand Up @@ -1054,6 +1054,16 @@ Describe "Repository" {
{Invoke-AzOpsPush -ChangeSet $changeSet} | Should -Throw
}
#endregion

#region Bicep Build-Params Test
It "Build with bicepparam should not throw" {
$script:bicepParamTemplatePath = Get-ChildItem -Path "$($global:testRoot)/templates/bicepparamtest*" | Copy-Item -Destination $script:resourceGroupDirectory -PassThru -Force
$changeSet = @(
"A`t$($script:bicepParamTemplatePath.FullName[0])"
)
{Invoke-AzOpsPush -ChangeSet $changeSet} | Should -Not -Throw
}
#endregion
}

AfterAll {
Expand Down
12 changes: 12 additions & 0 deletions src/tests/templates/bicepparamtest.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
param name string
param location string

resource symbolicname 'Microsoft.Network/routeTables@2023-04-01' = {
name: name
location: location
properties: {
disableBgpRoutePropagation: true
routes: [
]
}
}
4 changes: 4 additions & 0 deletions src/tests/templates/bicepparamtest.bicepparam
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
using './bicepparamtest.bicep'

param name = 'rt666'
param location = 'northeurope'
6 changes: 3 additions & 3 deletions src/tests/templates/policywithuam.bicep
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
param policyAssignmentName string = 'TestPolicyAssignmentWithUAM'
param policyDefinitionID string = '/providers/Microsoft.Authorization/policyDefinitions/014664e7-e348-41a3-aeb9-566e4ff6a9df'
param policyAssignmentName string
param policyDefinitionID string
param location string = resourceGroup().location
param uamName string = 'TestAzOpsUAM'
param uamName string

targetScope = 'resourceGroup'

Expand Down
5 changes: 5 additions & 0 deletions src/tests/templates/policywithuam.bicepparam
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using './policywithuam.bicep'

param policyAssignmentName = 'TestPolicyAssignmentWithUAM'
param policyDefinitionID = '/providers/Microsoft.Authorization/policyDefinitions/014664e7-e348-41a3-aeb9-566e4ff6a9df'
param uamName = 'TestAzOpsUAM'

0 comments on commit 5f103bb

Please sign in to comment.