Scripts/HydrationKit/Build-HydrationDeploymentPlans.ps1 (580 lines of code) (raw):
<#
.SYNOPSIS
Builds the deployment plans for the Policy as Code (PAC) environment.
.PARAMETER PacEnvironmentSelector
Defines which Policy as Code (PAC) environment we are using, if omitted, the script prompts for a value. The values are read from `$DefinitionsRootFolder/global-settings.jsonc.
.PARAMETER DefinitionsRootFolder
Definitions folder path. Defaults to environment variable `$env:PAC_DEFINITIONS_FOLDER or './Definitions'.
.PARAMETER OutputFolder
Output folder path for plan files. Defaults to environment variable `$env:PAC_OUTPUT_FOLDER or './Output'.
.PARAMETER Interactive
Script is used interactively. Script can prompt the interactive user for input.
.PARAMETER DevOpsType
If set, outputs variables consumable by conditions in a DevOps pipeline. Valid values are '', 'ado' and 'gitlab'.
.Parameter FullExportForDocumentationFile
Causes the script to output a full list of policySets in use for consumption by documentation generation automation. This will not output build plancs for pipeline deployment.
.Parameter ExtendedReporting
Output a report with specific data used for evaluation on each object, useful for debugging as well as large sets of changes.
.EXAMPLE
Build-HydrationDeploymentPlans -PacEnvironmentSelector "dev"
Builds the deployment plans for the Policy as Code (PAC) environment 'dev'.
.EXAMPLE
Build-HydrationDeploymentPlans -PacEnvironmentSelector "dev" -DevOpsType "ado"
Builds the deployment plans for the Policy as Code (PAC) environment 'dev' and outputs variables consumable by conditions in an Azure DevOps pipeline.
.LINK
https://azure.github.io/enterprise-azure-policy-as-code/#deployment-scripts
#>
function Build-HydrationDeploymentPlans {
[CmdletBinding()]
param (
[parameter(HelpMessage = "Defines which Policy as Code (PAC) environment we are using, if omitted, the script prompts for a value. The values are read from `$DefinitionsRootFolder/global-settings.jsonc.", Position = 0)]
[string] $PacEnvironmentSelector = "",
[Parameter(HelpMessage = "Definitions folder path. Defaults to environment variable `$env:PAC_DEFINITIONS_FOLDER or './Definitions'.")]
[string]$DefinitionsRootFolder,
[Parameter(HelpMessage = "Output folder path for plan files. Defaults to environment variable `$env:PAC_OUTPUT_FOLDER or './Output'.")]
[string]$OutputFolder,
[Parameter(HelpMessage = "If set, only build the exemptions plan.")]
[switch] $BuildExemptionsOnly,
[Parameter(HelpMessage = "Script is used interactively. Script can prompt the interactive user for input.")]
[switch] $Interactive,
[Parameter(HelpMessage = "If set, outputs variables consumable by conditions in a DevOps pipeline.")]
[ValidateSet("ado", "gitlab", "")]
[string] $DevOpsType = "",
[switch]$SkipNotScopedExemptions,
[parameter(Mandatory = $false, HelpMessage = "Causes the script to output a full list of policySets in use for consumption by documentation generation automation. This will not output build plancs for pipeline deployment.")]
[switch] $FullExportForDocumentationFile,
[parameter(Mandatory = $false, HelpMessage = "Output a report with specific data used for evaluation on each object, useful for debugging as well as large sets of changes.")]
[switch] $ExtendedReporting
)
$PSDefaultParameterValues = @{
"Write-Information:InformationVariable" = "+global:epacInfoStream"
}
Clear-Variable -Name epacInfoStream -Scope global -Force -ErrorAction SilentlyContinue
# Dot Source Helper Scripts
# TODO: Not necessary as a function, reinstate when returned to Deploy folder
# . "$PSScriptRoot/../Helpers/Add-HelperScripts.ps1"
# Initialize
$InformationPreference = "Continue"
$pacEnvironment = Select-PacEnvironment $PacEnvironmentSelector -DefinitionsRootFolder $DefinitionsRootFolder -OutputFolder $OutputFolder -Interactive $Interactive
$null = Set-AzCloudTenantSubscription -Cloud $pacEnvironment.cloud -TenantId $pacEnvironment.tenantId -Interactive $pacEnvironment.interactive -DeploymentDefaultContext $pacEnvironment.defaultContext
# Telemetry
if ($pacEnvironment.telemetryEnabled) {
Write-Information "Telemetry is enabled"
Submit-EPACTelemetry -Cuapid "pid-3c88f740-55a8-4a96-9fba-30a81b52151a" -DeploymentRootScope $pacEnvironment.deploymentRootScope
}
else {
Write-Information "Telemetry is disabled"
}
Write-Information ""
#region plan data structures
$buildSelections = @{
buildAny = $false
buildPolicyDefinitions = $false
buildPolicySetDefinitions = $false
buildPolicyAssignments = $false
buildPolicyExemptions = $false
}
$policyDefinitions = @{
new = @{}
update = @{}
replace = @{}
delete = @{}
numberOfChanges = 0
numberUnchanged = 0
}
$policyRoleIds = @{}
$allDefinitions = @{
policydefinitions = @{}
policysetdefinitions = @{}
}
$replaceDefinitions = @{}
$policySetDefinitions = @{
new = @{}
update = @{}
replace = @{}
delete = @{}
numberOfChanges = 0
numberUnchanged = 0
}
$assignments = @{
new = @{}
update = @{}
replace = @{}
delete = @{}
numberOfChanges = 0
numberUnchanged = 0
}
$roleAssignments = @{
numberOfChanges = 0
added = [System.Collections.ArrayList]::new()
updated = [System.Collections.ArrayList]::new()
removed = [System.Collections.ArrayList]::new()
}
$allAssignments = @{}
$exemptions = @{
new = @{}
update = @{}
replace = @{}
delete = @{}
numberOfOrphans = 0
numberOfExpired = 0
numberOfChanges = 0
numberUnchanged = 0
}
$pacOwnerId = $pacEnvironment.pacOwnerId
$timestamp = Get-Date -AsUTC -Format "u"
$policyPlan = @{
createdOn = $timestamp
pacOwnerId = $pacOwnerId
policyDefinitions = $policyDefinitions
policySetDefinitions = $policySetDefinitions
assignments = $assignments
exemptions = $exemptions
}
$rolesPlan = @{
createdOn = $timestamp
pacOwnerId = $pacOwnerId
roleAssignments = $roleAssignments
}
if ($ExtendedReporting) {
$detailedRecordList = [ordered]@{}
$detailedRecord = [ordered]@{
name = ""
definitionType = ""
evaluationResult = ""
fileRelativePath = ""
id = ""
changes = ""
changeList = @()
identityReplaced = $false
oldIdentity = ""
newIdentity = ""
changedIdentityStrings = ""
changedMemberPolicyDefinitions = $false
changedMemberPolicyDefinitionList = @()
scopeChangedOnly = $false
oldDefinitionId = ""
newDefinitionId = ""
replacedReferencedDefinition = $false
newReferencedDefinition = ""
oldReferencedDefinition = ""
requiresRoleChanges = $false
roleAdded = @()
roleUpdated = @()
roleRemoved = @()
displayNameChanged = $false
oldDisplayName = ""
newDisplayName = ""
descriptionChanged = $false
oldDescription = ""
newDescription = ""
oldOwner = ""
newOwner = ""
metadataChanged = $false
oldMetadata = @{}
newMetadata = @{}
definitionVersionChanged = $false
oldDefinitionVersion = ""
newDefinitionVersion = ""
parametersChanged = $false
oldParameters = @{}
newParameters = @{}
enforcementModeChanged = $false
oldEnforcementMode = ""
newEnforcementMode = ""
modeChanged = $false
oldMode = ""
newMode = ""
notScopesChanged = $false
oldNotScopes = @{}
newNotScopes = @{}
nonComplianceMessagesChanged = $false
oldNonComplianceMessages = @{}
newNonComplianceMessages = @{}
overridesChanged = $false
oldOverrides = @{}
newOverrides = @{}
resourceSelectorsChanged = $false
oldResourceSelectors = @{}
newResourceSelectors = @{}
policyRuleChanged = $false
oldPolicyRule = @{}
newPolicyRule = @{}
replacedPolicy = $false
replacedPolicyList = @()
policyDefinitionsChanged = $false
oldPolicyDefinitions = @()
newPolicyDefinitions = @()
policyDefinitionGroupsChanged = $false
deletedPolicyDefinitionGroups = $false
oldPolicyDefinitionGroups = @()
newPolicyDefinitionGroups = @()
}
}
$policyDefinitionsFolder = $pacEnvironment.policyDefinitionsFolder
$policySetDefinitionsFolder = $pacEnvironment.policySetDefinitionsFolder
$policyAssignmentsFolder = $pacEnvironment.policyAssignmentsFolder
$policyExemptionsFolder = $pacEnvironment.policyExemptionsFolder
$policyExemptionsFolderForPacEnvironment = "$($policyExemptionsFolder)/$($pacEnvironment.pacSelector)"
#endregion plan data structures
#region calculate which plans need to be built
$warningMessages = [System.Collections.ArrayList]::new()
$exemptionsAreNotManagedMessage = $null
$exemptionsAreManaged = $true
if (!(Test-Path $policyExemptionsFolder -PathType Container)) {
$exemptionsAreNotManagedMessage = "Policy Exemptions folder '$policyExemptionsFolder not found. Exemptions not managed by this EPAC instance."
$exemptionsAreManaged = $false
}
elseif (!(Test-Path $policyExemptionsFolderForPacEnvironment -PathType Container)) {
$exemptionsAreNotManagedMessage = "Policy Exemptions folder '$policyExemptionsFolderForPacEnvironment' for PaC environment $($pacEnvironment.pacSelector) not found. Exemptions not managed by this EPAC instance."
$exemptionsAreManaged = $false
}
$localBuildExemptionsOnly = $BuildExemptionsOnly
# $localBuildExemptionsOnly = $true
# $VerbosePreference = "Continue"
if ($localBuildExemptionsOnly) {
$null = $warningMessages.Add("Building only the Exemptions plan. Policy, Policy Set, and Assignment plans will not be built.")
if ($exemptionsAreManaged) {
$buildSelections.buildPolicyExemptions = $true
$buildSelections.buildAny = $true
}
else {
$null = $warningMessages.Add($exemptionsAreNotManagedMessage)
$null = $warningMessages.Add("Policy Exemptions plan will not be built. Exiting...")
}
$buildSelections.buildPolicyDefinitions = $false
$buildSelections.buildPolicySetDefinitions = $false
$buildSelections.buildPolicyAssignments = $false
}
else {
if (!(Test-Path $policyDefinitionsFolder -PathType Container)) {
$null = $warningMessages.Add("Policy definitions '$policyDefinitionsFolder' folder not found. Policy definitions not managed by this EPAC instance.")
}
else {
$buildSelections.buildPolicyDefinitions = $true
$buildSelections.buildAny = $true
}
if (!(Test-Path $policySetDefinitionsFolder -PathType Container)) {
$null = $warningMessages.Add("Policy Set definitions '$policySetDefinitionsFolder' folder not found. Policy Set definitions not managed by this EPAC instance.")
}
else {
$buildSelections.buildPolicySetDefinitions = $true
$buildSelections.buildAny = $true
}
if (!(Test-Path $policyAssignmentsFolder -PathType Container)) {
$null = $warningMessages.Add("Policy Assignments '$policyAssignmentsFolder' folder not found. Policy Assignments not managed by this EPAC instance.")
}
else {
$buildSelections.buildPolicyAssignments = $true
$buildSelections.buildAny = $true
}
if ($exemptionsAreManaged) {
$buildSelections.buildPolicyExemptions = $true
$buildSelections.buildAny = $true
}
else {
$null = $warningMessages.Add($exemptionsAreNotManagedMessage)
}
if (-not $buildSelections.buildAny) {
$null = $warningMessages.Add("No Policies, Policy Set, Assignment, or Exemptions managed by this EPAC instance found. No plans will be built. Exiting...")
}
}
if ($warningMessages.Count -gt 0) {
foreach ($warningMessage in $warningMessages) {
Write-Warning $warningMessage
}
}
#endregion calculate which plans need to be built
if ($FullExportForDocumentationFile) {
# Force items that are required for documentation
$buildSelections.buildPolicyAssignments = $true
$buildSelections.buildAny = $true
}
if ($buildSelections.buildAny) {
# get the scope table for the deployment root scope amd the resources
$scopeTable = Build-ScopeTableForDeploymentRootScope -PacEnvironment $pacEnvironment
$skipExemptions = -not $buildSelections.buildPolicyExemptions
$skipRoleAssignments = -not $buildSelections.buildPolicyAssignments
$deployedPolicyResources = Get-AzPolicyResources `
-PacEnvironment $pacEnvironment `
-ScopeTable $scopeTable `
-SkipExemptions:$skipExemptions `
-SkipRoleAssignments:$skipRoleAssignments
if ($FullExportForDocumentationFile) {
# Clear values that will be used to confirm existing deployments in order to ensure that all policySets are in the list to be documented in the policyDocumentation file
$deployedPolicyResources.policyassignments.readOnly = @{}
$deployedPolicyResources.policyassignments.managed = @{}
$deployedPolicyResources.policyassignments.all = @{}
}
# Calculate roleDefinitionIds for built-in and inherited Policies
$readOnlyPolicyDefinitions = $deployedPolicyResources.policydefinitions.readOnly
foreach ($id in $readOnlyPolicyDefinitions.Keys) {
$deployedDefinitionProperties = Get-PolicyResourceProperties -PolicyResource $readOnlyPolicyDefinitions.$id
if ($deployedDefinitionProperties.policyRule.then.details -and $deployedDefinitionProperties.policyRule.then.details.roleDefinitionIds) {
$roleIds = $deployedDefinitionProperties.policyRule.then.details.roleDefinitionIds
$null = $policyRoleIds.Add($id, $roleIds)
}
}
# Populate allDefinitions.policydefinitions with all deployed definitions
$allDeployedDefinitions = $deployedPolicyResources.policydefinitions.all
foreach ($id in $allDeployedDefinitions.Keys) {
$allDefinitions.policydefinitions[$id] = $allDeployedDefinitions.$id
}
if ($buildSelections.buildPolicyDefinitions) {
# Process Policies
Build-HydrationPolicyPlan `
-DefinitionsRootFolder $policyDefinitionsFolder `
-PacEnvironment $pacEnvironment `
-DeployedDefinitions $deployedPolicyResources.policydefinitions `
-Definitions $policyDefinitions `
-AllDefinitions $allDefinitions `
-ReplaceDefinitions $replaceDefinitions `
-PolicyRoleIds $policyRoleIds `
-DetailedRecord $detailedRecord `
-ExtendedReporting:$ExtendedReporting
}
# Calculate roleDefinitionIds for built-in and inherited PolicySets
$readOnlyPolicySetDefinitions = $deployedPolicyResources.policysetdefinitions.readOnly
foreach ($id in $readOnlyPolicySetDefinitions.Keys) {
$policySetProperties = Get-PolicyResourceProperties -PolicyResource $readOnlyPolicySetDefinitions.$id
$roleIds = @{}
foreach ($policyDefinition in $policySetProperties.policyDefinitions) {
$policyId = $policyDefinition.policyDefinitionId
if ($policyRoleIds.ContainsKey($policyId)) {
$addRoleDefinitionIds = $PolicyRoleIds.$policyId
foreach ($roleDefinitionId in $addRoleDefinitionIds) {
$roleIds[$roleDefinitionId] = "added"
}
}
}
if ($roleIds.psbase.Count -gt 0) {
$null = $policyRoleIds.Add($id, $roleIds.Keys)
}
}
# Populate allDefinitions.policysetdefinitions with deployed definitions
$allDeployedDefinitions = $deployedPolicyResources.policysetdefinitions.all
foreach ($id in $allDeployedDefinitions.Keys) {
$allDefinitions.policysetdefinitions[$id] = $allDeployedDefinitions.$id
}
if ($buildSelections.buildPolicySetDefinitions) {
# Process Policy Sets
Build-HydrationPolicySetPlan `
-DefinitionsRootFolder $policySetDefinitionsFolder `
-PacEnvironment $pacEnvironment `
-DeployedDefinitions $deployedPolicyResources.policysetdefinitions `
-Definitions $policySetDefinitions `
-AllDefinitions $allDefinitions `
-ReplaceDefinitions $replaceDefinitions `
-PolicyRoleIds $policyRoleIds `
-DetailedRecord $detailedRecord `
-ExtendedReporting:$ExtendedReporting
}
# Convert Policy and PolicySetDefinition to detailed Info
$combinedPolicyDetails = Convert-PolicyResourcesToDetails `
-AllPolicyDefinitions $allDefinitions.policydefinitions `
-AllPolicySetDefinitions $allDefinitions.policysetdefinitions
# Populate allAssignments
$deployedPolicyAssignments = $deployedPolicyResources.policyassignments.managed
foreach ($id in $deployedPolicyAssignments.Keys) {
$allAssignments[$id] = $deployedPolicyAssignments.$id
}
#region Process Deprecated
$deprecatedHash = @{}
foreach ($key in $combinedPolicyDetails.policies.keys) {
if ($combinedPolicyDetails.policies.$key.isDeprecated) {
$deprecatedHash[$combinedPolicyDetails.policies.$key.name] = $combinedPolicyDetails.policies.$key
}
}
if ($buildSelections.buildPolicyAssignments) {
# Process Assignment JSON files
Build-HydrationAssignmentPlan `
-AssignmentsRootFolder $policyAssignmentsFolder `
-PacEnvironment $pacEnvironment `
-ScopeTable $scopeTable `
-DeployedPolicyResources $deployedPolicyResources `
-Assignments $assignments `
-RoleAssignments $roleAssignments `
-AllAssignments $allAssignments `
-ReplaceDefinitions $replaceDefinitions `
-PolicyRoleIds $policyRoleIds `
-CombinedPolicyDetails $combinedPolicyDetails `
-DeprecatedHash $deprecatedHash `
-DetailedRecord $detailedRecord `
-ExtendedReporting:$ExtendedReporting
}
if ($buildSelections.buildPolicyExemptions) {
# Process Exemption JSON files
if ($SkipNotScopedExemptions) {
Build-ExemptionsPlan `
-ExemptionsRootFolder $policyExemptionsFolderForPacEnvironment `
-ExemptionsAreNotManagedMessage $exemptionsAreNotManagedMessage `
-PacEnvironment $pacEnvironment `
-ScopeTable $scopeTable `
-AllDefinitions $allDefinitions `
-AllAssignments $allAssignments `
-CombinedPolicyDetails $combinedPolicyDetails `
-Assignments $assignments `
-DeployedExemptions $deployedPolicyResources.policyExemptions `
-Exemptions $exemptions `
-SkipNotScopedExemptions
# TODO: Add ExtendedReporting to Exemptions
}
else {
Build-ExemptionsPlan `
-ExemptionsRootFolder $policyExemptionsFolderForPacEnvironment `
-ExemptionsAreNotManagedMessage $exemptionsAreNotManagedMessage `
-PacEnvironment $pacEnvironment `
-ScopeTable $scopeTable `
-AllDefinitions $allDefinitions `
-AllAssignments $allAssignments `
-CombinedPolicyDetails $combinedPolicyDetails `
-Assignments $assignments `
-DeployedExemptions $deployedPolicyResources.policyExemptions `
-Exemptions $exemptions
# TODO: Add ExtendedReporting to Exemptions
}
}
Write-Information "==================================================================================================="
Write-Information "Summary"
Write-Information "==================================================================================================="
if ($buildSelections.buildPolicyDefinitions) {
Write-Information "Policy counts:"
Write-Information " $($policyDefinitions.numberUnchanged) unchanged"
if ($policyDefinitions.numberOfChanges -eq 0) {
Write-Information " $($policyDefinitions.numberOfChanges) changes"
}
else {
Write-Information " $($policyDefinitions.numberOfChanges) changes:"
Write-Information " new = $($policyDefinitions.new.psbase.Count)"
Write-Information " update = $($policyDefinitions.update.psbase.Count)"
Write-Information " replace = $($policyDefinitions.replace.psbase.Count)"
Write-Information " delete = $($policyDefinitions.delete.psbase.Count)"
}
}
if ($buildSelections.buildPolicySetDefinitions) {
Write-Information "Policy Set counts:"
Write-Information " $($policySetDefinitions.numberUnchanged) unchanged"
if ($policySetDefinitions.numberOfChanges -eq 0) {
Write-Information " $($policySetDefinitions.numberOfChanges) changes"
}
else {
Write-Information " $($policySetDefinitions.numberOfChanges) changes:"
Write-Information " new = $($policySetDefinitions.new.psbase.Count)"
Write-Information " update = $($policySetDefinitions.update.psbase.Count)"
Write-Information " replace = $($policySetDefinitions.replace.psbase.Count)"
Write-Information " delete = $($policySetDefinitions.delete.psbase.Count)"
}
}
if ($buildSelections.buildPolicyAssignments) {
Write-Information "Policy Assignment counts:"
Write-Information " $($assignments.numberUnchanged) unchanged"
if ($assignments.numberOfChanges -eq 0) {
Write-Information " $($assignments.numberOfChanges) changes"
}
else {
Write-Information " $($assignments.numberOfChanges) changes:"
Write-Information " new = $($assignments.new.psbase.Count)"
Write-Information " update = $($assignments.update.psbase.Count)"
Write-Information " replace = $($assignments.replace.psbase.Count)"
Write-Information " delete = $($assignments.delete.psbase.Count)"
}
Write-Information "Role Assignment counts:"
if ($roleAssignments.numberOfChanges -eq 0) {
Write-Information " $($roleAssignments.numberOfChanges) changes"
}
else {
Write-Information " $($roleAssignments.numberOfChanges) changes:"
Write-Information " add = $($roleAssignments.added.psbase.Count)"
Write-Information " update = $($roleAssignments.updated.psbase.Count)"
Write-Information " remove = $($roleAssignments.removed.psbase.Count)"
}
}
if ($buildSelections.buildPolicyExemptions) {
Write-Information "Policy Exemption counts:"
Write-Information " $($exemptions.numberUnchanged) unchanged"
Write-Information " $($exemptions.numberOfOrphans) orphaned"
Write-Information " $($exemptions.numberOfExpired) expired"
if ($exemptions.numberOfChanges -eq 0) {
Write-Information " $($exemptions.numberOfChanges) changes"
}
else {
Write-Information " $($exemptions.numberOfChanges) changes:"
Write-Information " new = $($exemptions.new.psbase.Count)"
Write-Information " update = $($exemptions.update.psbase.Count)"
Write-Information " replace = $($exemptions.replace.psbase.Count)"
Write-Information " delete = $($exemptions.delete.psbase.Count)"
}
}
}
Write-Information "---------------------------------------------------------------------------------------------------"
Write-Information "Output plan(s); if any, will be written to the following file(s):"
$policyResourceChanges = $policyDefinitions.numberOfChanges
$policyResourceChanges += $policySetDefinitions.numberOfChanges
$policyResourceChanges += $assignments.numberOfChanges
$policyResourceChanges += $exemptions.numberOfChanges
$policyStage = "no"
$planFile = $pacEnvironment.policyPlanOutputFile
if ($policyResourceChanges -gt 0) {
Write-Information " Policy resource deployment required; writing Policy plan file '$planFile'"
if (-not (Test-Path $planFile)) {
$null = (New-Item $planFile -Force)
}
$null = $policyPlan | ConvertTo-Json -Depth 100 | Out-File -FilePath $planFile -Force
$policyStage = "yes"
}
else {
if (Test-Path $planFile) {
$null = (Remove-Item $planFile)
}
Write-Information " Skipping Policy deployment stage/step - no changes"
}
$roleStage = "no"
$planFile = $pacEnvironment.rolesPlanOutputFile
if ($roleAssignments.numberOfChanges -gt 0) {
Write-Information " Role assignment changes required; writing Policy plan file '$planFile'"
if (-not (Test-Path $planFile)) {
$null = (New-Item $planFile -Force)
}
$null = $rolesPlan | ConvertTo-Json -Depth 100 | Out-File -FilePath $planFile -Force
$roleStage = "yes"
}
else {
if (Test-Path $planFile) {
$null = (Remove-Item $planFile)
}
Write-Information " Skipping Role Assignment stage/step - no changes"
}
if ($ExtendedReporting -and ($policyStage -eq "yes" -or $roleStage -eq "yes")) {
# Output json, csv, and md
$detailedOutJson = Join-Path $OutputFolder ( -join ('plans-', $pacEnvironment.pacSelector)) 'detailed-deployment-report.json'
$detailedOutCsv = Join-Path $OutputFolder ( -join ('plans-', $pacEnvironment.pacSelector)) 'detailed-deployment-report.csv'
Write-Information " Detailed policy resource deployment documented; writing detailed change information json file '$detailedOutJson'"
Write-Information " Detailed policy resource deployment documented; writing detailed change information csv file '$detailedOutCsv'"
$detailedRecordList | Select-Object -Unique | ConvertTo-Json -Depth 100 `
| Out-File -FilePath $detailedOutJson -Force
$detailedRecordHashtable = Get-DeepCloneAsOrderedHashtable -InputObject $detailedRecordList
Convert-HashtableToFlatPsObject -Hashtable $detailedRecordHashtable.values `
| Select-Object @{Name = "CombinedKey"; Expression = { "$($_.id)-$($_.name)-$($_.fileRelativePath)" } }, * `
| Sort-Object CombinedKey `
| Select-Object -Unique * `
| Select-Object -ExcludeProperty CombinedKey `
| Export-Csv -Path $detailedOutCsv -NoTypeInformation -Force
# Build md file
## TODO: Confirming base format to generate template for output, will be in next release of this cmdlet
}
Write-Information "---------------------------------------------------------------------------------------------------"
Write-Information ""
switch ($DevOpsType) {
ado {
Write-Host "##vso[task.setvariable variable=deployPolicyChanges;isOutput=true]$($policyStage)"
Write-Host "##vso[task.setvariable variable=deployRoleChanges;isOutput=true]$($roleStage)"
break
}
gitlab {
Add-Content "build.env" "deployPolicyChanges=$($policyStage)"
Add-Content "build.env" "deployRoleChanges=$($roleStage)"
}
default {
}
}
}