internal/functions/Merge-AssignmentParametersEx.ps1

#Requires -PSEdition Core

function Merge-AssignmentParametersEx {
    # Recursive Function
    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-DeepClone $baseAssignment.parameters -AsHashTable
    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 -and $addedParameters.Count -gt 0) {
                    foreach ($parameterName in $addedParameters.Keys) {
                        $rawParameterValue = $addedParameters.$parameterName
                        $parameterValue = Get-DeepClone $rawParameterValue -AsHashTable
                        $parameters[$parameterName] = $parameterValue
                    }
                }
            }
        }
    }

    #endregion parameters column

    #region parameters column = mutual exclusion handled

    $overridesByEffect = @{}
    $nonComplianceMessages = [System.Collections.ArrayList]::new()
    $hasErrors = $false
    foreach ($row in $csvParameterArray) {
        $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

                #region effect parameters including overrides
                $perPolicySet = $policySetList.$policySetId
                $effectParameterName = $perPolicySet.effectParameterName
                $effect = $row[$effectColumn]
                $setEffectAllowedValues = $perPolicySet.effectAllowedValues
                $effectAllowedOverrides = $flatPolicyEntry.effectAllowedOverrides
                $effectDefault = $perPolicySet.effectDefault
                $desiredEffect = $effect.ToLower()
                $useOverrides = $false
                $policyDefinitionReferenceId = $perPolicySet.policyDefinitionReferenceId
                if ($perPolicySet.isEffectParameterized) {
                    if ($setEffectAllowedValues -notcontains $desiredEffect) {
                        if ($effectAllowedOverrides -notcontains $desiredEffect) {
                            Write-Error " Node $($nodeName): CSV parameterFile '$parameterFileName' row for Policy name '$name': the effect ($effect) must be an allowed parameter or override value [$($effectAllowedOverrides -join ',')]."
                            $hasErrors = $true
                            continue
                        }
                        else {
                            $useOverrides = $true
                        }
                    }
                }
                else {
                    if ($effectAllowedOverrides -notcontains $desiredEffect) {
                        Write-Error " Node $($nodeName): CSV parameterFile '$parameterFileName' row for Policy name '$name': the effect ($effect) must be an allowed override value [$($effectAllowedOverrides -join ',')]."
                        $hasErrors = $true
                        continue
                    }
                    $useOverrides = $true
                }
                $isProcessed = $effectProcessedForPolicy.ContainsKey($flatPolicyEntryKey)
                if ($isProcessed) {
                    if ($desiredEffect -eq $effectProcessedForPolicy.$flatPolicyEntryKey) {
                        # Adjust desiredEffect
                        $modifiedEffect = switch ($desiredEffect) {
                            append {
                                "audit" 
                            }
                            modify {
                                "audit" 
                            }
                            deny {
                                "audit" 
                            }
                            deployIfNotExists {
                                "auditIfNotExists" 
                            }
                            manual {
                                "manual" 
                            }
                            Default {
                                $_ 
                            }
                        }
                        if ($setEffectAllowedValues -contains $modifiedEffect) {
                            $desiredEffect = $modifiedEffect
                        }
                        elseif ($effectAllowedOverrides -contains $modifiedEffect) {
                            $useOverrides = $true
                            $desiredEffect = $modifiedEffect
                        }
                        elseif (@("audit", "auditIfNotExists") -contains $modifiedEffect) {
                            $desiredEffect = "disabled"
                            if ($setEffectAllowedValues -contains "disabled") {
                                $useOverrides = $false
                            }
                            elseif ($effectAllowedOverrides -contains "disabled") {
                                $useOverrides = $true
                            }
                        }
                    }
                }
                else {
                    $null = $effectProcessedForPolicy.Add($flatPolicyEntryKey, $desiredEffect)
                }

                $wrongCase = !($setEffectAllowedValues -ccontains $desiredEffect -or $effectAllowedOverrides -ccontains $desiredEffect)
                if ($wrongCase) {
                    $modifiedEffect = switch ($desiredEffect) {
                        append {
                            "Append" 
                        }
                        audit {
                            "Audit" 
                        }
                        auditIfNotExists {
                            "AuditIfNotExists" 
                        }
                        deny {
                            "Deny" 
                        }
                        deployIfNotExists {
                            "DeployIfNotExists" 
                        }
                        disabled {
                            "Disabled" 
                        }
                        manual {
                            "Manual" 
                        }
                        modify {
                            "Modify" 
                        }
                    }
                    if ($setEffectAllowedValues -ccontains $modifiedEffect -or $effectAllowedOverrides -ccontains $modifiedEffect) {
                        $desiredEffect = $modifiedEffect
                    }
                    else {
                        Write-Error " Node $($nodeName): CSV parameterFile '$parameterFileName' row for Policy name '$name': the effect ($desiredEffect) must be an allowed value [$($setEffectAllowedValues -join ',')]."
                        $hasErrors = $true
                        continue
                    }
                }
                if ($desiredEffect -ne $effectDefault) {
                    if ($useOverrides) {
                        $byEffectList = $null
                        if ($overridesByEffect.ContainsKey($desiredEffect)) {
                            $byEffectList = $overridesByEffect[$desiredEffect]
                        }
                        else {
                            $byEffectList = [System.Collections.ArrayList]::new()
                            $overridesByEffect[$desiredEffect] = $byEffectList
                        }
                        $null = $byEffectList.Add($policyDefinitionReferenceId)
                    }
                    else {
                        $parameters[$effectParameterName] = $desiredEffect
                    }
                }
                #endregion effect parameters including overrides and nonComplianceMessages

                #region nonComplianceMessages
                if ($null -ne $nonComplianceMessageColumn) {
                    $nonComplianceMessage = $row[$nonComplianceMessageColumn]
                    if ($nonComplianceMessage -ne "") {
                        $null = $nonComplianceMessages.Add(
                            @{
                                message                     = $nonComplianceMessage
                                policyDefinitionReferenceId = $policyDefinitionReferenceId
                            }
                        )
                    }
                }
                #endregion nonComplianceMessages
            }
        }
    }

    #endregion parameters column = mutual exclusion handled

    #region optimize overrides

    $effectsCount = $overridesByEffect.Count
    if ($effectsCount -gt 0) {
        $finalOverrides = [System.Collections.ArrayList]::new()
        foreach ($effectValue in $overridesByEffect.Keys) {
            [System.Collections.ArrayList] $policyDefinitionReferenceIds = $overridesByEffect[$effectValue]
            $idsCount = $policyDefinitionReferenceIds.Count
            while ($idsCount -gt 0) {
                $ids = $null
                if ($idsCount -gt 50) {
                    $ids = $policyDefinitionReferenceIds.GetRange(0, 50)
                    $policyDefinitionReferenceIds.RemoveRange(0, 50)
                    $idsCount -= 50
                }
                else {
                    $ids = $policyDefinitionReferenceIds
                    $idsCount = 0
                }
                $override = @{
                    kind      = "policyEffect"
                    value     = $effectValue
                    selectors = @(
                        @{
                            kind = "policyDefinitionReferenceId"
                            in   = $ids.ToArray()
                        }
                    )
                }
                $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
    if ($nonComplianceMessages.Count -gt 0) {
        $baseAssignment.nonComplianceMessages = $nonComplianceMessages.ToArray()
    }

    return $hasErrors
}