Scripts/Helpers/Merge-AssignmentParametersEx.ps1 (212 lines of code) (raw):
function Merge-AssignmentParametersEx {
param(
$NodeName,
$PolicySetId,
[hashtable] $BaseAssignment,
[hashtable] $ParameterInstructions,
[hashtable] $FlatPolicyList,
[hashtable] $CombinedPolicyDetails,
[hashtable] $EffectProcessedForPolicy
)
$csvParameterArray = $ParameterInstructions.csvParameterArray
$effectColumn = $ParameterInstructions.effectColumn
$parametersColumn = $ParameterInstructions.parametersColumn
$nonComplianceMessageColumn = $ParameterInstructions.nonComplianceMessageColumn
#region parameters column
$parameters = Get-DeepCloneAsOrderedHashtable $BaseAssignment.parameters
foreach ($row in $csvParameterArray) {
if ($row.flatPolicyEntryKey) {
$parametersColumnCell = $row[$parametersColumn]
if ($null -ne $parametersColumnCell -and $parametersColumnCell -ne "") {
$addedParameters = ConvertFrom-Json $parametersColumnCell -Depth 100 -AsHashTable
if ($null -ne $addedParameters) {
foreach ($parameterName in $addedParameters.Keys) {
if (!$parameters.ContainsKey($parameterName)) {
$parameterValue = $addedParameters.$parameterName
$parameters[$parameterName] = $parameterValue
}
}
}
}
}
}
#endregion parameters column
#region effects column = mutual exclusion handled
$overridesByEffect = @{}
$nonComplianceMessages = $BaseAssignment.nonComplianceMessages
$hasErrors = $false
$rowNumber = 1
foreach ($row in $csvParameterArray) {
++$rowNumber
$flatPolicyEntryKey = $row.flatPolicyEntryKey
if ($flatPolicyEntryKey) {
$name = $row.name
$flatPolicyEntry = $FlatPolicyList.$flatPolicyEntryKey
if ($null -eq $name -or $name -eq "" -or $null -eq $flatPolicyEntry -or $null -eq $flatPolicyEntry.policySetList -or $null -eq $row.policyId) {
continue
}
$policySetList = $flatPolicyEntry.policySetList
if ($policySetList.ContainsKey($PolicySetId)) {
# Policy in this for loop iteration is referenced in the Policy Set currently being processed
$requestedEffect = $row[$effectColumn]
$planedEffect = $requestedEffect
$isProcessed = $EffectProcessedForPolicy.ContainsKey($flatPolicyEntryKey)
$perPolicySet = $policySetList.$PolicySetId
$effectParameterName = $perPolicySet.effectParameterName
$effectAllowedValues = $perPolicySet.effectAllowedValues
$effectAllowedOverrides = $perPolicySet.effectAllowedOverrides
$effectDefault = $perPolicySet.effectDefault
$policyDefinitionReferenceId = $perPolicySet.policyDefinitionReferenceId
if ($isProcessed) {
#region the second and subsequent time this Policy is processed, the effect must be adjusted to audit
$planedEffect = switch ($requestedEffect) {
"Append" {
"Audit"
break
}
"Modify" {
"Audit"
break
}
"Deny" {
"Audit"
break
}
"DeployIfNotExists" {
"AuditIfNotExists"
break
}
"DenyAction" {
"Disabled"
break
}
default {
$_
}
}
#endregion the second and subsequent time this Policy is processed, the effect must be adjusted to audit
}
else {
$EffectProcessedForPolicy[$flatPolicyEntryKey] = $true
}
if ($planedEffect -ne $effectDefault) {
#region effect parameters including overrides
$useOverrides = $false
$confirmedEffect = $null
if ($perPolicySet.isEffectParameterized) {
# test parameter
$useOverrides = $false
$confirmedEffect = Confirm-EffectIsAllowed -Effect $planedEffect -AllowedEffects $effectAllowedValues
if ($null -eq $confirmedEffect) {
# fallback 1: test override
$useOverrides = $true
$confirmedEffect = Confirm-EffectIsAllowed -Effect $planedEffect -AllowedEffects $effectAllowedOverrides
if ($null -eq $confirmedEffect -and $requestedEffect -ne $planedEffect) {
# fallback 2: if this is the second processed Policy Set, try parameter with original requested effect
$useOverrides = $false
$confirmedEffect = Confirm-EffectIsAllowed -Effect $requestedEffect -AllowedEffects $effectAllowedValues
if ($null -eq $confirmedEffect) {
# fallback 3: try overrides with the original requested effect
$useOverrides = $true
$confirmedEffect = Confirm-EffectIsAllowed -Effect $requestedEffect -AllowedEffects $effectAllowedOverrides
}
}
}
}
else {
# the effect is not parameterized
$useOverrides = $true
$confirmedEffect = Confirm-EffectIsAllowed -Effect $planedEffect -AllowedEffects $effectAllowedOverrides
if ($null -eq $confirmedEffect) {
# fallback: try overrides with the original requested effect
$confirmedEffect = Confirm-EffectIsAllowed -Effect $requestedEffect -AllowedEffects $effectAllowedOverrides
}
}
if ($null -eq $confirmedEffect) {
# the effect is not an allowed value
Write-Error " Node $($NodeName): CSV parameterFile '$parameterFileName' row $rowNumber for Policy name '$name': the effect ($effect) must be an allowed value." -ErrorAction Continue
$hasErrors = $true
continue
}
elseif ($confirmedEffect -ne $effectDefault) {
if ($useOverrides) {
# collate the overrides by effect
$byEffectList = $null
if ($overridesByEffect.ContainsKey($confirmedEffect)) {
$byEffectList = $overridesByEffect[$confirmedEffect]
}
else {
$byEffectList = [System.Collections.ArrayList]::new()
$overridesByEffect[$confirmedEffect] = $byEffectList
}
$null = $byEffectList.Add($policyDefinitionReferenceId)
}
else {
# set the effect parameter
$parameters[$effectParameterName] = $confirmedEffect
}
}
#endregion effect parameters including overrides
}
#region nonComplianceMessages
if ($null -ne $nonComplianceMessageColumn) {
$nonComplianceMessage = $row[$nonComplianceMessageColumn]
if ($nonComplianceMessage -ne "") {
$null = $nonComplianceMessages.Add(
@{
message = $nonComplianceMessage
policyDefinitionReferenceId = $policyDefinitionReferenceId
}
)
}
}
#endregion nonComplianceMessages
}
}
}
#endregion effects column = mutual exclusion handled
#region optimize overrides
$effectsCount = $overridesByEffect.psbase.Count
if ($effectsCount -gt 0) {
$finalOverrides = [System.Collections.ArrayList]::new()
foreach ($effectValue in $overridesByEffect.Keys) {
[System.Collections.ArrayList] $policyDefinitionReferenceIds = $overridesByEffect[$effectValue]
$idsCount = $policyDefinitionReferenceIds.Count
$startIndex = 0
while ($idsCount -gt 0) {
$ids = $null
if ($idsCount -gt 50) {
# each override can have up to 50 selectors
$ids = ($policyDefinitionReferenceIds.GetRange($startIndex, 50)).ToArray()
$startIndex += 50
$idsCount -= 50
}
else {
$ids = ($policyDefinitionReferenceIds.GetRange($startIndex, $idsCount)).ToArray()
$idsCount = 0
}
$override = @{
kind = "policyEffect"
value = $effectValue
selectors = @(
@{
kind = "policyDefinitionReferenceId"
in = $ids
}
)
}
$null = $finalOverrides.Add($override)
}
}
if ($finalOverrides.Count -gt 10) {
Write-Error " Node $($NodeName): CSV parameterFile '$parameterFileName' causes too many overrides ($($finalOverrides.Count)) for Policies without parameterized effect." -ErrorAction Continue
$hasErrors = $true
}
else {
$BaseAssignment.overrides = $finalOverrides.ToArray()
}
}
#endregion optimize overrides
$BaseAssignment.parameters = $parameters
return $hasErrors
}