internal/functions/Get-AzPolicyResources.ps1

function Get-AzPolicyResources {
    [CmdletBinding()]
    param (
        [hashtable] $PacEnvironment,
        [hashtable] $ScopeTable,

        [switch] $SkipRoleAssignments,
        [switch] $SkipExemptions,
        [switch] $CollectAllPolicies,
        [switch] $NoParallelProcessing
    )

    $deploymentRootScope = $PacEnvironment.deploymentRootScope
    Write-Information ""
    Write-Information "==================================================================================================="
    Write-Information "Get Policy Resources for EPAC environment '$($PacEnvironment.pacSelector)' at root scope $($deploymentRootScope -replace '/providers/Microsoft.Management','')"
    Write-Information "==================================================================================================="

    $skipExemptionsLocal = $SkipExemptions.IsPresent
    $skipRoleAssignmentsLocal = $SkipRoleAssignments.IsPresent
    $collectAllPoliciesLocal = $CollectAllPolicies.IsPresent

    $deployedPolicyResources = @{
        policydefinitions            = @{
            all      = @{}
            readOnly = @{}
            managed  = @{}
            counters = @{
                builtIn         = 0
                inherited       = 0
                managedBy       = @{
                    thisPaC  = 0
                    otherPaC = 0
                    unknown  = 0
                }
                excluded        = 0
                unmanagedScopes = 0
            }
        }
        policysetdefinitions         = @{
            all      = @{}
            readOnly = @{}
            managed  = @{}
            counters = @{
                builtIn         = 0
                inherited       = 0
                managedBy       = @{
                    thisPaC  = 0
                    otherPaC = 0
                    unknown  = 0
                }
                excluded        = 0
                unmanagedScopes = 0
            }
        }
        policyassignments            = @{
            managed  = @{}
            counters = @{
                managedBy       = @{
                    thisPaC             = 0
                    otherPaC            = 0
                    dfcSecurityPolicies = 0
                    dfcDefenderPlans    = 0
                    unknown             = 0
                }
                excluded        = 0
                unmanagedScopes = 0
                withIdentity    = 0
            }
        }
        policyexemptions             = @{
            managed  = @{}
            counters = @{
                managedBy       = @{
                    thisPaC  = 0
                    otherPaC = 0
                    unknown  = 0
                }
                orphaned        = 0
                expired         = 0
                excluded        = 0
                unmanagedScopes = 0
            }
        }
        roleAssignmentsByPrincipalId = @{}
        numberOfRoleAssignments      = 0
        numberOfPrincipleIds         = 0
        remoteAssignmentsCount       = 0
        roleDefinitions              = @{}
        roleAssignmentsNotRetrieved  = $false
        excludedScopes               = $excludedScopes
    }

    $collectionList = [System.Collections.ArrayList]::new()
    if ($skipExemptionsLocal) {
        $collectionList.AddRange(@( `
                    "policyDefinitions", `
                    "policySetDefinitions", `
                    "policyAssignments"))
    }
    else {
        $collectionList.AddRange(@( `
                    "policyDefinitions", `
                    "policySetDefinitions", `
                    "policyAssignments", `
                    "policyExemptions"))
    }

    $deployedPolicyDefinitions = $deployedPolicyResources.policydefinitions
    $deployedPolicySetDefinitions = $deployedPolicyResources.policysetdefinitions
    if ($NoParallelProcessing) {
        foreach ($collectionItem in $collectionList) {
            switch ($collectionItem) {
                policyDefinitions {
                    Get-AzPolicyOrSetDefinitions `
                        -DefinitionType "policyDefinitions" `
                        -PolicyResourcesTable $deployedPolicyResources.policydefinitions `
                        -PacEnvironment $PacEnvironment `
                        -ScopeTable $ScopeTable `
                        -CollectAllPolicies $collectAllPoliciesLocal
                    break
                }
                policySetDefinitions {
                    Get-AzPolicyOrSetDefinitions `
                        -DefinitionType "policySetDefinitions" `
                        -PolicyResourcesTable $deployedPolicyResources.policysetdefinitions `
                        -PacEnvironment $PacEnvironment `
                        -ScopeTable $ScopeTable `
                        -CollectAllPolicies $collectAllPoliciesLocal
                    break
                }
                policyAssignments {
                    Get-AzPolicyAssignments `
                        -DeployedPolicyResources $deployedPolicyResources `
                        -PacEnvironment $PacEnvironment `
                        -ScopeTable $ScopeTable `
                        -SkipRoleAssignments $skipRoleAssignmentsLocal
                    break
                }
                policyExemptions {
                    Get-AzPolicyExemptions `
                        -DeployedPolicyResources $deployedPolicyResources `
                        -PacEnvironment $PacEnvironment `
                        -ScopeTable $ScopeTable
                    break
                }
            }
        }
    }
    else {
        $funcGetAzPolicyAssignments = ${function:Get-AzPolicyAssignments}.ToString()
        $funcGetAzRoleAssignmentsRestMethod = ${function:Get-AzRoleAssignmentsRestMethod}.ToString()
        $funcGetAzRoleDefinitionsRestMethod = ${function:Get-AzRoleDefinitionsRestMethod}.ToString()
        $funcGetAzPolicyExemptions = ${function:Get-AzPolicyExemptions}.ToString()
        $funcGetAzPolicyExemptionsRestMethod = ${function:Get-AzPolicyExemptionsRestMethod}.ToString()
        $funcGetAzPolicyOrSetDefinitions = ${function:Get-AzPolicyOrSetDefinitions}.ToString()
        $funcSearchAzGraphAllItems = ${function:Search-AzGraphAllItems}.ToString()
        $funcDefGetClonedObject = ${function:Get-ClonedObject}.ToString()
        $funcDefConfirmPolicyResourceExclusions = ${function:Confirm-PolicyResourceExclusions}.ToString()
        $funcConfirmPacOwner = ${function:Confirm-PacOwner}.ToString()
        $funcGetPolicyResourceProperties = ${function:Get-PolicyResourceProperties}.ToString()
        $funcSplitAzPolicyResourceId = ${function:Split-AzPolicyResourceId}.ToString()
        $funcConvertToHashTable = ${function:ConvertTo-HashTable}.ToString()
        $funcSetUniqueRoleAssignmentScopes = ${function:Set-UniqueRoleAssignmentScopes}.ToString()

        $collectionList | Foreach-Object -Parallel {
            # Import dot sourced functions into context
            if ($null -eq ${function:Get-AzPolicyAssignments}) {
                ${function:Get-AzPolicyAssignments} = $using:funcGetAzPolicyAssignments
                ${function:Get-AzRoleAssignmentsRestMethod} = $using:funcGetAzRoleAssignmentsRestMethod
                ${function:Get-AzRoleDefinitionsRestMethod} = $using:funcGetAzRoleDefinitionsRestMethod
                ${function:Get-AzPolicyExemptions} = $using:funcGetAzPolicyExemptions
                ${function:Get-AzPolicyExemptionsRestMethod} = $using:funcGetAzPolicyExemptionsRestMethod
                ${function:Get-AzPolicyOrSetDefinitions} = $using:funcGetAzPolicyOrSetDefinitions
                ${function:Search-AzGraphAllItems} = $using:funcSearchAzGraphAllItems
                ${function:Get-ClonedObject} = $using:funcDefGetClonedObject
                ${function:Confirm-PolicyResourceExclusions} = $using:funcDefConfirmPolicyResourceExclusions
                ${function:Confirm-PacOwner} = $using:funcConfirmPacOwner
                ${function:Get-PolicyResourceProperties} = $using:funcGetPolicyResourceProperties
                ${function:Split-AzPolicyResourceId} = $using:funcSplitAzPolicyResourceId
                ${function:ConvertTo-HashTable} = $using:funcConvertToHashTable
                ${function:Set-UniqueRoleAssignmentScopes} = $using:funcSetUniqueRoleAssignmentScopes
            }

            #Action that will run in Parallel. Reference the current object via $PSItem and bring in outside variables with $USING:varname
            $PacEnvironment = $using:PacEnvironment
            $ScopeTable = $using:ScopeTable
            $skipRoleAssignmentsLocal = $using:skipRoleAssignmentsLocal
            $deployedPolicyResources = $using:deployedPolicyResources
            $deployedPolicyDefinitions = $using:deployedPolicyDefinitions
            $deployedPolicySetDefinitions = $using:deployedPolicySetDefinitions
            $collectAllPoliciesLocal = $using:collectAllPoliciesLocal

            switch ($_) {
                policyDefinitions {
                    Get-AzPolicyOrSetDefinitions `
                        -DefinitionType "policyDefinitions" `
                        -PolicyResourcesTable $deployedPolicyDefinitions `
                        -PacEnvironment $PacEnvironment `
                        -ScopeTable $ScopeTable `
                        -CollectAllPolicies $collectAllPoliciesLocal
                    break
                }
                policySetDefinitions {
                    Get-AzPolicyOrSetDefinitions `
                        -DefinitionType "policySetDefinitions" `
                        -PolicyResourcesTable $deployedPolicySetDefinitions `
                        -PacEnvironment $PacEnvironment `
                        -ScopeTable $ScopeTable `
                        -CollectAllPolicies $collectAllPoliciesLocal
                    break
                }
                policyAssignments {
                    Get-AzPolicyAssignments `
                        -DeployedPolicyResources $deployedPolicyResources `
                        -PacEnvironment $PacEnvironment `
                        -ScopeTable $ScopeTable `
                        -SkipRoleAssignments $skipRoleAssignmentsLocal
                    break
                }
                policyExemptions {
                    Get-AzPolicyExemptions `
                        -DeployedPolicyResources $deployedPolicyResources `
                        -PacEnvironment $PacEnvironment `
                        -ScopeTable $ScopeTable
                    break
                }
            }
        }
    }

    Write-Information "Processing Exemptions for orphaned assignments"
    if (-not $skipExemptionsLocal) {
        $policyExemptionsCounters = $deployedPolicyResources.policyexemptions.counters
        $managedPolicyExemptionsTable = $deployedPolicyResources.policyexemptions.managed
        $managedPolicyAssignmentsTable = $deployedPolicyResources.policyassignments.managed
        # change exemption status if exemption orphaned (policyAssignment with policyAssignmentId does not exist)
        foreach ($policyResource in $managedPolicyExemptionsTable.Values) {
            if ($policyResource.assignmentScopeValidation -eq "Default") {
                $assignmentId = $policyResource.policyAssignmentId
                $assignmentScopeValidation = $policyResource.assignmentScopeValidation
                if ($assignmentScopeValidation -eq "Default") {
                    if (-not $managedPolicyAssignmentsTable.ContainsKey($assignmentId)) {
                        $policyResource.status = "orphaned-assignment"
                        $policyExemptionsCounters.orphaned += 1
                        continue
                    }
                }
            }
        }
    }

    Write-Information ""
    Write-Information "==================================================================================================="
    Write-Information "Policy Resources found for EPAC environment '$($PacEnvironment.pacSelector)' at root scope $($deploymentRootScope -replace '/providers/Microsoft.Management', '')"
    Write-Information "==================================================================================================="

    foreach ($kind in @("policydefinitions", "policysetdefinitions")) {
        $deployedPolicyTable = $deployedPolicyResources.$kind
        $counters = $deployedPolicyTable.counters
        $managedBy = $counters.managedBy
        $managedByAny = $managedBy.thisPaC + $managedBy.otherPaC + $managedBy.unknown
        Write-Information ""
        if ($kind -eq "policydefinitions") {
            Write-Information "Policy counts:"
        }
        else {
            Write-Information "Policy Set counts:"
        }
        Write-Information " BuiltIn = $($counters.builtIn)"
        Write-Information " Managed ($($managedByAny)) by:"
        Write-Information " This PaC = $($managedBy.thisPaC)"
        Write-Information " Other PaC = $($managedBy.otherPaC)"
        Write-Information " Unknown = $($managedBy.unknown)"
        Write-Information " Inherited = $($counters.inherited)"
        Write-Information " Excluded = $($counters.excluded)"
        Write-Verbose " Not our scopes = $($counters.unmanagedScopes)"
    }

    $counters = $deployedPolicyResources.policyassignments.counters
    $managedBy = $counters.managedBy
    $managedByAny = $managedBy.thisPaC + $managedBy.otherPaC + $managedBy.unknown + $managedBy.dfcSecurityPolicies + $managedBy.dfcDefenderPlans
    Write-Information ""
    Write-Information "Policy Assignment counts:"
    Write-Information " Managed ($($managedByAny)) by:"
    Write-Information " This PaC = $($managedBy.thisPaC)"
    Write-Information " Other PaC = $($managedBy.otherPaC)"
    Write-Information " Unknown = $($managedBy.unknown)"
    Write-Information " DfC Security Policies = $($managedBy.dfcSecurityPolicies)"
    Write-Information " DfC Defender Plans = $($managedBy.dfcDefenderPlans)"
    Write-Information " With identity = $($counters.withIdentity)"
    Write-Information " Excluded = $($counters.excluded)"
    Write-Verbose " Not our scopes = $($counters.unmanagedScopes)"

    if (!$skipExemptionsLocal) {
        $counters = $deployedPolicyResources.policyexemptions.counters
        $managedBy = $counters.managedBy
        $managedByAny = $managedBy.thisPaC + $managedBy.otherPaC + $managedBy.unknown
        Write-Information ""
        Write-Information "Policy Exemptions:"
        Write-Information " Managed ($($managedByAny)) by:"
        Write-Information " This PaC = $($managedBy.thisPaC)"
        Write-Information " Other PaC = $($managedBy.otherPaC)"
        Write-Information " Unknown = $($managedBy.unknown)"
        Write-Information " Orphaned = $($counters.orphaned)"
        Write-Information " Expired = $($counters.expired)"
        Write-Information " Excluded = $($counters.excluded)"
    }

    if (!$SkipRoleAssignments) {
        $managedRoleAssignmentsByPrincipalId = $deployedPolicyResources.roleAssignmentsByPrincipalId
        Write-Information ""
        $numberPrincipalIds = $deployedPolicyResources.numberOfPrincipleIds
        $numberPrincipalIdsWithRoleAssignments = $managedRoleAssignmentsByPrincipalId.Count
        if ($numberPrincipalIds -ne $numberPrincipalIdsWithRoleAssignments) {
            Write-Warning "Role assignment not retrieved for every principal Id ($($numberPrincipalIds) in assignments, $($numberPrincipalIdsWithRoleAssignments) retrieved).`n This is likely due to a missing permission for the SPN running the pipeline. Please read the pipeline documentation in EPAC.`n In rare cases, this can happen when a previous role assignment failed." -WarningAction Continue
            $deployedPolicyResources.roleAssignmentsNotRetrieved = $numberPrincipalIdsWithRoleAssignments -eq 0
        }
        Write-Information "Role Assignments:"
        Write-Information " Principal Ids = $($numberPrincipalIds)"
        Write-Information " With Role Assignments = $($numberPrincipalIdsWithRoleAssignments)"
        Write-Information " Role Assignments = $($deployedPolicyResources.numberOfRoleAssignments)"
        if ($PacEnvironment.managingTenantId) {
            Write-Information " Remote Role Assignments = $($deployedPolicyResources.remoteAssignmentsCount)"
        }
    }
    Write-Information ""

    return $deployedPolicyResources
}