From 74484103e74c21e7ed5952cc8b88c1113e82ccae Mon Sep 17 00:00:00 2001 From: Anthony Watherston Date: Thu, 20 Feb 2025 15:47:48 +1100 Subject: [PATCH] Metadata and version fix, variable fix for automation (#887) Co-authored-by: Anthony Watherston --- Scripts/Helpers/Add-HelperScripts.ps1 | 2 + Scripts/Helpers/Build-PolicySetPlan.ps1 | 3 +- Scripts/Helpers/Compare-SemanticVersion.ps1 | 64 +++++++++++++++++++ .../Confirm-ObjectValueEqualityDeep.ps1 | 24 ++++++- ...firm-PolicyDefinitionsInPolicySetMatch.ps1 | 20 ++++-- .../Get-AzResourceListRestMethod.ps1 | 9 +++ 6 files changed, 116 insertions(+), 6 deletions(-) create mode 100644 Scripts/Helpers/Compare-SemanticVersion.ps1 diff --git a/Scripts/Helpers/Add-HelperScripts.ps1 b/Scripts/Helpers/Add-HelperScripts.ps1 index 96f37105..ba8ddeb1 100644 --- a/Scripts/Helpers/Add-HelperScripts.ps1 +++ b/Scripts/Helpers/Add-HelperScripts.ps1 @@ -21,6 +21,8 @@ . "$PSScriptRoot/Build-ScopeTableForManagementGroup.ps1" . "$PSScriptRoot/Build-ScopeTableForSubscription.ps1" +. "$PSScriptRoot/Compare-SemanticVersion.ps1" + . "$PSScriptRoot/Confirm-EffectIsAllowed.ps1" . "$PSScriptRoot/Confirm-MetadataMatches.ps1" . "$PSScriptRoot/Confirm-NullOrEmptyValue.ps1" diff --git a/Scripts/Helpers/Build-PolicySetPlan.ps1 b/Scripts/Helpers/Build-PolicySetPlan.ps1 index 301076f1..57e9e183 100644 --- a/Scripts/Helpers/Build-PolicySetPlan.ps1 +++ b/Scripts/Helpers/Build-PolicySetPlan.ps1 @@ -208,7 +208,8 @@ function Build-PolicySetPlan { -DefinedParametersObj $parameters $policyDefinitionsMatch = Confirm-PolicyDefinitionsInPolicySetMatch ` $deployedDefinition.policyDefinitions ` - $policyDefinitionsFinal + $policyDefinitionsFinal ` + $AllDefinitions.policydefinitions $policyDefinitionGroupsMatch = Confirm-ObjectValueEqualityDeep ` $deployedDefinition.policyDefinitionGroups ` $policyDefinitionGroupsFinal diff --git a/Scripts/Helpers/Compare-SemanticVersion.ps1 b/Scripts/Helpers/Compare-SemanticVersion.ps1 new file mode 100644 index 00000000..aeb5c8a9 --- /dev/null +++ b/Scripts/Helpers/Compare-SemanticVersion.ps1 @@ -0,0 +1,64 @@ +function Compare-SemanticVersion { + param ( + [Parameter(Mandatory = $true)] + [string] $Version1, + + [Parameter(Mandatory = $true)] + [string] $Version2 + ) + + # Split the versions into their components + $v1Parts = $Version1 -split '\.' + $v2Parts = $Version2 -split '\.' + + # Ensure both versions have the same number of components + $maxLength = [math]::Max($v1Parts.Length, $v2Parts.Length) + #$v1Parts = $v1Parts + (0..($maxLength - $v1Parts.Length) | ForEach-Object { '*' }) + #$v2Parts = $v2Parts + (0..($maxLength - $v2Parts.Length) | ForEach-Object { '*' }) + + for ($i = 0; $i -lt $maxLength; $i++) { + $part1 = $v1Parts[$i] + $part2 = $v2Parts[$i] + + if ($part1 -eq '*' -or $part2 -eq '*') { + continue + } + + if ($part1 -match "-preview" -or $part2 -match "-preview") { + if ($part1 -match "-preview" -and $part2 -match "-preview") { + $part1 = $part1 -replace "-preview", "" + $part2 = $part2 -replace "-preview", "" + + if ($part1 -eq '*' -or $part2 -eq '*') { + continue + } + + if ([int]$part1 -lt [int]$part2) { + return -1 + } + elseif ([int]$part1 -gt [int]$part2) { + return 1 + } + } + else { + return -1 + } + + } + + if ([int]$part1 -lt [int]$part2) { + return -1 + } + elseif ([int]$part1 -gt [int]$part2) { + return 1 + } + } + + return 0 +} + +# Example usage: +# Compare-SemanticVersion "1.2.3" "1.2.4" # Returns -1 +# Compare-SemanticVersion "1.2.3" "1.2.*" # Returns 0 +# Compare-SemanticVersion "1.2.3" "1.2.3" # Returns 0 +# Compare-SemanticVersion "1.2.3" "1.2.2" # Returns 1 \ No newline at end of file diff --git a/Scripts/Helpers/Confirm-ObjectValueEqualityDeep.ps1 b/Scripts/Helpers/Confirm-ObjectValueEqualityDeep.ps1 index b2f4c116..4650f6b6 100644 --- a/Scripts/Helpers/Confirm-ObjectValueEqualityDeep.ps1 +++ b/Scripts/Helpers/Confirm-ObjectValueEqualityDeep.ps1 @@ -37,9 +37,31 @@ function Confirm-ObjectValueEqualityDeep { # $Object1 or $Object2 is an array or ArrayList if ($Object1 -isnot [System.Collections.Ilist]) { $Object1 = @($Object1) + # This resolves a unique situation where GraphApi returns a string instead of an array for a metadata value, very unique situation + # By testing the json and count, we can confirm that it is a json string and convert it to an array + # A try catch is included to revert to the value we normally use in the event of failure just in case there is a scenario that has not been accounted for + + try { + $Object1 = $Object1 | ConvertFrom-Json -Depth 100 + } + catch { + $Object1 = @($Object1) + } + } elseif ($Object2 -isnot [System.Collections.Ilist]) { $Object2 = @($Object2) + # This resolves a unique situation where GraphApi returns a string instead of an array for a metadata value, very unique situation + # By testing the json and count, we can confirm that it is a json string and convert it to an array + # A try catch is included to revert to the value we normally use in the event of failure just in case there is a scenario that has not been accounted for + + try { + $Object2 = $Object2 | ConvertFrom-Json -Depth 100 + } + catch { + $Object2 = @($Object2) + } + } if ($Object1.Count -ne $Object2.Count) { return $false @@ -165,4 +187,4 @@ function Confirm-ObjectValueEqualityDeep { return $false } } -} +} \ No newline at end of file diff --git a/Scripts/Helpers/Confirm-PolicyDefinitionsInPolicySetMatch.ps1 b/Scripts/Helpers/Confirm-PolicyDefinitionsInPolicySetMatch.ps1 index 6b503117..370c9b45 100644 --- a/Scripts/Helpers/Confirm-PolicyDefinitionsInPolicySetMatch.ps1 +++ b/Scripts/Helpers/Confirm-PolicyDefinitionsInPolicySetMatch.ps1 @@ -2,7 +2,8 @@ function Confirm-PolicyDefinitionsInPolicySetMatch { [CmdletBinding()] param ( $Object1, - $Object2 + $Object2, + $Definitions ) # check for null or empty scenarios @@ -39,9 +40,20 @@ function Confirm-PolicyDefinitionsInPolicySetMatch { return $false } if ($null -ne $item2.definitionVersion) { - # ignore aut-generated definitionVersion, only compare if Policy definition entry has a definitionVersion - $definitionVersionMatches = $item1.definitionVersion -eq $item2.definitionVersion - if (!$definitionVersionMatches) { + # ignore auto-generated definitionVersion, only compare if Policy definition entry has a definitionVersion + $deployedPolicyDefinitionVersion = $Definitions[$item1.policyDefinitionId].properties.version + if ($null -eq $deployedPolicyDefinitionVersion) { + # Custom policy definition - version is in a different place + $deployedPolicyDefinitionVersion = $Definitions[$item1.policyDefinitionId].metadata.version + } + # $definitionVersionMatches = $item1.definitionVersion -eq $item2.definitionVersion + # if (!$definitionVersionMatches) { + # return $false + # } + $definitionVersionMatches = Compare-SemanticVersion -Version1 $deployedPolicyDefinitionVersion -Version2 $item2.definitionVersion + if ($definitionVersionMatches -ne 0) { + Write-Verbose "Definition Id: $($item1.policyDefinitionId)" + Write-Verbose "DefinitionVersion does not match: Azure: $deployedPolicyDefinitionVersion, Local: $($item2.definitionVersion)" return $false } } diff --git a/Scripts/Helpers/RestMethods/Get-AzResourceListRestMethod.ps1 b/Scripts/Helpers/RestMethods/Get-AzResourceListRestMethod.ps1 index 4c157866..2d6e8b13 100644 --- a/Scripts/Helpers/RestMethods/Get-AzResourceListRestMethod.ps1 +++ b/Scripts/Helpers/RestMethods/Get-AzResourceListRestMethod.ps1 @@ -50,5 +50,14 @@ function Get-AzResourceListRestMethod { $resources.value += $subnetResources.value } + # Get the automation account variables for all the automation accounts found in the basic resources + $automationAccounts = $($resources.value | Where-Object { $_.type -eq 'Microsoft.Automation/automationAccounts' }) + foreach ($account in $automationAccounts) { + $ApiVersion = "2023-11-01" + $path = "$($account.id)/variables?api-version=$ApiVersion" + $variableResources = Invoke-AzRestMethodCustom -path $path -method GET + $resources.value += $variableResources.value + } + Write-Output $resources.value -NoEnumerate }