Public/Invoke-IdentityAtlas.ps1

function Invoke-IdentityAtlas {
    [CmdletBinding()]
    param(
        [string] $OutputPath = (Join-Path (Get-Location) "IdentityAtlasReport-$([datetime]::Now.ToString('yyyyMMdd-HHmmss'))"),

        [switch] $OpenReport,

        [ValidateSet('Auto', 'Core', 'Governance')]
        [string] $CollectionProfile = 'Auto'
    )

    if (-not (Get-Command -Name Get-MgContext -ErrorAction SilentlyContinue)) {
        throw 'Microsoft.Graph.Authentication is required for live collection. Run Connect-IdentityAtlas after installing the dependency.'
    }

    $context = Get-MgContext
    if (-not $context -or -not $context.TenantId) {
        throw 'No Microsoft Graph PowerShell session is active. Run Connect-IdentityAtlas first.'
    }

    if ($CollectionProfile -eq 'Auto') {
        $storedProfile = Get-Variable -Name IdentityAtlasCollectionProfile -Scope Script -ErrorAction SilentlyContinue
        $CollectionProfile = if ($storedProfile -and $storedProfile.Value) { $storedProfile.Value } else { 'Core' }
    }

    $permissionPreflight = New-AtlasPermissionPreflightResult -ContextScope @($context.Scopes) -CollectionProfile $CollectionProfile
    $users = Invoke-AtlasCollector -Name 'users' -DisplayName 'Users' -Collector {
        Get-AtlasUser -TenantId $context.TenantId
    }
    $groups = Invoke-AtlasCollector -Name 'groups' -DisplayName 'Groups' -Collector {
        Get-AtlasGroup -TenantId $context.TenantId
    }
    $identityCollection = Merge-AtlasCollectionResult -Result @($permissionPreflight, $users, $groups)
    $devicesAndAuthentication = Invoke-AtlasCollector -Name 'devicesAndAuthentication' -DisplayName 'Devices and authentication methods' -Collector {
        Get-AtlasDeviceAndAuthentication -TenantId $context.TenantId -KnownNode @($identityCollection.Nodes)
    }
    $roles = Invoke-AtlasCollector -Name 'directoryRoles' -DisplayName 'Directory roles' -Collector {
        Get-AtlasDirectoryRole -TenantId $context.TenantId -KnownNode @($identityCollection.Nodes)
    }
    $applications = Invoke-AtlasCollector -Name 'applications' -DisplayName 'Applications' -Collector {
        Get-AtlasApplication -TenantId $context.TenantId -KnownNode @($identityCollection.Nodes)
    }
    $identityAndApplicationCollection = Merge-AtlasCollectionResult -Result @($identityCollection, $applications)
    $applicationManagementPolicies = Invoke-AtlasCollector -Name 'applicationManagementPolicies' -DisplayName 'Application management policies' -Collector {
        Get-AtlasApplicationManagementPolicy -TenantId $context.TenantId -KnownNode @($identityAndApplicationCollection.Nodes)
    }
    $crossTenantAccess = Invoke-AtlasCollector -Name 'crossTenantAccess' -DisplayName 'Cross-tenant access settings' -Collector {
        Get-AtlasCrossTenantAccess -TenantId $context.TenantId
    }
    $conditionalAccess = Invoke-AtlasCollector -Name 'conditionalAccess' -DisplayName 'Conditional Access policies' -Collector {
        Get-AtlasConditionalAccessPolicy -TenantId $context.TenantId -KnownNode @($identityAndApplicationCollection.Nodes)
    }
    $conditionalAccessReferences = Invoke-AtlasCollector -Name 'conditionalAccessReferences' -DisplayName 'Conditional Access references' -Collector {
        Get-AtlasConditionalAccessReference -TenantId $context.TenantId -KnownNode @($conditionalAccess.Nodes)
    }
    $coreCollection = Merge-AtlasCollectionResult -Result @(
        $identityCollection
        $devicesAndAuthentication
        $roles
        $applications
        $applicationManagementPolicies
        $crossTenantAccess
        $conditionalAccess
        $conditionalAccessReferences
    )

    $governanceResults = @()
    if ($CollectionProfile -eq 'Governance') {
        $administrativeUnits = Invoke-AtlasCollector -Name 'administrativeUnits' -DisplayName 'Administrative Units' -Collector {
            Get-AtlasAdministrativeUnit -TenantId $context.TenantId -KnownNode @($coreCollection.Nodes) -KnownEdge @($roles.Edges)
        }
        $pimGroups = Invoke-AtlasCollector -Name 'pimGroups' -DisplayName 'PIM for Groups assignments' -Collector {
            Get-AtlasPrivilegedGroupAssignment -TenantId $context.TenantId -KnownNode @($coreCollection.Nodes)
        }
        $governanceFoundation = Merge-AtlasCollectionResult -Result @($coreCollection, $administrativeUnits, $pimGroups)
        $entitlementManagement = Invoke-AtlasCollector -Name 'entitlementManagement' -DisplayName 'Entitlement Management' -Collector {
            Get-AtlasEntitlementManagement -TenantId $context.TenantId -KnownNode @($governanceFoundation.Nodes)
        }
        $governanceWithEntitlements = Merge-AtlasCollectionResult -Result @($governanceFoundation, $entitlementManagement)
        $accessReviews = Invoke-AtlasCollector -Name 'accessReviews' -DisplayName 'Access Reviews' -Collector {
            Get-AtlasAccessReview -TenantId $context.TenantId -KnownNode @($governanceWithEntitlements.Nodes)
        }
        $governanceResults = @($administrativeUnits, $pimGroups, $entitlementManagement, $accessReviews)
    }
    $collection = Merge-AtlasCollectionResult -Result (@($coreCollection) + $governanceResults)

    $collectors = @(
        @{ name = 'permissionPreflight'; status = $permissionPreflight.Status; metrics = $permissionPreflight.Metrics }
        @{ name = 'users'; status = $users.Status; metrics = $users.Metrics }
        @{ name = 'groups'; status = $groups.Status; metrics = $groups.Metrics }
        @{ name = 'devicesAndAuthentication'; status = $devicesAndAuthentication.Status; metrics = $devicesAndAuthentication.Metrics }
        @{ name = 'directoryRoles'; status = $roles.Status; metrics = $roles.Metrics }
        @{ name = 'applications'; status = $applications.Status; metrics = $applications.Metrics }
        @{ name = 'applicationManagementPolicies'; status = $applicationManagementPolicies.Status; metrics = $applicationManagementPolicies.Metrics }
        @{ name = 'crossTenantAccess'; status = $crossTenantAccess.Status; metrics = $crossTenantAccess.Metrics }
        @{ name = 'conditionalAccess'; status = $conditionalAccess.Status; metrics = $conditionalAccess.Metrics }
        @{ name = 'conditionalAccessReferences'; status = $conditionalAccessReferences.Status; metrics = $conditionalAccessReferences.Metrics }
    )
    if ($CollectionProfile -eq 'Governance') {
        $collectors += @(
            @{ name = 'administrativeUnits'; status = $administrativeUnits.Status; metrics = $administrativeUnits.Metrics }
            @{ name = 'pimGroups'; status = $pimGroups.Status; metrics = $pimGroups.Metrics }
            @{ name = 'entitlementManagement'; status = $entitlementManagement.Status; metrics = $entitlementManagement.Metrics }
            @{ name = 'accessReviews'; status = $accessReviews.Status; metrics = $accessReviews.Metrics }
        )
    }
    $report = New-AtlasReport -TenantId $context.TenantId -TenantDisplayName $context.TenantId -Collection $collection -Collectors $collectors -DataOrigin LiveTenant -CollectionProfile $CollectionProfile

    $indexFile = Write-AtlasReport -Report $report -OutputPath $OutputPath
    if ($OpenReport) {
        Start-Process -FilePath $indexFile.FullName
    }

    return [pscustomobject] @{
        OutputPath = $indexFile.DirectoryName
        IndexPath = $indexFile.FullName
        NodeCount = $report.manifest.counts.nodes
        EdgeCount = $report.manifest.counts.edges
        EvidenceCount = $report.manifest.counts.evidence
        CoverageStatus = $report.manifest.coverage.status
        CollectionProfile = $CollectionProfile
    }
}