Public/Test-GSAForwardingProfileAssignment.ps1

#
# Function: Test-GSAForwardingProfileAssignment
# Public function for the GSA client diagnostics toolkit.
#
function Test-GSAForwardingProfileAssignment
{
    <#
.SYNOPSIS
    Checks via Microsoft Graph that a GSA forwarding profile is enabled and the user is assigned to it.
.DESCRIPTION
    Verifies the tenant is onboarded to Global Secure Access, that the requested forwarding profile
    (m365/private/internet) is enabled, and that the target user is directly assigned - or a member of
    a group assigned - to that profile's enterprise application (service principal).
.PARAMETER AccessToken
    A Microsoft Graph access token with NetworkAccess.Read.All, Application.Read.All, Directory.Read.All,
    and User.Read.All permissions (e.g. obtained via Test-EntraIDUserAuthentication or your own MSAL/Graph sign-in).
    If omitted, an interactive Connect-MgGraph sign-in is triggered automatically (requires the
    Microsoft.Graph.Authentication module), reusing any already-active Graph session.
.PARAMETER ProfileType
    The forwarding profile to check: m365, private, or internet.
.PARAMETER UserPrincipalName
    UPN of the user to check assignment for. Defaults to the currently signed-in Windows user (whoami /upn).
.PARAMETER PassThru
    Return the $true/$false test result. By default nothing is returned - only logged.
.EXAMPLE
    Test-GSAForwardingProfileAssignment -AccessToken $token -ProfileType private
.EXAMPLE
    Test-GSAForwardingProfileAssignment -ProfileType private
.EXAMPLE
    Test-GSAForwardingProfileAssignment -ProfileType m365 -UserPrincipalName user@domain.com
#>

    [CmdletBinding()]
    param (
        [string]$AccessToken,
        [Parameter(Mandatory)]
        [ValidateSet("m365", "private", "internet")]
        [string]$ProfileType,
        [string]$UserPrincipalName,
        [switch]$PassThru,
        [ValidateSet("ERROR", "WARNING", "INFO", "DEBUG", "SUCCESS")]
        [string]$LogLevel
    )

    if ($PSBoundParameters.ContainsKey('LogLevel'))
    { $script:LogLevel = $LogLevel }

    # Map the profile type to its human-readable name for logging purposes.
    $profileNames = @{ m365 = "Microsoft Traffic"; private = "Private Access"; internet = "Internet Access" }
    $profileName = $profileNames[$ProfileType]

    # Determine the user principal name to check assignment for.
    if (-not $UserPrincipalName)
    { $UserPrincipalName = Get-GSACurrentUserUPN }
    if (-not $UserPrincipalName)
    {
        Write-Log "Unable to determine the current user's UPN. Specify -UserPrincipalName explicitly." "ERROR"
        if ($PassThru)
        { return $false }
        return
    }

    # Check if the tenant is onboarded to Global Secure Access.
    Write-Log "Checking the Global Secure Access activation status..." "INFO"
    try
    {
        $tenantStatus = Invoke-GSAGraphRequest -AccessToken $AccessToken -Uri "https://graph.microsoft.com/beta/networkAccess/tenantStatus"
    }
    catch
    {
        Write-Log "Failed to check tenant onboarding status via Microsoft Graph: $($_.Exception.Message)" "ERROR"
        if ($PassThru)
        { return $false }
        return
    }
    if ($tenantStatus.onboardingStatus -ne 'onboarded')
    {
        Write-Log "Global Secure Access is NOT activated on the tenant." "ERROR"
        Write-Log "Recommended action: Activate Global Secure Access in Entra > Global Secure Access > Get started > Activate Global Secure Access in your tenant." "WARNING"
        if ($PassThru)
        { return $false }
        return
    }
    Write-Log "Global Secure Access is activated in the tenant." "SUCCESS"

    # Check if the forwarding profile for the specified profile type is enabled.
    Write-Log "Checking the $profileName forwarding profile..." "INFO"
    try
    {
        $profilesResult = Invoke-GSAGraphRequest -AccessToken $AccessToken -Uri "https://graph.microsoft.com/beta/networkAccess/forwardingProfiles?`$filter=trafficForwardingType eq '$ProfileType'"
    }
    catch
    {
        Write-Log "Failed to retrieve the $profileName forwarding profile via Microsoft Graph: $($_.Exception.Message)" "ERROR"
        if ($PassThru)
        { return $false }
        return
    }
    $forwardingProfile = @($profilesResult.value)[0]
    if (-not $forwardingProfile -or $forwardingProfile.state -ne 'enabled')
    {
        Write-Log "$profileName forwarding profile is NOT enabled." "ERROR"
        Write-Log "Recommended action: Enable the $profileName profile from Entra > Global Secure Access > Connect > Traffic forwarding." "WARNING"
        if ($PassThru)
        { return $false }
        return
    }
    Write-Log "$profileName forwarding profile is enabled." "SUCCESS"

    # Check if the current user is assigned to the forwarding profile for the specified profile type.
    Write-Log "Checking user assignment to the $profileName forwarding profile..." "INFO"
    $servicePrincipalId = $forwardingProfile.servicePrincipal.id
    try
    {
        $sp = Invoke-GSAGraphRequest -AccessToken $AccessToken -Uri "https://graph.microsoft.com/v1.0/servicePrincipals/$servicePrincipalId`?`$select=id,appid,accountEnabled,appRoleAssignmentRequired&`$expand=appRoleAssignedTo(`$select=principalId,principalType,principalDisplayName)"
    }
    catch
    {
        Write-Log "Failed to retrieve the $profileName service principal via Microsoft Graph: $($_.Exception.Message)" "ERROR"
        if ($PassThru)
        { return $false }
        return
    }

    # Extract the assigned users and groups from the service principal's app role assignments.
    $assignedUsers = @($sp.appRoleAssignedTo) | Where-Object { $_.principalType -eq 'User' }
    $assignedGroupIds = @($sp.appRoleAssignedTo) | Where-Object { $_.principalType -eq 'Group' } | ForEach-Object { $_.principalId }

    # Test if the current user is assigned to the forwarding profile, either directly or via group membership.
    $assigned = Test-GSAAppRoleAssignment -AccessToken $AccessToken -UserPrincipalName $UserPrincipalName -AssignmentRequired ([bool]$sp.appRoleAssignmentRequired) -AssignedUsers $assignedUsers -AssignedGroupIds $assignedGroupIds
    if (-not $assigned)
    {
        Write-Log "'$UserPrincipalName' is NOT assigned (directly or via group) to the $profileName profile." "ERROR"
        Write-Log "Recommended action: Assign the user directly, or via group membership, to the $profileName profile's enterprise application in Entra." "WARNING"
        if ($PassThru)
        { return $false }
        return
    }
    # The user is successfully assigned to the forwarding profile.
    Write-Log "'$UserPrincipalName' is assigned to the $profileName profile." "SUCCESS"

    if ($PassThru)
    { return $true }
}