Private/Resolve-RBACScope.ps1

function Resolve-RBACScope {
    <#
    .SYNOPSIS
        Normalizes or builds an Azure RBAC scope string.
    .DESCRIPTION
        Internal. Builds (or validates) an Azure Resource Manager scope from the
        supplied management-group, subscription, resource-group, and resource
        inputs. An explicit -Scope always wins. A trailing slash is trimmed.
 
        For tenant-level platforms (Graph / Fabric / Purview) the scope is more
        loosely defined; pass '/' or a platform-native identifier and it is
        returned as-is (trimmed).
    .OUTPUTS
        System.String
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter()]
        [string]$Scope,

        [Parameter()]
        [string]$ManagementGroupId,

        [Parameter()]
        [string]$SubscriptionId,

        [Parameter()]
        [string]$ResourceGroupName,

        [Parameter()]
        [string]$ResourceId,

        [Parameter()]
        [switch]$AllowTenantRoot
    )

    Write-PSFMessage -Level Debug -Message "Resolving scope (Scope='$Scope', MG='$ManagementGroupId', Sub='$SubscriptionId', RG='$ResourceGroupName', Resource='$ResourceId', AllowTenantRoot=$AllowTenantRoot)." -Tag 'PSAutoRBAC', 'Scope'

    if ($Scope) {
        $trimmed = $Scope.TrimEnd('/')
        if ([string]::IsNullOrEmpty($trimmed)) { return '/' }
        Write-PSFMessage -Level Debug -Message "Using explicit scope '$trimmed'." -Tag 'PSAutoRBAC', 'Scope'
        return $trimmed
    }

    if ($ResourceId) { return $ResourceId.TrimEnd('/') }

    if ($ManagementGroupId) {
        return "/providers/Microsoft.Management/managementGroups/$ManagementGroupId"
    }

    if ($SubscriptionId -and $ResourceGroupName) {
        return "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName"
    }

    if ($SubscriptionId) {
        return "/subscriptions/$SubscriptionId"
    }

    if ($AllowTenantRoot) {
        Write-PSFMessage -Level Debug -Message 'No ARM scope inputs supplied; defaulting to tenant root.' -Tag 'PSAutoRBAC', 'Scope'
        return '/'
    }

    Write-PSFMessage -Level Error -Message 'Unable to resolve an RBAC scope from the supplied inputs.' -Tag 'PSAutoRBAC', 'Scope'
    throw 'Unable to resolve an RBAC scope. Provide -Scope, -ManagementGroupId, or -SubscriptionId (optionally with -ResourceGroupName / -ResourceId).'
}