internal/functions/Build-PolicyEnrollmentPlan.ps1
|
function Build-PolicyEnrollmentPlan { [CmdletBinding()] param ( [string] $EnrollmentsRootFolder, $PacEnvironment, $DeployedEnrollments, $Enrollments ) Write-ModernSection -Title "Processing Policy Enrollments (Preview)" -Color Blue Write-ModernStatus -Message "Source folder: $EnrollmentsRootFolder" -Status "info" -Indent 2 [array] $enrollmentFiles = @() $enrollmentFiles += Get-ChildItem -Path $EnrollmentsRootFolder -Recurse -File -Filter "*.json" $enrollmentFiles += Get-ChildItem -Path $EnrollmentsRootFolder -Recurse -File -Filter "*.jsonc" if ($enrollmentFiles.Length -gt 0) { Write-ModernStatus -Message "Found $($enrollmentFiles.Length) policy enrollment files" -Status "success" -Indent 2 } else { Write-ModernStatus -Message "No policy enrollment files found - managed enrollments may be deleted" -Status "warning" -Indent 2 } $deployedManagedEnrollments = $DeployedEnrollments.managed $deleteCandidates = $deployedManagedEnrollments.Clone() $desiredStateStrategy = $PacEnvironment.desiredState.strategy $uniqueIds = @{} foreach ($enrollmentFile in $enrollmentFiles) { Write-ModernStatus -Message "Processing policy enrollment file '$($enrollmentFile.FullName)'" -Status "info" -Indent 2 $content = Get-Content -Path $enrollmentFile.FullName -Raw -ErrorAction Stop try { $definedEnrollment = ConvertFrom-Json $content -AsHashTable -Depth 100 } catch { throw "Policy Enrollment JSON file '$($enrollmentFile.FullName)' is not valid." } $name = $definedEnrollment.name $policyAssignmentId = $definedEnrollment.policyAssignmentId if ([string]::IsNullOrWhiteSpace($name) -or [string]::IsNullOrWhiteSpace($policyAssignmentId)) { throw "Policy Enrollment file '$($enrollmentFile.FullName)' must define name and policyAssignmentId." } if (-not (Confirm-ValidPolicyResourceName -Name $name)) { throw "Policy Enrollment name '$name' in '$($enrollmentFile.FullName)' contains invalid characters or ends with a space." } $hasScope = -not [string]::IsNullOrWhiteSpace($definedEnrollment.scope) $hasScopes = $null -ne $definedEnrollment.scopes if ($hasScope -eq $hasScopes) { throw "Policy Enrollment file '$($enrollmentFile.FullName)' must define exactly one of scope or scopes." } if ($hasScope) { $scopes = @($definedEnrollment.scope) } else { $scopes = $definedEnrollment.scopes if ($scopes -isnot [array] -or $scopes.Count -eq 0) { throw "Policy Enrollment scopes in '$($enrollmentFile.FullName)' must be a non-empty array of strings." } foreach ($scope in $scopes) { if ($scope -isnot [string] -or [string]::IsNullOrWhiteSpace($scope)) { throw "Policy Enrollment scopes in '$($enrollmentFile.FullName)' must be a non-empty array of strings." } } } $assignmentScopeValidation = $definedEnrollment.assignmentScopeValidation if ([string]::IsNullOrWhiteSpace($assignmentScopeValidation)) { $assignmentScopeValidation = "Default" } elseif ($assignmentScopeValidation -notin @("Default", "DoNotValidate")) { throw "Policy Enrollment assignmentScopeValidation '$assignmentScopeValidation' in '$($enrollmentFile.FullName)' must be Default or DoNotValidate." } $metadata = @{} if ($null -ne $definedEnrollment.metadata) { $metadata = Get-DeepCloneAsOrderedHashtable $definedEnrollment.metadata } if ($metadata.ContainsKey("pacOwnerId")) { throw "Policy Enrollment metadata.pacOwnerId is reserved for EPAC usage in '$($enrollmentFile.FullName)'." } $metadata.pacOwnerId = $PacEnvironment.pacOwnerId foreach ($scope in $scopes) { $id = "$scope/providers/Microsoft.Authorization/policyEnrollments/$name" if ($uniqueIds.ContainsKey($id)) { throw "Duplicate Policy Enrollment id '$id' in '$($enrollmentFile.FullName)' and '$($uniqueIds[$id])'." } $uniqueIds[$id] = $enrollmentFile.FullName $displayLabel = if ([string]::IsNullOrWhiteSpace($definedEnrollment.displayName)) { $name } else { $definedEnrollment.displayName } $enrollment = [ordered]@{ id = $id name = $name scope = $scope displayName = $definedEnrollment.displayName description = $definedEnrollment.description metadata = $metadata policyAssignmentId = $policyAssignmentId policyDefinitionReferenceIds = $definedEnrollment.policyDefinitionReferenceIds resourceSelectors = $definedEnrollment.resourceSelectors assignmentScopeValidation = $assignmentScopeValidation } if ($deployedManagedEnrollments.ContainsKey($id)) { $deleteCandidates.Remove($id) $deployedEnrollment = $deployedManagedEnrollments[$id] $metadataMatches, $changePacOwnerId = Confirm-MetadataMatches ` -ExistingMetadataObj $deployedEnrollment.metadata ` -DefinedMetadataObj $metadata $displayNameMatches = $deployedEnrollment.displayName -eq $enrollment.displayName $descriptionMatches = $deployedEnrollment.description -eq $enrollment.description $policyAssignmentIdMatches = $deployedEnrollment.policyAssignmentId -eq $enrollment.policyAssignmentId $assignmentScopeValidationMatches = $deployedEnrollment.assignmentScopeValidation -eq $enrollment.assignmentScopeValidation $policyDefinitionReferenceIdsMatch = Confirm-ObjectValueEqualityDeep $deployedEnrollment.policyDefinitionReferenceIds $enrollment.policyDefinitionReferenceIds $resourceSelectorsMatch = Confirm-ObjectValueEqualityDeep $deployedEnrollment.resourceSelectors $enrollment.resourceSelectors $matches = ` $displayNameMatches ` -and $descriptionMatches ` -and $policyAssignmentIdMatches ` -and $assignmentScopeValidationMatches ` -and $policyDefinitionReferenceIdsMatch ` -and $resourceSelectorsMatch ` -and $metadataMatches ` -and -not $changePacOwnerId if ($matches) { $Enrollments.numberUnchanged++ } else { $changes = [System.Collections.ArrayList]::new() if (-not $displayNameMatches) { $null = $changes.Add("displayName") } if (-not $descriptionMatches) { $null = $changes.Add("description") } if (-not $policyAssignmentIdMatches) { $null = $changes.Add("policyAssignmentId") } if (-not $assignmentScopeValidationMatches) { $null = $changes.Add("assignmentScopeValidation") } if (-not $policyDefinitionReferenceIdsMatch) { $null = $changes.Add("policyDefinitionReferenceIds") } if (-not $resourceSelectorsMatch) { $null = $changes.Add("resourceSelectors") } if (-not $metadataMatches -or $changePacOwnerId) { $null = $changes.Add("metadata") } Write-ModernStatus -Message "Update ($($changes -join ', ')): '$displayLabel' at scope '$scope'" -Status "update" -Indent 4 $Enrollments.update[$id] = $enrollment $Enrollments.numberOfChanges++ } } else { Write-ModernStatus -Message "New: '$displayLabel' at scope '$scope'" -Status "success" -Indent 4 $Enrollments.new[$id] = $enrollment $Enrollments.numberOfChanges++ } } } foreach ($id in $deleteCandidates.Keys) { $deployedEnrollment = $deleteCandidates[$id] if (Confirm-DeleteForStrategy -PacOwner $deployedEnrollment.pacOwner -Strategy $desiredStateStrategy) { $displayLabel = if ([string]::IsNullOrWhiteSpace($deployedEnrollment.displayName)) { $deployedEnrollment.name } else { $deployedEnrollment.displayName } Write-ModernStatus -Message "Delete: '$displayLabel' at scope '$($deployedEnrollment.scope)'" -Status "error" -Indent 4 $Enrollments.delete[$id] = $deployedEnrollment $Enrollments.numberOfChanges++ } else { Write-ModernStatus -Message "Skip delete ($($deployedEnrollment.pacOwner),$desiredStateStrategy): '$($deployedEnrollment.name)' at scope '$($deployedEnrollment.scope)'" -Status "skip" -Indent 4 } } if ($Enrollments.numberUnchanged -gt 0) { Write-ModernStatus -Message "Unchanged Policy Enrollments: $($Enrollments.numberUnchanged)" -Status "status" -Indent 2 } } |