Private/Get-NSPConditionalAccessDefinitionSchema.ps1

function Get-NSPConditionalAccessDefinitionSchema {
    <#
    .SYNOPSIS
        The policy definition format: allowed keys, reference placement, closed value sets, and Graph property names.
    .DESCRIPTION
        Test-NSPConditionalAccessDefinition and Resolve-NSPConditionalAccessPolicy both read this, so
        validation and resolution cannot drift apart. Each list field names its Graph target path
        and either a closed value set (Values) or the references and keywords it accepts. A Shape
        changes how the list is sent: 'Csv' joins it into one comma-separated string (Graph's flag
        enums), and 'GuestTypes' wraps it in the guests-or-external-users object (all external
        tenants).
    #>

    [CmdletBinding()]
    [OutputType([hashtable])]
    param()

    $userKeywords = @('All', 'None', 'GuestsOrExternalUsers')
    $appKeywords = @('All', 'None', 'Office365', 'MicrosoftAdminPortals')
    $locationKeywords = @('All', 'AllTrusted')
    $platforms = @('all', 'android', 'iOS', 'windows', 'windowsPhone', 'macOS', 'linux')
    $guestTypes = @('internalGuest', 'b2bCollaborationGuest', 'b2bCollaborationMember', 'b2bDirectConnectUser', 'otherExternalUser', 'serviceProvider')
    $riskLevels = @('low', 'medium', 'high', 'hidden', 'none')

    @{
        TopLevelKeys = @(
            'Id', 'Name', 'Version', 'Description', 'Notes', 'LegacyNames', 'DefaultState', 'RequireBreakGlassExclusion', 'Option',
            'Users', 'Applications', 'Platforms', 'Locations', 'ClientAppTypes', 'SignInRiskLevels', 'UserRiskLevels', 'AuthenticationFlows',
            'Grant', 'Session', 'SourcePath'
        )
        Sections = @{
            Users = @('IncludeUsers', 'ExcludeUsers', 'IncludeGroups', 'ExcludeGroups', 'IncludeRoles', 'ExcludeRoles', 'IncludeGuestsOrExternalUsers', 'ExcludeGuestsOrExternalUsers')
            Applications = @('Include', 'Exclude', 'UserActions')
            Platforms = @('Include', 'Exclude')
            Locations = @('Include', 'Exclude')
            Grant = @('Operator', 'BuiltInControls', 'AuthenticationStrength')
            Session = @('SignInFrequency', 'PersistentBrowser')
        }
        Lists = @(
            @{ Section = 'Users'; Key = 'IncludeUsers'; Target = 'users', 'includeUsers'; References = @('BreakGlass', 'ServiceAccounts'); Keywords = $userKeywords }
            @{ Section = 'Users'; Key = 'ExcludeUsers'; Target = 'users', 'excludeUsers'; References = @('BreakGlass', 'ServiceAccounts'); Keywords = @('GuestsOrExternalUsers') }
            @{ Section = 'Users'; Key = 'IncludeGroups'; Target = 'users', 'includeGroups'; References = @('Group', 'TravelGroup', 'TravelGroups'); Keywords = @() }
            @{ Section = 'Users'; Key = 'ExcludeGroups'; Target = 'users', 'excludeGroups'; References = @('Group', 'TravelGroup', 'TravelGroups'); Keywords = @() }
            @{ Section = 'Users'; Key = 'IncludeRoles'; Target = 'users', 'includeRoles'; References = @('Role'); Keywords = @() }
            @{ Section = 'Users'; Key = 'ExcludeRoles'; Target = 'users', 'excludeRoles'; References = @('Role'); Keywords = @() }
            @{ Section = 'Users'; Key = 'IncludeGuestsOrExternalUsers'; Target = 'users', 'includeGuestsOrExternalUsers'; Values = $guestTypes; Shape = 'GuestTypes' }
            @{ Section = 'Users'; Key = 'ExcludeGuestsOrExternalUsers'; Target = 'users', 'excludeGuestsOrExternalUsers'; Values = $guestTypes; Shape = 'GuestTypes' }
            @{ Section = 'Applications'; Key = 'Include'; Target = 'applications', 'includeApplications'; References = @('App'); Keywords = $appKeywords }
            @{ Section = 'Applications'; Key = 'Exclude'; Target = 'applications', 'excludeApplications'; References = @('App'); Keywords = $appKeywords }
            @{ Section = 'Applications'; Key = 'UserActions'; Target = 'applications', 'includeUserActions'; Values = @('urn:user:registersecurityinfo', 'urn:user:registerdevice') }
            @{ Section = 'Platforms'; Key = 'Include'; Target = 'platforms', 'includePlatforms'; Values = $platforms }
            @{ Section = 'Platforms'; Key = 'Exclude'; Target = 'platforms', 'excludePlatforms'; Values = $platforms }
            @{ Section = 'Locations'; Key = 'Include'; Target = 'locations', 'includeLocations'; References = @('Location', 'Country'); Keywords = $locationKeywords }
            @{ Section = 'Locations'; Key = 'Exclude'; Target = 'locations', 'excludeLocations'; References = @('Location', 'Country'); Keywords = $locationKeywords }
            @{ Section = $null; Key = 'ClientAppTypes'; Target = @('clientAppTypes'); Values = @('all', 'browser', 'mobileAppsAndDesktopClients', 'exchangeActiveSync', 'easSupported', 'other') }
            @{ Section = $null; Key = 'SignInRiskLevels'; Target = @('signInRiskLevels'); Values = $riskLevels }
            @{ Section = $null; Key = 'UserRiskLevels'; Target = @('userRiskLevels'); Values = $riskLevels }
            @{ Section = $null; Key = 'AuthenticationFlows'; Target = 'authenticationFlows', 'transferMethods'; Values = @('deviceCodeFlow', 'authenticationTransfer'); Shape = 'Csv' }
        )
        States = @{
            reportOnly = 'enabledForReportingButNotEnforced'
            disabled = 'disabled'
            enabled = 'enabled'
        }
        GrantOperators = @('AND', 'OR')
        BuiltInControls = @('block', 'mfa', 'compliantDevice', 'domainJoinedDevice', 'approvedApplication', 'compliantApplication', 'passwordChange')
        SignInFrequencyTypes = @('hours', 'days')
        PersistentBrowserModes = @('always', 'never')
        ReferencePattern = '^\{(?<kind>BreakGlass|ServiceAccounts|Group|TravelGroups|TravelGroup|Role|App|Location|Country|Strength)(:(?<name>[^{}]+))?\}$'
        IdPattern = '^NSP-[A-Z0-9]+-[A-Z0-9]+-\d{2}$'
    }
}