Private/24-GetAtlasConditionalAccessPolicies.ps1

function Get-AtlasConditionalAccessCollection {
    [CmdletBinding()]
    param(
        [object] $Object,

        [Parameter(Mandatory)]
        [string] $Name
    )

    if ($null -eq $Object) {
        return @()
    }

    $value = Get-AtlasResponseProperty -InputObject $Object -Name $Name
    if ($null -eq $value) {
        return @()
    }

    return @($value)
}

function New-AtlasConditionalAccessScopeNode {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
        'PSUseShouldProcessForStateChangingFunctions',
        '',
        Justification = 'Creates an in-memory model object and does not change external state.'
    )]
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string] $TenantId,

        [Parameter(Mandatory)]
        [string] $ScopeId,

        [Parameter(Mandatory)]
        [string] $DisplayName,

        [Parameter(Mandatory)]
        [string] $SubjectKind
    )

    return New-AtlasNode -TenantId $TenantId -Id "conditionalAccessScope:$ScopeId" -Kind 'conditionalAccessScope' -DisplayName $DisplayName -Properties @{
        subjectKind = $SubjectKind
    } -Source @{
        provider = 'microsoftGraph'
        apiVersion = 'v1.0'
        odataType = '#microsoft.graph.conditionalAccessScope'
        resourcePath = "/identity/conditionalAccess/policies"
        collector = 'conditionalAccess'
    } -Tags @('synthetic')
}

function Get-AtlasConditionalAccessPolicy {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string] $TenantId,

        [Parameter(Mandatory)]
        [AllowEmptyCollection()]
        [AtlasNode[]] $KnownNode
    )

    $result = [AtlasCollectionResult]::new()
    $keyById = @{}
    $keyByAppId = @{}
    foreach ($node in $KnownNode) {
        if (-not $keyById.ContainsKey($node.Id) -or $node.Status -eq 'complete') {
            $keyById[$node.Id] = $node.Key
        }
        if ($node.Kind -eq 'servicePrincipal' -and $node.Properties.ContainsKey('appId') -and $node.Properties.appId) {
            $keyByAppId[$node.Properties.appId] = $node.Key
        }
    }

    $endpoint = '/v1.0/identity/conditionalAccess/policies'
    try {
        $response = Invoke-AtlasGraphRequest -Uri $endpoint
    }
    catch {
        $result.Status = 'partial'
        $result.Warnings.Add("Conditional Access policies could not be collected: $(Get-AtlasSafeErrorDetail -ErrorRecord $_)")
        $result.Metrics = @{
            policyCount = 0
            assignmentEdgeCount = 0
            requestCount = 0
            retryCount = 0
        }
        return $result
    }

    $addedNode = @{}
    foreach ($policy in $response.Items) {
        $conditions = Get-AtlasResponseProperty -InputObject $policy -Name 'conditions'
        $users = Get-AtlasResponseProperty -InputObject $conditions -Name 'users'
        $applications = Get-AtlasResponseProperty -InputObject $conditions -Name 'applications'
        $grantControls = Get-AtlasResponseProperty -InputObject $policy -Name 'grantControls'
        $sessionControls = Get-AtlasResponseProperty -InputObject $policy -Name 'sessionControls'

        $policyNode = New-AtlasNode -TenantId $TenantId -Id $policy.id -Kind 'conditionalAccessPolicy' -DisplayName $policy.displayName -Properties @{
            state = $policy.state
            createdDateTime = $policy.createdDateTime
            modifiedDateTime = $policy.modifiedDateTime
            includeUserCount = @(Get-AtlasConditionalAccessCollection -Object $users -Name 'includeUsers').Count
            excludeUserCount = @(Get-AtlasConditionalAccessCollection -Object $users -Name 'excludeUsers').Count
            includeGroupCount = @(Get-AtlasConditionalAccessCollection -Object $users -Name 'includeGroups').Count
            excludeGroupCount = @(Get-AtlasConditionalAccessCollection -Object $users -Name 'excludeGroups').Count
            includeApplicationCount = @(Get-AtlasConditionalAccessCollection -Object $applications -Name 'includeApplications').Count
            excludeApplicationCount = @(Get-AtlasConditionalAccessCollection -Object $applications -Name 'excludeApplications').Count
            grantControls = $grantControls
            sessionControls = $sessionControls
        } -Source @{
            provider = 'microsoftGraph'
            apiVersion = 'v1.0'
            odataType = '#microsoft.graph.conditionalAccessPolicy'
            resourcePath = "/identity/conditionalAccess/policies/$($policy.id)"
            collector = 'conditionalAccess'
        }
        $result.Nodes.Add($policyNode)
        $addedNode[$policyNode.Key] = $true

        $assignments = @(
            @{
                Values = @(Get-AtlasConditionalAccessCollection -Object $users -Name 'includeUsers')
                Assignment = 'include'
                Category = 'users'
                SubjectKind = 'user'
                Relationship = 'conditionalAccessIncludes'
            }
            @{
                Values = @(Get-AtlasConditionalAccessCollection -Object $users -Name 'excludeUsers')
                Assignment = 'exclude'
                Category = 'users'
                SubjectKind = 'user'
                Relationship = 'conditionalAccessExcludes'
            }
            @{
                Values = @(Get-AtlasConditionalAccessCollection -Object $users -Name 'includeGroups')
                Assignment = 'include'
                Category = 'groups'
                SubjectKind = 'group'
                Relationship = 'conditionalAccessIncludes'
            }
            @{
                Values = @(Get-AtlasConditionalAccessCollection -Object $users -Name 'excludeGroups')
                Assignment = 'exclude'
                Category = 'groups'
                SubjectKind = 'group'
                Relationship = 'conditionalAccessExcludes'
            }
            @{
                Values = @(Get-AtlasConditionalAccessCollection -Object $applications -Name 'includeApplications')
                Assignment = 'include'
                Category = 'applications'
                SubjectKind = 'application'
                Relationship = 'conditionalAccessIncludes'
            }
            @{
                Values = @(Get-AtlasConditionalAccessCollection -Object $applications -Name 'excludeApplications')
                Assignment = 'exclude'
                Category = 'applications'
                SubjectKind = 'application'
                Relationship = 'conditionalAccessExcludes'
            }
        )

        foreach ($assignmentSet in $assignments) {
            foreach ($value in $assignmentSet.Values) {
                if (-not $value -or $value -eq 'None') {
                    continue
                }

                $subjectKey = $null
                if ($assignmentSet.Category -eq 'applications' -and $keyByAppId.ContainsKey($value)) {
                    $subjectKey = $keyByAppId[$value]
                }
                elseif ($keyById.ContainsKey($value)) {
                    $subjectKey = $keyById[$value]
                }
                else {
                    $scopeId = "$($assignmentSet.Category):$value"
                    $displayName = switch ($value) {
                        'All' { "All $($assignmentSet.Category)" }
                        'Office365' { 'Office 365' }
                        'GuestsOrExternalUsers' { 'Guests or external users' }
                        default { $value }
                    }
                    $scopeNode = New-AtlasConditionalAccessScopeNode -TenantId $TenantId -ScopeId $scopeId -DisplayName $displayName -SubjectKind $assignmentSet.SubjectKind
                    if (-not $addedNode.ContainsKey($scopeNode.Key)) {
                        $result.Nodes.Add($scopeNode)
                        $addedNode[$scopeNode.Key] = $true
                    }
                    $subjectKey = $scopeNode.Key
                }

                $evidence = New-AtlasEvidence -TenantId $TenantId -Collector 'conditionalAccess' -Endpoint $endpoint -SourceObjectId "$($policy.id)|$($assignmentSet.Assignment)|$($assignmentSet.Category)|$value" -Fields @{
                    policyId = $policy.id
                    policyDisplayName = $policy.displayName
                    assignment = $assignmentSet.Assignment
                    category = $assignmentSet.Category
                    value = $value
                    state = $policy.state
                }
                $result.Evidence.Add($evidence)
                $result.Edges.Add(
                    (New-AtlasEdge -TenantId $TenantId -From $subjectKey -To $policyNode.Key -Relationship $assignmentSet.Relationship -State @{
                        assignment = $assignmentSet.Assignment
                        category = $assignmentSet.Category
                        subjectKind = $assignmentSet.SubjectKind
                        policyState = $policy.state
                    } -EvidenceIds @($evidence.Key) -Source @{
                        collector = 'conditionalAccess'
                    })
                )
            }
        }
    }

    $result.Metrics = @{
        policyCount = $response.Items.Count
        assignmentEdgeCount = $result.Edges.Count
        requestCount = $response.Metrics.requestCount
        retryCount = $response.Metrics.retryCount
    }
    return $result
}