Skip to content

Commit 11bebe9

Browse files
anwatherAnthony Watherston
and
Anthony Watherston
authored
Hydration updates (#963)
Co-authored-by: Anthony Watherston <Anthony.Watherston@microsoft.com>
1 parent 1289026 commit 11bebe9

10 files changed

+155
-9
lines changed

Docs/guidance-remediation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ The objective at this point is to reduce the administration teams' effort to cor
6060

6161
While Infrastructure as Code is an excellent first layer, the second layer in a Defense In Depth model is to enforce this. In Azure Resource Manager, we use Azure Policy Remediation Tasks to accomplish this. Whereas Start-AzPolicyRemediation does allow very targetted deployments, EPAC seeks to take corrective action in broad swaths using the security structure that has been implemented. To this end, the cmdlet New-AzRemediationTasks was created, and can be used in a pipeline to remediate all policyAssignments (that have that capability) in a single pipeline action that should be scheduled with a cron trigger.
6262

63-
Once the *Updating Security Posture* Workstreams above are complete, it is time to move on into the upper tiers of CMM for ARM Governance, congratulations! The environment will be largely self-correcting after this is compelte.
63+
Once the *Updating Security Posture* Workstreams above are complete, it is time to move on into the upper tiers of CMM for ARM Governance, congratulations! The environment will be largely self-correcting after this is complete.
6464

6565
1. Change `enforcementMode: 'DoNotEnforce'` to `enforcementMode: 'Default'` where applicable
6666
1. Ensure `DesireStateConfiguration: 'full'` is configured

Docs/integrating-with-alz-library.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,16 +101,16 @@ The next command will generate policy assignments based on the values in this fi
101101

102102
```ps1
103103
# Sync the ALZ policies and assign to the "epac-dev" PAC environment.
104-
Sync-ALZPolicyFromLibrary -DefinitionsRootFolder .\Definitions -Type ALZ -DefinitionsRootFolder .\Definitions -PacEnvironmentSelector "epac-dev"
104+
Sync-ALZPolicyFromLibrary -DefinitionsRootFolder .\Definitions -Type ALZ -PacEnvironmentSelector "epac-dev"
105105
106106
# Sync the ALZ policies and assign to the "epac-dev" PAC environment. Specify a tagged version of the ALZ library
107-
Sync-ALZPolicyFromLibrary -DefinitionsRootFolder .\Definitions -Type ALZ -DefinitionsRootFolder .\Definitions -PacEnvironmentSelector "epac-dev" -Tag "platform/alz/2025.02.0"
107+
Sync-ALZPolicyFromLibrary -DefinitionsRootFolder .\Definitions -Type ALZ -PacEnvironmentSelector "epac-dev" -Tag "platform/alz/2025.02.0"
108108
109109
# Sync the ALZ policies from a cloned/modified library
110-
Sync-ALZPolicyFromLibrary -DefinitionsRootFolder .\Definitions -Type ALZ -DefinitionsRootFolder .\Definitions -PacEnvironmentSelector "epac-dev" -LibraryPath <<path to library>>
110+
Sync-ALZPolicyFromLibrary -DefinitionsRootFolder .\Definitions -Type ALZ -PacEnvironmentSelector "epac-dev" -LibraryPath <<path to library>>
111111
112112
# Sync the AMBA policies and assign to the "epac-dev" PAC environment.
113-
Sync-ALZPolicyFromLibrary -DefinitionsRootFolder .\Definitions -Type AMBA -DefinitionsRootFolder .\Definitions -PacEnvironmentSelector "epac-dev"
113+
Sync-ALZPolicyFromLibrary -DefinitionsRootFolder .\Definitions -Type AMBA -PacEnvironmentSelector "epac-dev"
114114
```
115115

116116
Carefully review the generated policy assigments and ensure all parameter and scope information is correct.

Scripts/CloudAdoptionFramework/New-ALZPolicyDefaultStructure.ps1

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,24 @@ foreach ($mg in $archetypeDefinitionFile.management_groups) {
6262
$policyDefaultFile = Get-Content -Path "$LibraryPath\platform\$($Type.ToLower())\alz_policy_default_values.json" | ConvertFrom-Json
6363

6464
foreach ($parameter in $policyDefaultFile.defaults) {
65+
# Grab the first policy assignment to grab default value of the parameter
66+
$parameterAssignmentName = $parameter.policy_assignments[0].parameter_names[0]
67+
$assignment = $parameter.policy_assignments[0]
68+
69+
$assingmentFileName = ("$($assignment.policy_assignment_name).alz_policy_assignment.json")
70+
if ($type -eq "AMBA") {
71+
$assingmentFileName = $assingmentFileName -replace ("-", "_")
72+
}
73+
$file = Get-ChildItem -Recurse -Path ".\temp" -Filter "$assingmentFileName" -File | Select-Object -First 1
74+
$jsonContent = Get-Content -Path $file.FullName -Raw | ConvertFrom-Json
75+
$tempDefaultParamValue = $jsonContent.properties.parameters.$parameterAssignmentName.value
76+
6577
$obj = @{
6678
description = $parameter.description
6779
policy_assignment_name = $parameter.policy_assignments.policy_assignment_name
6880
parameters = @{
6981
parameter_name = $parameter.policy_assignments[0].parameter_names[0]
70-
value = ""
82+
value = $tempDefaultParamValue
7183
}
7284
}
7385

Scripts/Helpers/Add-HelperScripts.ps1

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#Requires -PSEdition Core
22

3+
# Define ./Scripts location in repo based on it being the parent of the Helpers directiory in which this script is located
4+
$scriptRoot = Split-Path $PSScriptRoot -Parent
5+
36
# Load cmdlets
47
. "$PSScriptRoot/Add-ErrorMessage.ps1"
58
. "$PSScriptRoot/Add-SelectedPacArray.ps1"
@@ -120,3 +123,48 @@
120123
. "$PSScriptRoot/RestMethods/Set-AzPolicySetDefinitionRestMethod.ps1"
121124
. "$PSScriptRoot/RestMethods/Set-AzPolicyExemptionRestMethod.ps1"
122125
. "$PSScriptRoot/RestMethods/Set-AzRoleAssignmentRestMethod.ps1"
126+
127+
# Hydration Kit Content
128+
129+
. "$PSScriptRoot/Build-HydrationAssignmentPlan.ps1"
130+
. "$PSScriptRoot/Build-HydrationPolicyPlan.ps1"
131+
. "$PSScriptRoot/Build-HydrationPolicySetPlan.ps1"
132+
. "$PSScriptRoot/Compare-HydrationMetadata.ps1"
133+
. "$PSScriptRoot/Copy-HydrationOrderedHashtable.ps1"
134+
. "$PSScriptRoot/Export-HydrationObjectToJsonFile.ps1"
135+
. "$PSScriptRoot/Get-HydrationChildManagementGroupNameList.ps1"
136+
. "$PSScriptRoot/Get-HydrationDefinitionSubfolderByContentId.ps1"
137+
. "$PSScriptRoot/Get-HydrationEpacRepo.ps1"
138+
. "$PSScriptRoot/Get-HydrationUserObjectId.ps1"
139+
. "$PSScriptRoot/Join-HydrationHashtableToPath.ps1"
140+
. "$PSScriptRoot/New-HydrationAnswerFile.ps1"
141+
. "$PSScriptRoot/New-HydrationAnswerSet.ps1"
142+
. "$PSScriptRoot/New-HydrationChangeEntry.ps1"
143+
. "$PSScriptRoot/New-HydrationContinuePrompt.ps1"
144+
. "$PSScriptRoot/New-HydrationManagementGroupChildren.ps1"
145+
. "$PSScriptRoot/New-HydrationMenuResponse.ps1"
146+
. "$PSScriptRoot/New-HydrationMultipleChoicePrompt.ps1"
147+
. "$PSScriptRoot/New-HydrationSeparatorBlock.ps1"
148+
. "$PSScriptRoot/Remove-HydrationChildHierarchy.ps1"
149+
. "$PSScriptRoot/Test-HydrationAccess.ps1"
150+
. "$PSScriptRoot/Test-HydrationCaf3Hierarchy.ps1"
151+
. "$PSScriptRoot/Update-HydrationModuleToSupportedVersion.ps1"
152+
. "$PSScriptRoot/Update-HydrationStarterKitAssignmentScope.ps1"
153+
. "$PSScriptRoot/Write-HydrationLogFile.ps1"
154+
155+
. "$scriptRoot/HydrationKit/Build-HydrationDeploymentPlans.ps1"
156+
. "$scriptRoot/HydrationKit/Copy-HydrationManagementGroupHierarchy.ps1"
157+
. "$scriptRoot/HydrationKit/Install-HydrationEpac.ps1"
158+
. "$scriptRoot/HydrationKit/New-FilteredExceptionFile.ps1"
159+
. "$scriptRoot/HydrationKit/New-HydrationAssignmentPacSelector.ps1"
160+
. "$scriptRoot/HydrationKit/New-HydrationCaf3Hierarchy.ps1"
161+
. "$scriptRoot/HydrationKit/New-HydrationDefinitionsFolder.ps1"
162+
. "$scriptRoot/HydrationKit/New-HydrationGlobalSettingsFile.ps1"
163+
. "$scriptRoot/HydrationKit/New-HydrationPolicyDocumentationSourceFile.ps1"
164+
. "$scriptRoot/HydrationKit/Remove-HydrationManagementGroupRecursively.ps1"
165+
. "$scriptRoot/HydrationKit/Test-HydrationConnection.ps1"
166+
. "$scriptRoot/HydrationKit/Test-HydrationManagementGroupName.ps1"
167+
. "$scriptRoot/HydrationKit/Test-HydrationPath.ps1"
168+
. "$scriptRoot/HydrationKit/Test-HydrationRbacAssignment.ps1"
169+
. "$scriptRoot/HydrationKit/Update-HydrationAssignmentDestination.ps1"
170+
. "$scriptRoot/HydrationKit/Update-HydrationDefinitionFolderStructureByAssignment.ps1"

Scripts/Helpers/Build-HydrationAssignmentPlan.ps1

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,9 @@ function Build-HydrationAssignmentPlan {
326326
$assignmentRecord.Set_Item('metadataChanged', !($metadataMatches))
327327
$assignmentRecord.Set_Item('oldMetadata', $deployedPolicyAssignmentProperties.metadata)
328328
$assignmentRecord.Set_Item('newMetadata', $metadata)
329+
$metadataComparison = Compare-HydrationMetadata -oldKeys $deployedPolicyAssignmentProperties.metadata -newKeys $metadata
330+
$assignmentRecord.Set_Item('metadataComparison', $metadataComparison)
331+
329332
}
330333
if (!$definitionVersionMatches) {
331334
$assignmentRecord.Set_Item('definitionVersionChanged', !($definitionVersionMatches))

Scripts/Helpers/Build-HydrationPolicyPlan.ps1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@ function Build-HydrationPolicyPlan {
245245
$fileRecord.Set_Item('metadataChanged', !($metadataMatches))
246246
$fileRecord.Set_Item('oldMetadata', $deployedDefinitionProperties.metadata)
247247
$fileRecord.Set_Item('newMetadata', $metadata)
248+
$metadataComparison = Compare-HydrationMetadata -oldKeys $deployedDefinitionProperties.metadata -newKeys $metadata
249+
$fileRecord.Set_Item('metadataComparison', $metadataComparison)
248250
}
249251
if (!$parametersMatch -and !$incompatible) {
250252
$fileRecord.Set_Item('parametersChanged', !($parametersMatch))

Scripts/Helpers/Build-HydrationPolicySetPlan.ps1

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ function Build-HydrationPolicySetPlan {
227227
$parametersMatch, $incompatible = Confirm-ParametersDefinitionMatch `
228228
-ExistingParametersObj $deployedDefinition.parameters `
229229
-DefinedParametersObj $parameters
230-
Remove-Variable policyDefinitionsMatch -ErrorAction SilentlyContinue
230+
Remove-Variable policyDefinitionsMatch -ErrorAction SilentlyContinue # TODO: THis is where it breaks
231231
$policyDefinitionsMatch = Confirm-PolicyDefinitionsInPolicySetMatch `
232232
$deployedDefinition.policyDefinitions `
233233
$policyDefinitionsFinal
@@ -238,7 +238,9 @@ function Build-HydrationPolicySetPlan {
238238
$deletedPolicyDefinitionGroups = !$policyDefinitionGroupsMatch -and ($null -eq $policyDefinitionGroupsFinal -or $policyDefinitionGroupsFinal.Length -eq 0)
239239
# Update Policy Set in Azure if necessary
240240
$containsReplacedPolicy = $false
241-
$replacedPolicyList = @()
241+
if($ExtendedReporting){
242+
$replacedPolicyList = @()
243+
}
242244
foreach ($policyDefinitionEntry in $policyDefinitionsFinal) {
243245
$policyId = $policyDefinitionEntry.policyDefinitionId
244246
if ($ReplaceDefinitions.ContainsKey($policyId)) {
@@ -249,6 +251,7 @@ function Build-HydrationPolicySetPlan {
249251
else{
250252
# Capture full list of replaced policies for ExtendedReporting
251253
$replacedPolicyList += $policyId
254+
break
252255
}
253256
}
254257
}
@@ -345,6 +348,14 @@ function Build-HydrationPolicySetPlan {
345348
$fileRecord.Set_Item('metadataChanged', !($metadataMatches))
346349
$fileRecord.Set_Item('oldMetadata', $deployedDefinition.metadata)
347350
$fileRecord.Set_Item('newMetadata', $metadata)
351+
$metadataComparison = Compare-HydrationMetadata -oldKeys $deployedDefinition.metadata -newKeys $metadata
352+
try{
353+
$fileRecord.Set_Item('metadataComparison', $metadataComparison)
354+
355+
}
356+
catch{
357+
Write-Error "Failed to set metadataComparison for $($fileRecord.id) with $($metadataComparison)"
358+
}
348359
}
349360
if (!$parametersMatch -and !$incompatible) { # I don't think this is really useful, we don't test on it anywhere, we test on incompatible... which appears to be the same intended outcome.
350361
$fileRecord.Set_Item('parametersChanged', !($parametersMatch))
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
function Compare-HydrationMetadata {
2+
3+
[CmdletBinding()]
4+
param (
5+
[Parameter(Mandatory = $false)]
6+
$oldKeys = @{},
7+
[Parameter(Mandatory = $false)]
8+
$newKeys = @{}
9+
)
10+
11+
$metadataComparison = [ordered]@{
12+
ValueDifferences = @{}
13+
OnlyInOld = @()
14+
OnlyInNew = @()
15+
}
16+
17+
# Compare values for matching keys
18+
$valueDifferences = @()
19+
foreach ($key in ($oldKeys.keys + $newKeys.keys | Select-Object -Unique)) {
20+
$oldValue = $oldKeys[$key]
21+
$newValue = $newKeys[$key]
22+
if ($oldValue -ne $newValue) {
23+
$valueDifferences += `
24+
[PSCustomObject]@{
25+
Key = $key
26+
OldValue = $oldValue
27+
NewValue = $newValue
28+
}
29+
}
30+
}
31+
32+
# Find keys only in old or only in new
33+
$onlyInOld = $oldKeys | Where-Object { $_ -notin $newKeys }
34+
$onlyInNew = $newKeys | Where-Object { $_ -notin $oldKeys }
35+
36+
$metadataComparison = [ordered]@{
37+
ValueDifferences = $valueDifferences
38+
OnlyInOld = $onlyInOld
39+
OnlyInNew = $onlyInNew
40+
}
41+
42+
return $metadataComparison
43+
}

Scripts/Helpers/Confirm-PolicyDefinitionsInPolicySetMatch.ps1

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,33 @@ function Confirm-PolicyDefinitionsInPolicySetMatch {
4141
}
4242

4343
# Validate the Azure definitionVersion with the local definitionVersion, if the local definitionVersion doesn't exist and the Azure definitionVersion is not equal to latest policy version then return false
44-
$definitionVersionMatches = Compare-SemanticVersion -Version1 $($item1.definitionVersion ?? $Definitions[$item1.policyDefinitionId].properties.version ?? '1.*.*') -Version2 $($item2.definitionVersion ?? $Definitions[$item1.policyDefinitionId].properties.version ?? '1.*.*')
44+
# This addresses an error that occurs when there is a null value in the definitionVersion field that cropped up when we removed the variable prior to processing to fix a bug spotted in Build-HydrationDeploymentPlans where the values were retained, and adversely affecting the update information.
45+
try {
46+
if ($null -eq $item1.definitionVersion -and $null -eq $item2.definitionVersion) {
47+
# Compare-SemanticVersion -Version1 0 -Version2 0 is always 0, so we forego the calculation and set it
48+
$definitionVersionMatches = 0
49+
}
50+
elseif ($null -eq $item1.definitionVersion) {
51+
# Compare-SemanticVersion -Version1 0 -Version2 (anything not 0) is always -1, so we forego the calculation and set it
52+
# $definitionVersionMatches = Compare-SemanticVersion -Version1 0 -Version2 $item2.definitionVersion
53+
$definitionVersionMatches = -1
54+
}
55+
elseif ($null -eq $item2.definitionVersion) {
56+
# Compare-SemanticVersion -Version1 (anything not 0) -Version2 0 is always 1, so we forego the calculation and set it
57+
# $definitionVersionMatches = Compare-SemanticVersion -Version1 $item1.definitionVersion -Version2 0
58+
$definitionVersionMatches = 1
59+
}
60+
else {
61+
# If neither of the definitionVersion values are null, then the compare can proceed without error
62+
$definitionVersionMatches = Compare-SemanticVersion -Version1 $($item1.definitionVersion ?? $Definitions[$item1.policyDefinitionId].properties.version ?? '1.*.*') -Version2 $($item2.definitionVersion ?? $Definitions[$item1.policyDefinitionId].properties.version ?? '1.*.*')
63+
}
64+
}
65+
catch {
66+
Write-Information "Comparison has generated an error."
67+
Write-Information "Item1: $($item1.policyDefinitionId) $($item1.policySetDefinitionId) $($item1.policyDefinitionName) $($item1.policySetDefinitionName)"
68+
Write-Information "Item2: $($item2.policyDefinitionId) $($item2.policySetDefinitionId) $($item2.policyDefinitionName) $($item2.policySetDefinitionName)"
69+
continue
70+
}
4571
if ($definitionVersionMatches -ne 0) {
4672
Write-Verbose "Definition Id: $($item1.policyDefinitionId)"
4773
Write-Verbose "DefinitionVersion does not match: Azure: $($item1.definitionVersion), Local: $($item2.definitionVersion)"

Scripts/HydrationKit/Build-HydrationDeploymentPlans.ps1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ function Build-HydrationDeploymentPlans {
202202
oldOwner = ""
203203
newOwner = ""
204204
metadataChanged = $false
205+
metadataComparison = @{}
205206
oldMetadata = @{}
206207
newMetadata = @{}
207208
definitionVersionChanged = $false

0 commit comments

Comments
 (0)