From ed703607e2c5b9b6808b18b003694a501eccd762 Mon Sep 17 00:00:00 2001 From: Guy Pritchard MSFT <111762521+lovelysandwich@users.noreply.github.com> Date: Fri, 31 Mar 2023 13:22:59 +0100 Subject: [PATCH] Handling File Targeted Configuration and fixing Environment Computed Values (#39) Adding Computed values to Environment, also adding file targeted config to ALZ-BICEP-CONFIG # Pull Request ## Issue Fixes #38 ## Description Handling 'Computed' values for the Environment file. (This needs a refactor of how we handle computed values, which I'll do after this change #40) Supporting file specific replacement values with configuration. ``` { Name = {Name of the setting in Bicep config or Environment} File = {FileName} Destination = {Parameters|Environment} } ``` ## License By submitting this pull request, I confirm that my contribution is made under the terms of the projects associated license. --- .../alz-bicep-config/v0.14.1-pre.config.json | 39 +++++++-- .../Private/Build-ALZDeploymentEnvFile.ps1 | 8 +- .../Edit-ALZConfigurationFilesInPlace.ps1 | 7 +- .../Build-ALZDeploymentEnvFile.Tests.ps1 | 32 ++++++++ ...dit-ALZConfigurationFilesInPlace.Tests.ps1 | 82 +++++++++++++++++++ 5 files changed, 161 insertions(+), 7 deletions(-) diff --git a/src/ALZ/Assets/alz-bicep-config/v0.14.1-pre.config.json b/src/ALZ/Assets/alz-bicep-config/v0.14.1-pre.config.json index 3b90186..30c6adf 100644 --- a/src/ALZ/Assets/alz-bicep-config/v0.14.1-pre.config.json +++ b/src/ALZ/Assets/alz-bicep-config/v0.14.1-pre.config.json @@ -273,7 +273,7 @@ "Description": "The identifier of the Identity Subscription. (e.g '00000000-0000-0000-0000-000000000000')", "Valid": "^( {){0,1}[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}(}){0,1}$", "Targets": [ - { + { "Name": "IDENTITY_SUBSCRIPTION_ID", "Destination": "Environment" } @@ -310,7 +310,7 @@ "Valid": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", "Targets": [ { - "Name":"parPolicyAssignmentParameters.value.emailSecurityContact.value", + "Name": "parPolicyAssignmentParameters.value.emailSecurityContact.value", "Destination": "Parameters" } ], @@ -327,7 +327,6 @@ } ] }, - "LogAnalyticsResourceId": { "Type": "Computed", "Value": "/subscriptions/{%ManagementSubscriptionId%}/resourcegroups/alz-logging/providers/microsoft.operationalinsights/workspaces/alz-log-analytics", @@ -384,7 +383,7 @@ } ] }, - "VirtualWanName":{ + "VirtualWanName": { "Type": "Computed", "Value": "alz-vwan-{%Location%}", "Targets": [ @@ -394,7 +393,7 @@ } ] }, - "FirewallPoliciesName":{ + "FirewallPoliciesName": { "Type": "Computed", "Value": "alz-azfwpolicy-{%Location%}", "Targets": [ @@ -453,6 +452,36 @@ "Destination": "Environment" } ] + }, + "ConnectivityResourceGroupName": { + "Type": "Computed", + "Value": "rg-{%Prefix%}-connectivity", + "Targets": [ + { + "Name": "CONNECTIVITY_RESOURCE_GROUP", + "Destination": "Environment" + }, + { + "File": "resourceGroupConnectivity.parameters.all.json", + "Name": "parResourceGroupName.value", + "Destination": "Parameters" + } + ] + }, + "LoggingResourceGroupName": { + "Type": "Computed", + "Value": "rg-{%Prefix%}-logging", + "Targets": [ + { + "Name": "LOGGING_RESOURCE_GROUP", + "Destination": "Environment" + }, + { + "File": "resourceGroupLoggingAndSentinel.parameters.all.json", + "Name": "parResourceGroupName.value", + "Destination": "Parameters" + } + ] } } } \ No newline at end of file diff --git a/src/ALZ/Private/Build-ALZDeploymentEnvFile.ps1 b/src/ALZ/Private/Build-ALZDeploymentEnvFile.ps1 index 064d202..b74def4 100644 --- a/src/ALZ/Private/Build-ALZDeploymentEnvFile.ps1 +++ b/src/ALZ/Private/Build-ALZDeploymentEnvFile.ps1 @@ -24,7 +24,13 @@ function Build-ALZDeploymentEnvFile { foreach ($configurationValue in $configuration.PsObject.Properties) { foreach ($target in $configurationValue.Value.Targets) { if ($target.Destination -eq "Environment") { - Add-Content -Path $envFile -Value "$($($target.Name))=`"$($configurationValue.Value.Value)`"" | Out-String | Write-Verbose + + $formattedValue = $configurationValue.Value.Value + if ($configurationValue.Value.Type -eq "Computed") { + $formattedValue = Format-TokenizedConfigurationString -tokenizedString $configurationValue.Value.Value -configuration $configuration + } + + Add-Content -Path $envFile -Value "$($($target.Name))=`"$formattedValue`"" | Out-String | Write-Verbose } } } diff --git a/src/ALZ/Private/Edit-ALZConfigurationFilesInPlace.ps1 b/src/ALZ/Private/Edit-ALZConfigurationFilesInPlace.ps1 index 1912a2e..c5a1695 100644 --- a/src/ALZ/Private/Edit-ALZConfigurationFilesInPlace.ps1 +++ b/src/ALZ/Private/Edit-ALZConfigurationFilesInPlace.ps1 @@ -25,6 +25,11 @@ function Edit-ALZConfigurationFilesInPlace { foreach ($configKey in $configuration.PsObject.Properties) { foreach ($target in $configKey.Value.Targets) { + # Is this configuration value for this file? + $targetedAtThisFile = $null -eq $target.File -or $target.File -eq $file.Name + if ($targetedAtThisFile -eq $false) { + continue + } # Find the appropriate item which will be changed in the Bicep file. # Remove array '[' ']' characters so we can use the index value direct. @@ -56,7 +61,7 @@ function Edit-ALZConfigurationFilesInPlace { } while (($null -ne $bicepConfigNode) -and ($index -lt $propertyNames.Length - 1)) - # If we're here, we've got the object at the bottom of the hierarchy - and we can modify values on it. + # If we're here, we can modify this file and we've got an actual object specified by the Name path value - and we can modify values on it. if ($target.Destination -eq "Parameters" -and $null -ne $bicepConfigNode) { $leafPropertyName = $propertyNames[-1] diff --git a/src/Tests/Unit/Private/Build-ALZDeploymentEnvFile.Tests.ps1 b/src/Tests/Unit/Private/Build-ALZDeploymentEnvFile.Tests.ps1 index 4d9cc9e..a344cce 100644 --- a/src/Tests/Unit/Private/Build-ALZDeploymentEnvFile.Tests.ps1 +++ b/src/Tests/Unit/Private/Build-ALZDeploymentEnvFile.Tests.ps1 @@ -77,6 +77,38 @@ InModuleScope 'ALZ' { Should -Invoke New-Item -ParameterFilter { $Path -match ".env$" } -Scope It -Times 1 -Exactly Should -Invoke Add-Content -Scope It -Times 1 -Exactly } + + It 'Handles Computed values correctly and adds to the .env file.' { + + Mock -CommandName New-Item + Mock -CommandName Add-Content + + $configuration = [pscustomobject]@{ + Setting1 = [pscustomobject]@{ + Targets = @( + [pscustomobject]@{ + Name = "Setting1" + Destination = "Environment" + }) + Value = "Test" + } + Setting2 = [pscustomobject]@{ + Targets = @( + [pscustomobject]@{ + Name = "Setting2" + Destination = "Environment" + }) + Type = "Computed" + Value = "{%Setting1%}" + } + } + + Build-ALZDeploymentEnvFile -configuration $configuration -destination "test" + + Should -Invoke New-Item -ParameterFilter { $Path -match ".env$" } -Scope It -Times 1 -Exactly + Should -Invoke Add-Content -ParameterFilter { $Value -match "^Setting1=`"Test`"$" } -Scope It -Times 1 -Exactly + Should -Invoke Add-Content -ParameterFilter { $Value -match "^Setting2=`"Test`"$" } -Scope It -Times 1 -Exactly + } } } } \ No newline at end of file diff --git a/src/Tests/Unit/Private/Edit-ALZConfigurationFilesInPlace.Tests.ps1 b/src/Tests/Unit/Private/Edit-ALZConfigurationFilesInPlace.Tests.ps1 index 2c9ccee..b158caa 100644 --- a/src/Tests/Unit/Private/Edit-ALZConfigurationFilesInPlace.Tests.ps1 +++ b/src/Tests/Unit/Private/Edit-ALZConfigurationFilesInPlace.Tests.ps1 @@ -715,6 +715,88 @@ InModuleScope 'ALZ' { -ParameterFilter { $FilePath -eq $testFile1Name -and $InputObject -eq $expectedContent } ` -Scope It } + + It 'Multiple files with file specific configuration should be changed correctly' { + $defaultConfig = [pscustomobject]@{ + Value1 = [pscustomobject]@{ + Description = "The prefix that will be added to all resources created by this deployment." + Targets = @( + [pscustomobject]@{ + File = "test1.parameters.json" + Name = "parCompanyPrefix.value" + Destination = "Parameters" + }) + Value = "value1" + DefaultValue = "alz" + } + Value2 = [pscustomobject]@{ + Description = "The prefix that will be added to all resources created by this deployment." + Targets = @( + [pscustomobject]@{ + File = "test2.parameters.json" + Name = "parCompanyPrefix.value" + Destination = "Parameters" + }) + Value = "value2" + DefaultValue = "alz" + } + } + + $firstFileContent = '{ + "parameters": { + "parCompanyPrefix": { + "value": "" + }, + } + }' + $secondFileContent = '{ + "parameters": { + "parCompanyPrefix": { + "value": "" + }, + } + }' + + Mock -CommandName Get-ChildItem -ParameterFilter { $Path -match 'config$' } -MockWith { + @( + [PSCustomObject]@{ + Name = 'test1.parameters.json' + FullName = 'test1.parameters.json' + }, + [PSCustomObject]@{ + Name = 'test2.parameters.json' + FullName = 'test2.parameters.json' + } + ) + } + + Mock -CommandName Get-Content -ParameterFilter { $Path -eq 'test1.parameters.json' } -MockWith { + $firstFileContent + } + Mock -CommandName Get-Content -ParameterFilter { $Path -eq 'test2.parameters.json' } -MockWith { + $secondFileContent + } + + Edit-ALZConfigurationFilesInPlace -alzEnvironmentDestination '.' -configuration $defaultConfig + + Should -Invoke -CommandName Out-File -Scope It -Times 2 + + # Assert that the file was written back with the new values + $contentAfterParsing = ConvertFrom-Json -InputObject $firstFileContent -AsHashtable + $contentAfterParsing.parameters.parCompanyPrefix.value = 'value1' + + $contentStringAfterParsing = ConvertTo-Json -InputObject $contentAfterParsing + Write-InformationColored $contentStringAfterParsing -ForegroundColor Yellow -InformationAction Continue + Should -Invoke -CommandName Out-File -ParameterFilter { $FilePath -eq "test1.parameters.json" -and $InputObject -eq $contentStringAfterParsing } -Scope It + + $contentAfterParsing = ConvertFrom-Json -InputObject $secondFileContent -AsHashtable + $contentAfterParsing.parameters.parCompanyPrefix.value = 'value2' + + $contentStringAfterParsing = ConvertTo-Json -InputObject $contentAfterParsing + Write-InformationColored $contentStringAfterParsing -ForegroundColor Yellow -InformationAction Continue + Should -Invoke -CommandName Out-File -ParameterFilter { $FilePath -eq "test2.parameters.json" -and $InputObject -eq $contentStringAfterParsing } -Scope It + + } } } }