Public/Connection/Connect-TBTenant.ps1

function Connect-TBTenant {
    <#
    .SYNOPSIS
        Connects to Microsoft Graph with the scopes required for UTCM operations.
    .DESCRIPTION
        Wraps Connect-MgGraph to establish a Microsoft Graph session for tenant
        configuration management. Supports delegated interactive sign-in for
        local/admin usage and unattended authentication modes for automation
        scenarios such as Azure Automation, GitHub Actions, and scheduled
        runners. Supports national/government cloud environments via the
        -Environment parameter. After a successful connection the module-scoped
        API base URI is updated automatically based on the active Graph
        environment.
    .PARAMETER TenantId
        The tenant ID to connect to. Used by delegated, app certificate, and
        client secret authentication. If not specified for delegated auth, uses
        the default tenant.
    .PARAMETER Scenario
        Delegated authentication scope profile:
        - ReadOnly: ConfigurationMonitoring.Read.All
        - Manage: ConfigurationMonitoring.ReadWrite.All
        - Setup: Manage + Application.ReadWrite.All + AppRoleAssignment.ReadWrite.All
    .PARAMETER Scopes
        Additional delegated scopes to request beyond the selected scenario
        scopes.
    .PARAMETER IncludeDirectoryMetadata
        Delegated-only option that requests optional directory metadata scopes
        (Organization.Read.All and Domain.Read.All) and attempts to resolve
        tenant display name and primary domain for friendly identity labels.
    .PARAMETER UseDeviceCode
        Delegated-only option that uses the device code flow instead of opening
        a browser window.
    .PARAMETER ClientId
        The application or managed identity client ID for unattended
        authentication modes.
    .PARAMETER CertificateThumbprint
        App-certificate authentication: certificate thumbprint in the current
        user's certificate store.
    .PARAMETER CertificateSubjectName
        App-certificate authentication: certificate subject name in the current
        user's certificate store.
    .PARAMETER Certificate
        App-certificate authentication: X509 certificate object to present.
    .PARAMETER ClientSecretCredential
        Client secret credential for app-only authentication. Supply a
        PSCredential whose username is the client ID and whose password is the
        client secret.
    .PARAMETER Identity
        Uses managed identity authentication for unattended execution.
    .PARAMETER AccessToken
        Uses a pre-acquired Microsoft Graph access token.
    .PARAMETER Environment
        The Microsoft Graph cloud environment to connect to:
        - Global (default): Commercial cloud
        - USGov: GCC High (graph.microsoft.us)
        - USGovDoD: DoD (dod-graph.microsoft.us)
        - China: 21Vianet (microsoftgraph.chinacloudapi.cn)
    .EXAMPLE
        Connect-TBTenant
        Connects with Manage scopes to the Global cloud.
    .EXAMPLE
        Connect-TBTenant -Scenario Setup
        Connects with setup scopes required for service principal provisioning.
    .EXAMPLE
        Connect-TBTenant -TenantId 'contoso.onmicrosoft.com'
        Connects to a specific tenant.
    .EXAMPLE
        Connect-TBTenant -Environment USGov
        Connects to a GCC High tenant.
    .EXAMPLE
        Connect-TBTenant -Identity
        Connects using the ambient managed identity.
    .EXAMPLE
        Connect-TBTenant -ClientId '11111111-1111-1111-1111-111111111111' -TenantId 'contoso.onmicrosoft.com' -CertificateThumbprint 'ABC123...'
        Connects using app-only certificate authentication.
    #>

    [CmdletBinding(DefaultParameterSetName = 'Delegated')]
    param(
        [Parameter(ParameterSetName = 'Delegated')]
        [Parameter(ParameterSetName = 'AppCertificate')]
        [Parameter(ParameterSetName = 'ClientSecret')]
        [string]$TenantId,

        [Parameter(ParameterSetName = 'Delegated')]
        [ValidateSet('ReadOnly', 'Manage', 'Setup')]
        [string]$Scenario = 'Manage',

        [Parameter(ParameterSetName = 'Delegated')]
        [string[]]$Scopes,

        [Parameter(ParameterSetName = 'Delegated')]
        [switch]$IncludeDirectoryMetadata,

        [Parameter(ParameterSetName = 'Delegated')]
        [switch]$UseDeviceCode,

        [Parameter(ParameterSetName = 'AppCertificate', Mandatory = $true)]
        [Parameter(ParameterSetName = 'ManagedIdentity')]
        [string]$ClientId,

        [Parameter(ParameterSetName = 'AppCertificate')]
        [string]$CertificateThumbprint,

        [Parameter(ParameterSetName = 'AppCertificate')]
        [string]$CertificateSubjectName,

        [Parameter(ParameterSetName = 'AppCertificate')]
        [System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate,

        [Parameter(ParameterSetName = 'ClientSecret', Mandatory = $true)]
        [pscredential]$ClientSecretCredential,

        [Parameter(ParameterSetName = 'ManagedIdentity', Mandatory = $true)]
        [switch]$Identity,

        [Parameter(ParameterSetName = 'AccessToken', Mandatory = $true)]
        [securestring]$AccessToken,

        [Parameter(ParameterSetName = 'Delegated')]
        [Parameter(ParameterSetName = 'AppCertificate')]
        [Parameter(ParameterSetName = 'ClientSecret')]
        [Parameter(ParameterSetName = 'ManagedIdentity')]
        [Parameter(ParameterSetName = 'AccessToken')]
        [ValidateSet('Global', 'USGov', 'USGovDoD', 'China')]
        [string]$Environment = 'Global'
    )

    $connectParams = @{
        NoWelcome   = $true
        Environment = $Environment
    }

    $authType = switch ($PSCmdlet.ParameterSetName) {
        'Delegated' { 'Delegated' }
        'AppCertificate' { 'AppCertificate' }
        'ClientSecret' { 'ClientSecret' }
        'ManagedIdentity' { 'ManagedIdentity' }
        'AccessToken' { 'AccessToken' }
    }

    $requestedScopes = @()

    switch ($PSCmdlet.ParameterSetName) {
        'Delegated' {
            $defaultScopes = switch ($Scenario) {
                'ReadOnly' { @('ConfigurationMonitoring.Read.All') }
                'Manage' { @('ConfigurationMonitoring.ReadWrite.All') }
                'Setup' { @('ConfigurationMonitoring.ReadWrite.All', 'Application.ReadWrite.All', 'AppRoleAssignment.ReadWrite.All') }
            }

            $requestedScopes = @($defaultScopes)

            if ($IncludeDirectoryMetadata) {
                $requestedScopes += @(
                    'Organization.Read.All'
                    'Domain.Read.All'
                )
            }

            if ($Scopes) {
                $requestedScopes += $Scopes
            }

            $requestedScopes = $requestedScopes | Select-Object -Unique
            $connectParams['Scopes'] = $requestedScopes

            if ($TenantId) {
                $connectParams['TenantId'] = $TenantId
            }

            if ($UseDeviceCode) {
                $connectParams['UseDeviceCode'] = $true
            }

            Write-TBLog -Message ('Connecting to Microsoft Graph ({0}) using delegated auth with scopes: {1}' -f $Environment, ($requestedScopes -join ', '))
        }
        'AppCertificate' {
            if (-not ($CertificateThumbprint -or $CertificateSubjectName -or $Certificate)) {
                throw 'App certificate authentication requires -CertificateThumbprint, -CertificateSubjectName, or -Certificate.'
            }

            $connectParams['ClientId'] = $ClientId
            if ($TenantId) {
                $connectParams['TenantId'] = $TenantId
            }
            if ($CertificateThumbprint) {
                $connectParams['CertificateThumbprint'] = $CertificateThumbprint
            }
            if ($CertificateSubjectName) {
                $connectParams['CertificateSubjectName'] = $CertificateSubjectName
            }
            if ($Certificate) {
                $connectParams['Certificate'] = $Certificate
            }

            Write-TBLog -Message ('Connecting to Microsoft Graph ({0}) using app certificate auth for client {1}' -f $Environment, $ClientId)
        }
        'ClientSecret' {
            $connectParams['ClientSecretCredential'] = $ClientSecretCredential
            if ($TenantId) {
                $connectParams['TenantId'] = $TenantId
            }

            Write-TBLog -Message ('Connecting to Microsoft Graph ({0}) using client secret auth for client {1}' -f $Environment, $ClientSecretCredential.UserName)
        }
        'ManagedIdentity' {
            if ($Identity) {
                $connectParams['Identity'] = $true
            }
            if ($ClientId) {
                $connectParams['ClientId'] = $ClientId
            }

            Write-TBLog -Message ('Connecting to Microsoft Graph ({0}) using managed identity{1}' -f $Environment, $(if ($ClientId) { " client $ClientId" } else { '' }))
        }
        'AccessToken' {
            $connectParams['AccessToken'] = $AccessToken

            Write-TBLog -Message ('Connecting to Microsoft Graph ({0}) using a supplied access token' -f $Environment)
        }
    }

    try {
        Connect-MgGraph @connectParams
        $context = Get-MgContext

        $script:TBApiBaseUri = "$(Get-TBGraphBaseUri)/beta/admin/configurationManagement"

        if ($Environment -ne 'Global') {
            Write-TBLog -Message 'UTCM APIs are only available in the Global cloud; operations may fail.' -Level 'Warning'
        }

        $tenantDisplayName = $null
        $primaryDomain = $null
        if ($IncludeDirectoryMetadata) {
            try {
                $directoryMetadata = Get-TBDirectoryMetadata
                if ($directoryMetadata) {
                    $tenantDisplayName = $directoryMetadata.TenantDisplayName
                    $primaryDomain = $directoryMetadata.PrimaryDomain
                }
            }
            catch {
                Write-TBLog -Message ('Directory metadata enrichment failed: {0}' -f $_.Exception.Message) -Level 'Warning'
            }
        }

        $script:TBConnection = [PSCustomObject]@{
            TenantId                 = $context.TenantId
            Account                  = $context.Account
            Scopes                   = if ($context.Scopes) { $context.Scopes } else { $requestedScopes }
            ConnectedAt              = Get-Date
            DirectoryMetadataEnabled = [bool]($PSCmdlet.ParameterSetName -eq 'Delegated' -and $IncludeDirectoryMetadata)
            TenantDisplayName        = $tenantDisplayName
            PrimaryDomain            = $primaryDomain
            Environment              = $Environment
            AuthType                 = $authType
            ClientId                 = $ClientId
        }

        Write-TBLog -Message ('Connected to tenant {0} using {1} authentication{2}' -f $context.TenantId, $authType, $(if ($context.Account) { " as $($context.Account)" } else { '' }))
        Write-Output ('Connected to tenant {0}' -f $context.TenantId)
    }
    catch {
        Write-TBLog -Message ('Failed to connect: {0}' -f $_) -Level 'Error'
        throw
    }
}