Private/Scanning/Get-SubAssignments.ps1

function Get-SubAssignments {
    <#
    .SYNOPSIS
        Get policy assignments for a specific subscription.
     
    .DESCRIPTION
        Retrieves policy assignments for a subscription, with optional filtering
        for direct assignments only (excluding inherited from Management Groups).
     
    .PARAMETER SubscriptionId
        The Subscription ID to query.
     
    .PARAMETER DirectOnly
        If specified, returns only assignments directly assigned to the subscription
        (filters out inherited assignments from parent Management Groups).
     
    .EXAMPLE
        $assignments = Get-SubAssignments -SubscriptionId "12345..." -DirectOnly
     
    .OUTPUTS
        Array of policy assignment objects
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$SubscriptionId,
        
        [switch]$DirectOnly
    )
    
    $subScope = "/subscriptions/$SubscriptionId"
    
    $subAssignments = Invoke-AzCommandWithRetry -Command {
        Get-AzPolicyAssignment -Scope $subScope -ErrorAction SilentlyContinue
    } -OperationName "PolicyAssignment"
    
    # Track API type
    if ($script:ApiCallStats) {
        $script:ApiCallStats.PolicyAssignmentCalls++
    }
    
    if (-not $DirectOnly) {
        # Add metadata properties to all assignments
        foreach ($assignment in $subAssignments) {
            $isIndividualPolicy = $assignment.PolicyDefinitionId -like "*/policyDefinitions/*" -and 
                                  $assignment.PolicyDefinitionId -notlike "*/policySetDefinitions/*"
            
            $assignment | Add-Member -MemberType NoteProperty -Name 'IsPolicySet' -Value (-not $isIndividualPolicy) -Force
            $assignment | Add-Member -MemberType NoteProperty -Name 'IsIndividualPolicy' -Value $isIndividualPolicy -Force
        }
        
        return $subAssignments
    }
    
    # Filter for direct assignments only
    $directAssignments = @()
    $individualPolicyCount = 0
    
    foreach ($assignment in $subAssignments) {
        $assignmentScope = if ($assignment.Properties.Scope) { 
            $assignment.Properties.Scope 
        } else { 
            $assignment.Scope 
        }
        
        # Check if directly assigned to this subscription
        $isDirect = ($assignmentScope -eq $subScope)
        
        # Check if individual policy (not initiative)
        $isIndividualPolicy = $assignment.PolicyDefinitionId -like "*/policyDefinitions/*" -and 
                              $assignment.PolicyDefinitionId -notlike "*/policySetDefinitions/*"
        
        # Include if direct OR individual policy (individual policies are always relevant)
        if ($isDirect -or $isIndividualPolicy) {
            # Add metadata properties
            $assignment | Add-Member -MemberType NoteProperty -Name 'IsPolicySet' -Value (-not $isIndividualPolicy) -Force
            $assignment | Add-Member -MemberType NoteProperty -Name 'IsIndividualPolicy' -Value $isIndividualPolicy -Force
            
            $directAssignments += $assignment
            if ($isIndividualPolicy) {
                $individualPolicyCount++
            }
        }
    }
    
    return $directAssignments
}