Private/Resolve-NSPConditionalAccessReference.ps1

function Resolve-NSPConditionalAccessReference {
    <#
    .SYNOPSIS
        Turns one {Kind:Name} reference into object IDs, or throws. Never returns an empty result.
    .DESCRIPTION
        A reference that cannot be resolved stops resolution of the whole policy. The legacy
        wizard dropped such entries with a warning, which could remove a breakglass exclusion.
    #>

    [CmdletBinding()]
    [OutputType([string[]])]
    param(
        [Parameter(Mandatory)][string]$Kind,
        [string]$Name,
        $TenantProfile,
        $Directory,
        [Parameter(Mandatory)][string]$Context
    )

    function Get-BoundId {
        param($Binding, [string]$What)
        $ids = @(Get-NSPConditionalAccessMapValue $Binding 'Ids' | Where-Object { $_ })
        if ($ids.Count -eq 0) { throw "$Context : $What has no object IDs in the tenant profile." }
        $ids
    }

    switch ($Kind) {
        'BreakGlass' {
            Get-BoundId (Get-NSPConditionalAccessMapValue $TenantProfile 'BreakGlass') '{BreakGlass}'
        }
        'ServiceAccounts' {
            # Unlike breakglass, a tenant may legitimately have none, but the profile must say so
            # explicitly (Ids: []). A missing entry means the exclusions were never reviewed.
            $binding = Get-NSPConditionalAccessMapValue $TenantProfile 'ServiceAccounts'
            if ($null -eq $binding) { throw "$Context : {ServiceAccounts} has not been reviewed in the tenant profile (set Ids to [] if there are none)." }
            @(Get-NSPConditionalAccessMapValue $binding 'Ids' | Where-Object { $_ })
        }
        'Group' {
            $groups = Get-NSPConditionalAccessMapValue $TenantProfile 'Groups'
            Get-BoundId (Get-NSPConditionalAccessMapValue $groups $Name) "{Group:$Name}"
        }
        'TravelGroup' {
            $entries = Get-NSPConditionalAccessMapValue (Get-NSPConditionalAccessMapValue $TenantProfile 'Travel') 'Entries'
            Get-BoundId (Get-NSPConditionalAccessMapValue $entries $Name) "{TravelGroup:$Name}"
        }
        'TravelGroups' {
            # A set the planner computes (Active: every current travel group; Enforced: those whose
            # travel policy is On). It may legitimately be empty, but it must have been computed.
            if ($Name -notin 'Active', 'Enforced') { throw "$Context : {TravelGroups:$Name} must be Active or Enforced." }
            $sets = Get-NSPConditionalAccessMapValue $Directory 'TravelGroups'
            if ($null -eq $sets) { throw "$Context : {TravelGroups:$Name} needs the planner's travel group sets (Directory.TravelGroups)." }
            @(Get-NSPConditionalAccessMapValue $sets $Name | Where-Object { $_ })
        }
        'Role' {
            $table = Get-NSPConditionalAccessRoleTable
            if (-not $table.ContainsKey($Name)) { throw "$Context : {Role:$Name} is not in the role table." }
            $table[$Name]
        }
        'App' {
            $apps = Get-NSPConditionalAccessMapValue $Directory 'Applications'
            $appId = Get-NSPConditionalAccessMapValue $apps $Name
            if (-not $appId) { throw "$Context : {App:$Name} was not found in the directory lookup." }
            $appId
        }
        'Location' {
            $named = Get-NSPConditionalAccessMapValue (Get-NSPConditionalAccessMapValue $TenantProfile 'NamedLocations') 'Named'
            $id = Get-NSPConditionalAccessMapValue $named $Name
            if (-not $id) { throw "$Context : {Location:$Name} has no named location bound in the tenant profile." }
            $id
        }
        'Country' {
            $iso = if ($Name -eq 'Home') { Get-NSPConditionalAccessMapValue $TenantProfile 'HomeCountry' } else { $Name }
            if (-not $iso) { throw "$Context : {Country:Home} needs HomeCountry in the tenant profile." }
            $countries = Get-NSPConditionalAccessMapValue (Get-NSPConditionalAccessMapValue $TenantProfile 'NamedLocations') 'Countries'
            $id = Get-NSPConditionalAccessMapValue $countries $iso
            if (-not $id) { throw "$Context : {Country:$Name} ($iso) has no country named location bound in the tenant profile." }
            $id
        }
        'Strength' {
            # Microsoft's built-in strengths have the same ID everywhere; nothing is bound.
            $builtIn = (Get-NSPConditionalAccessStrengthCatalog)[$Name]
            if ($builtIn -and $builtIn.BuiltInId) { return $builtIn.BuiltInId }
            $strengths = Get-NSPConditionalAccessMapValue $TenantProfile 'AuthenticationStrengths'
            $id = Get-NSPConditionalAccessMapValue $strengths $Name
            if (-not $id) { throw "$Context : {Strength:$Name} has no authentication strength bound in the tenant profile." }
            $id
        }
        default { throw "$Context : unknown reference kind '$Kind'." }
    }
}