functions/Get-XdrCloudAppsPolicy.ps1

function Get-XdrCloudAppsPolicy {
    <#
    .SYNOPSIS
        Retrieves policies from Microsoft Defender for Cloud Apps.

    .DESCRIPTION
        The Get-XdrCloudAppsPolicy cmdlet retrieves policies from Microsoft Defender for Cloud Apps.
        Policies help control and govern cloud application usage and data protection within your
        organization. You can filter, sort, and paginate the results using the available parameters.

        Use -Type to retrieve specific policy types (ConditionalAccess, File, InformationProtection,
        OAuth, ShadowIT, Template, ThreatDetection). Use -Metadata with -Type to get filter and field
        definitions for that policy type. Use -Setting to retrieve policy settings.

        For File and OAuth policies, additional options are available:
        - Use -PolicyId with -Type to retrieve a specific policy by ID
        - Use -Action with -Type File or -Type OAuth to get available actions
        - Use -PolicyLimit with -Type File to get file policy limits

    .PARAMETER Type
        The type of policies to retrieve. Valid values are:
        - ConditionalAccess: Policies controlling access based on conditions
        - File: File policies for data protection
        - InformationProtection: Policies protecting sensitive data
        - OAuth: OAuth app policies for third-party app governance
        - ShadowIT: Policies detecting unsanctioned cloud app usage
        - Template: Pre-configured policy templates
        - ThreatDetection: Policies identifying security threats

    .PARAMETER PolicyId
        The unique identifier of a specific policy to retrieve.
        If -Type is specified, retrieves from that policy type's endpoint.
        If -Type is not specified, attempts to discover the policy type automatically.

    .PARAMETER Metadata
        When specified with -Type, retrieves metadata including available filters, fields,
        and configuration options instead of the policies themselves.

    .PARAMETER Action
        When specified with -Type File or -Type OAuth, retrieves available actions
        that can be configured for those policy types.

    .PARAMETER PolicyLimit
        When specified with -Type File, retrieves file policy limits and constraints.

    .PARAMETER Setting
        When specified, retrieves policy settings configuration.

    .PARAMETER Limit
        The maximum number of policies to return. Default is 20.

    .PARAMETER Skip
        The number of policies to skip for pagination. Default is 0.

    .PARAMETER SortField
        The field to sort results by. Default is "severity".

    .PARAMETER SortDirection
        The sort direction. Valid values are "asc" or "desc". Default is "desc".

    .PARAMETER Filters
        A hashtable of filters to apply to the policies query.

    .PARAMETER Force
        Bypasses the cache and retrieves fresh data from the API.

    .EXAMPLE
        Get-XdrCloudAppsPolicy

        Retrieves all policies sorted by severity (highest first).

    .EXAMPLE
        Get-XdrCloudAppsPolicy -Type ConditionalAccess

        Retrieves conditional access policies.

    .EXAMPLE
        Get-XdrCloudAppsPolicy -Type File

        Retrieves file policies.

    .EXAMPLE
        Get-XdrCloudAppsPolicy -Type OAuth

        Retrieves OAuth app policies.

    .EXAMPLE
        Get-XdrCloudAppsPolicy -Type File -PolicyId "abc123"

        Retrieves a specific file policy by ID.

    .EXAMPLE
        Get-XdrCloudAppsPolicy -PolicyId "abc123"

        Retrieves a policy by ID, automatically discovering the policy type.

    .EXAMPLE
        Get-XdrCloudAppsPolicy -Type ThreatDetection -Metadata

        Retrieves metadata for threat detection policies including available filters.

    .EXAMPLE
        Get-XdrCloudAppsPolicy -Type OAuth -Metadata

        Retrieves metadata for OAuth policies including available filters.

    .EXAMPLE
        Get-XdrCloudAppsPolicy -Type File -Action

        Retrieves available actions for file policies.

    .EXAMPLE
        Get-XdrCloudAppsPolicy -Type OAuth -Action

        Retrieves available actions for OAuth policies.

    .EXAMPLE
        Get-XdrCloudAppsPolicy -Type File -PolicyLimit

        Retrieves file policy limits and constraints.

    .EXAMPLE
        Get-XdrCloudAppsPolicy -Type ShadowIT -Limit 50 -SortField "name" -SortDirection "asc"

        Retrieves 50 Shadow IT policies sorted by name ascending.

    .EXAMPLE
        Get-XdrCloudAppsPolicy -Type Template

        Retrieves policy templates.

    .EXAMPLE
        $filters = @{ "enabled" = @{ "eq" = @($true) } }
        Get-XdrCloudAppsPolicy -Type InformationProtection -Filters $filters

        Retrieves enabled information protection policies.

    .EXAMPLE
        Get-XdrCloudAppsPolicy -Setting

        Retrieves policy settings configuration.

    .EXAMPLE
        Get-XdrCloudAppsPolicy -Force

        Forces a fresh retrieval of all policies, bypassing the cache.

    .NOTES
        Requires an active XDR session established via Connect-XdrByEstsCookie.
    #>

    [OutputType([PSCustomObject])]
    [CmdletBinding(DefaultParameterSetName = 'Default')]
    param(
        [Parameter(ParameterSetName = 'Typed', Mandatory = $true)]
        [Parameter(ParameterSetName = 'TypedMetadata', Mandatory = $true)]
        [Parameter(ParameterSetName = 'TypedAction', Mandatory = $true)]
        [Parameter(ParameterSetName = 'TypedPolicyLimit', Mandatory = $true)]
        [Parameter(ParameterSetName = 'TypedPolicyId', Mandatory = $true)]
        [ValidateSet("ConditionalAccess", "File", "InformationProtection", "OAuth", "ShadowIT", "Template", "ThreatDetection")]
        [string]$Type,

        [Parameter(ParameterSetName = 'TypedPolicyId', Mandatory = $true)]
        [Parameter(ParameterSetName = 'PolicyIdOnly', Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias("_id", "Id")]
        [string]$PolicyId,

        [Parameter(ParameterSetName = 'TypedMetadata', Mandatory = $true)]
        [switch]$Metadata,

        [Parameter(ParameterSetName = 'TypedAction', Mandatory = $true)]
        [switch]$Action,

        [Parameter(ParameterSetName = 'TypedPolicyLimit', Mandatory = $true)]
        [switch]$PolicyLimit,

        [Parameter(ParameterSetName = 'Setting', Mandatory = $true)]
        [switch]$Setting,

        [Parameter(ParameterSetName = 'Default')]
        [Parameter(ParameterSetName = 'Typed')]
        [ValidateRange(1, 5000)]
        [int]$Limit = 20,

        [Parameter(ParameterSetName = 'Default')]
        [Parameter(ParameterSetName = 'Typed')]
        [ValidateRange(0, [int]::MaxValue)]
        [int]$Skip = 0,

        [Parameter(ParameterSetName = 'Default')]
        [Parameter(ParameterSetName = 'Typed')]
        [string]$SortField = "severity",

        [Parameter(ParameterSetName = 'Default')]
        [Parameter(ParameterSetName = 'Typed')]
        [ValidateSet("asc", "desc")]
        [string]$SortDirection = "desc",

        [Parameter(ParameterSetName = 'Default')]
        [Parameter(ParameterSetName = 'Typed')]
        [hashtable]$Filters = @{},

        [Parameter()]
        [switch]$Force
    )

    begin {
        Update-XdrConnectionSettings

        # Define URL mappings for policy types
        $typeUrlMap = @{
            "ConditionalAccess"     = "conditional_access"
            "File"                  = "file"
            "InformationProtection" = "information_protection"
            "OAuth"                 = "oauth"
            "ShadowIT"              = "shadow_it"
            "Template"              = "policy_templates_inmemo"
            "ThreatDetection"       = "threat_detection"
        }
    }

    process {
        # Handle Setting request
        if ($Setting) {
            $CacheKey = "XdrCloudAppsPolicySetting"

            if (-not $Force) {
                $cache = Get-XdrCache -CacheKey $CacheKey -ErrorAction SilentlyContinue
                if ($cache -and $cache.NotValidAfter -gt (Get-Date)) {
                    Write-Verbose "Returning cached policy settings"
                    return $cache.Value
                }
            }

            $Uri = "https://security.microsoft.com/apiproxy/mcas/cas/api/v1/settings/"
            Write-Verbose "Retrieving policy settings from $Uri"

            try {
                $result = Invoke-RestMethod -Uri $Uri -Method Get -ContentType "application/json" -WebSession $script:session -Headers $script:headers
                Set-XdrCache -CacheKey $CacheKey -Value $result -TTLMinutes 15
                return $result
            } catch {
                Write-Error "Failed to retrieve policy settings: $_"
            }
            return
        }

        # Handle Action request for File or OAuth
        if ($Action) {
            if ($Type -eq "File") {
                $CacheKey = "XdrCloudAppsFilePolicyAction"
                $Uri = "https://security.microsoft.com/apiproxy/mcas/cas/api/v1/policy/file/actions/"
            } elseif ($Type -eq "OAuth") {
                $CacheKey = "XdrCloudAppsOAuthPolicyAction"
                $Uri = "https://security.microsoft.com/apiproxy/mcas/cas/api/v1/policy/app_permissions/actions/"
            } else {
                Write-Error "-Action is only supported with -Type File or -Type OAuth"
                return
            }

            if (-not $Force) {
                $cache = Get-XdrCache -CacheKey $CacheKey -ErrorAction SilentlyContinue
                if ($cache -and $cache.NotValidAfter -gt (Get-Date)) {
                    Write-Verbose "Returning cached $Type policy actions"
                    return $cache.Value
                }
            }

            Write-Verbose "Retrieving $Type policy actions from $Uri"

            try {
                $result = Invoke-RestMethod -Uri $Uri -Method Get -ContentType "application/json" -WebSession $script:session -Headers $script:headers
                Set-XdrCache -CacheKey $CacheKey -Value $result -TTLMinutes 15
                return $result
            } catch {
                Write-Error "Failed to retrieve $Type policy actions: $_"
            }
            return
        }

        # Handle PolicyLimit request for File policies
        if ($PolicyLimit) {
            if ($Type -ne "File") {
                Write-Error "-PolicyLimit is only supported with -Type File"
                return
            }

            $CacheKey = "XdrCloudAppsFilePolicyLimit"

            if (-not $Force) {
                $cache = Get-XdrCache -CacheKey $CacheKey -ErrorAction SilentlyContinue
                if ($cache -and $cache.NotValidAfter -gt (Get-Date)) {
                    Write-Verbose "Returning cached file policy limits"
                    return $cache.Value
                }
            }

            $Uri = "https://security.microsoft.com/apiproxy/mcas/cas/api/v1/policy/file/get_limit/"
            Write-Verbose "Retrieving file policy limits from $Uri"

            try {
                $result = Invoke-RestMethod -Uri $Uri -Method Get -ContentType "application/json" -WebSession $script:session -Headers $script:headers
                Set-XdrCache -CacheKey $CacheKey -Value $result -TTLMinutes 15
                return $result
            } catch {
                Write-Error "Failed to retrieve file policy limits: $_"
            }
            return
        }

        # Handle PolicyId request (with or without Type)
        if ($PSBoundParameters.ContainsKey('PolicyId')) {
            if ($PSBoundParameters.ContainsKey('Type')) {
                # Type is specified, use the correct endpoint directly
                if ($Type -eq "File") {
                    $CacheKey = "XdrCloudAppsFilePolicy_$PolicyId"
                    $Uri = "https://security.microsoft.com/apiproxy/mcas/cas/api/v1/policy/file/$PolicyId/"
                    $typeName = "XdrCloudAppsPolicyFile"
                } else {
                    Write-Error "-PolicyId with -Type is currently only supported for File policies. For other types, omit -Type to use auto-discovery."
                    return
                }

                if (-not $Force) {
                    $cache = Get-XdrCache -CacheKey $CacheKey -ErrorAction SilentlyContinue
                    if ($cache -and $cache.NotValidAfter -gt (Get-Date)) {
                        Write-Verbose "Returning cached $Type policy for PolicyId: $PolicyId"
                        return $cache.Value
                    }
                }

                Write-Verbose "Retrieving $Type policy from $Uri"

                try {
                    $response = Invoke-RestMethod -Uri $Uri -Method Get -ContentType "application/json" -WebSession $script:session -Headers $script:headers
                    $result = if ($null -ne $response.data) { $response.data } else { $response }
                    if ($null -ne $result) {
                        if ($result -is [array]) {
                            foreach ($item in $result) {
                                $item.PSObject.TypeNames.Insert(0, $typeName)
                            }
                        } else {
                            $result.PSObject.TypeNames.Insert(0, $typeName)
                        }
                    }

                    Set-XdrCache -CacheKey $CacheKey -Value $result -TTLMinutes 5
                    return $result
                } catch {
                    Write-Error "Failed to retrieve $Type policy '$PolicyId': $_"
                }
                return
            } else {
                # Auto-discovery mode: try File endpoint first, then others
                $discoveryEndpoints = @(
                    @{ Type = "File"; Uri = "https://security.microsoft.com/apiproxy/mcas/cas/api/v1/policy/file/$PolicyId/"; TypeName = "XdrCloudAppsPolicyFile" }
                )

                foreach ($endpoint in $discoveryEndpoints) {
                    Write-Verbose "Attempting to discover policy $PolicyId as $($endpoint.Type) type"
                    try {
                        $response = Invoke-RestMethod -Uri $endpoint.Uri -Method Get -ContentType "application/json" -WebSession $script:session -Headers $script:headers -ErrorAction Stop
                        $result = if ($null -ne $response.data) { $response.data } else { $response }
                        if ($null -ne $result) {
                            if ($result -is [array]) {
                                foreach ($item in $result) {
                                    $item.PSObject.TypeNames.Insert(0, $endpoint.TypeName)
                                }
                            } else {
                                $result.PSObject.TypeNames.Insert(0, $endpoint.TypeName)
                            }
                            Write-Verbose "Found policy $PolicyId as $($endpoint.Type) type"
                            return $result
                        }
                    } catch {
                        Write-Verbose "Policy $PolicyId not found as $($endpoint.Type) type, trying next..."
                        continue
                    }
                }

                Write-Error "Policy '$PolicyId' not found. Specify -Type to narrow the search or verify the PolicyId is correct."
                return
            }
        }

        # Handle Metadata request for typed policies
        if ($Metadata) {
            if ($Type -eq "File") {
                throw 'File policy metadata is not exposed by the live Cloud Apps API. Use Get-XdrCloudAppsPolicy -Type File for file policy data, or -Type File -Action / -PolicyLimit for supported file policy metadata surfaces.'
            }

            $CacheKey = "XdrCloudAppsPolicy${Type}Metadata"

            if (-not $Force) {
                $cache = Get-XdrCache -CacheKey $CacheKey -ErrorAction SilentlyContinue
                if ($cache -and $cache.NotValidAfter -gt (Get-Date)) {
                    Write-Verbose "Returning cached $Type policy metadata"
                    return $cache.Value
                }
            }

            $urlSegment = $typeUrlMap[$Type]

            # Different URL patterns for different types
            if ($Type -eq "Template") {
                $Uri = "https://security.microsoft.com/apiproxy/mcas/cas/api/v1/${urlSegment}/metadata/"
            } elseif ($Type -eq "OAuth") {
                $Uri = "https://security.microsoft.com/apiproxy/mcas/cas/api/v1/policies/oauth/metadata/"
            } else {
                $Uri = "https://security.microsoft.com/apiproxy/mcas/cas/api/v1/policies/${urlSegment}/metadata/"
            }

            Write-Verbose "Retrieving $Type policy metadata from $Uri"

            try {
                $result = Invoke-RestMethod -Uri $Uri -Method Get -ContentType "application/json" -WebSession $script:session -Headers $script:headers
                Set-XdrCache -CacheKey $CacheKey -Value $result -TTLMinutes 15
                return $result
            } catch {
                Write-Error "Failed to retrieve $Type policy metadata: $_"
            }
            return
        }

        $effectiveFilters = @{}
        foreach ($filterKey in $Filters.Keys) {
            $effectiveFilters[$filterKey] = $Filters[$filterKey]
        }

        # Default: List policies (with or without Type filter)
        if ($PSBoundParameters.ContainsKey('Type')) {
            $urlSegment = $typeUrlMap[$Type]
            $typeName = "XdrCloudAppsPolicy$Type"

            if ($Type -eq "Template") {
                $Uri = "https://security.microsoft.com/apiproxy/mcas/cas/api/v1/${urlSegment}/"
            } elseif ($Type -eq "OAuth") {
                $Uri = "https://security.microsoft.com/apiproxy/mcas/cas/api/v1/policies/oauth/"
            } elseif ($Type -eq "File") {
                # File policies use the general policies endpoint with type filter
                $Uri = "https://security.microsoft.com/apiproxy/mcas/cas/api/v1/policies/"
                # Add file type filter (uses 'type' filter with string value 'FILE')
                if (-not $effectiveFilters.ContainsKey('type')) {
                    $effectiveFilters['type'] = @{ 'eq' = @('FILE') }
                }
            } else {
                $Uri = "https://security.microsoft.com/apiproxy/mcas/cas/api/v1/policies/${urlSegment}/"
            }
        } else {
            $typeName = "XdrCloudAppsPolicy"
            $Uri = "https://security.microsoft.com/apiproxy/mcas/cas/api/v1/policies/"
        }

        # Create cache key based on parameters
        $filterHash = if ($effectiveFilters.Count -gt 0) { ($effectiveFilters | ConvertTo-Json -Compress) } else { "none" }
        $CacheKey = "${typeName}_${Limit}_${Skip}_${SortField}_${SortDirection}_${filterHash}"

        if (-not $Force) {
            $cache = Get-XdrCache -CacheKey $CacheKey -ErrorAction SilentlyContinue
            if ($cache -and $cache.NotValidAfter -gt (Get-Date)) {
                Write-Verbose "Returning cached policies"
                return $cache.Value
            }
        }

        $body = @{
            filters           = $effectiveFilters
            limit             = $Limit
            performAsyncTotal = $true
            skip              = $Skip
            sortDirection     = $SortDirection
            sortField         = $SortField
        }

        $jsonBody = $body | ConvertTo-Json -Depth 10

        Write-Verbose "Retrieving policies from $Uri"
        Write-Verbose "Request body: $jsonBody"

        try {
            $response = Invoke-RestMethod -Uri $Uri -Method Post -Body $jsonBody -ContentType "application/json" -WebSession $script:session -Headers $script:headers

            $result = if ($null -ne $response.data) { $response.data } else { $response }
            if ($null -ne $result) {
                $result = $result | Add-XdrCloudAppsTypeName -TypeName $typeName
            }

            # Templates cache longer than policies
            $ttl = if ($Type -eq "Template") { 15 } else { 5 }
            Set-XdrCache -CacheKey $CacheKey -Value $result -TTLMinutes $ttl

            return $result
        } catch {
            $policyType = if ($Type) { "$Type " } else { "" }
            Write-Error "Failed to retrieve ${policyType}policies: $_"
        }
    }
}