functions/Switch-BapTenant.ps1


<#
    .SYNOPSIS
        Switches the current context to a specified BAP tenant.
         
    .DESCRIPTION
        This function allows you to switch the current context to a specified BAP tenant based on the tenant details stored in the local PSFramework configuration.
         
    .PARAMETER Id
        The ID of the BAP tenant to switch to.
         
    .PARAMETER Force
        Instruct the function to force an authentication prompt even if the current token is still valid. This can be useful if you want to ensure that you are using the most up-to-date credentials or if you want to switch to a different user within the same tenant.
         
    .EXAMPLE
        PS C:\> Switch-BapTenant -Id "Contoso"
         
        This will switch the current context to the BAP tenant with the id "Contoso".
        It will ensure that the authentication token is valid, prompting for re-authentication if necessary.
         
    .EXAMPLE
        PS C:\> Switch-BapTenant -Id "Contoso" -Force
         
        This will switch the current context to the BAP tenant with the id "Contoso".
        It will force an authentication prompt, even if the current token is still valid.
         
    .NOTES
        Author: Mötz Jensen (@Splaxi)
#>

function Switch-BapTenant {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string] $Id,

        [switch] $Force
    )

    begin {
        
    }
    
    process {
        $hashTenants = [hashtable](Get-PSFConfigValue -FullName "d365bap.tools.tenant.details")
        
        if ($null -eq $hashTenants."$Id") {
            $messageString = "No tenant details found for Id <c='em'>$Id</c>. Please add the tenant details using <c='em'>Set-BapTenantDetail</c> first."

            Write-PSFMessage -Level Important -Message $messageString
            Stop-PSFFunction -Message "Stopping because tenant was NOT found based on the id." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', '')))
            return
        }
        
        $obj = $hashTenants."$Id"
        $contextObj = (Get-AzContext -ListAvailable | `
                Where-Object { $_.tenant.id -eq $obj.Tenant } | `
                Where-Object { $_.Account.Id -eq $obj.User } | `
                Select-Object -First 1)

        Select-AzContext -InputObject $contextObj > $null

        $fake = (Get-AzAccessToken `
                -ResourceUrl "https://service.powerapps.com/" `
                -AsSecureString -ErrorAction SilentlyContinue).Token
        
        if ([string]::IsNullOrWhiteSpace($fake) -or $Force) {
            if ([string]::IsNullOrWhiteSpace($fake)) {
                Write-PSFMessage -Level Important -Message "It seems that your credentials/cache has <c='sub'>expired</c>. Will force an authentication prompt for the <c='em'>$($obj.User)</c>."
            }
            else {
                Write-PSFMessage -Level Verbose -Message "Force flag is set. Will force an authentication prompt for the <c='em'>$($obj.User)</c>."
            }
            
            Start-Sleep -Seconds 2

            # Subscription selection is irrelevant for BAP tenant auth (Power Platform tokens).
            # Process-scoped LoginExperienceV2 Off avoids the interactive tenant/subscription picker.
            # Warning (3) and Information (6) streams are discarded so Connect-AzAccount does not print:
            # "Please select the account...", "Retrieving subscriptions...", or DefaultSubscriptionForLogin guidance.
            $loginExperienceOverridden = $false
            $previousWarningPreference = $WarningPreference
            $previousInformationPreference = $InformationPreference
            try {
                $currentLoginExperience = (Get-AzConfig -LoginExperienceV2 -ErrorAction SilentlyContinue).Value
                if ("$currentLoginExperience" -ne 'Off') {
                    $null = Update-AzConfig -LoginExperienceV2 Off -Scope Process -ErrorAction SilentlyContinue
                    $loginExperienceOverridden = $true
                }

                $WarningPreference = 'SilentlyContinue'
                $InformationPreference = 'SilentlyContinue'

                $connectParams = @{
                    Tenant                = $obj.Tenant
                    AccountId             = $obj.User
                    SkipContextPopulation = $true
                    WarningAction         = 'SilentlyContinue'
                    InformationAction     = 'SilentlyContinue'
                }

                # Prefer an already-known subscription so Az skips multi-subscription selection logic.
                $knownSubscriptionId = $null
                if ($null -ne $contextObj -and $null -ne $contextObj.Subscription) {
                    $knownSubscriptionId = $contextObj.Subscription.Id
                }
                if (-not [string]::IsNullOrWhiteSpace($knownSubscriptionId)) {
                    $connectParams['Subscription'] = $knownSubscriptionId
                }

                # 3 = Warning stream, 6 = Information stream (Az login status lines).
                $null = Connect-AzAccount @connectParams 3>$null 6>$null
            }
            finally {
                $WarningPreference = $previousWarningPreference
                $InformationPreference = $previousInformationPreference

                if ($loginExperienceOverridden) {
                    $null = Clear-AzConfig -LoginExperienceV2 -Scope Process -ErrorAction SilentlyContinue
                }
            }
        }

        Register-PSFTaskEngineTask -Name EnvironmentRefresh -Interval (New-TimeSpan -Minutes 15) -ResetTask -ScriptBlock {
            Set-PSFTaskEngineCache -Module d365bap.tools -Name Environments -Value (Get-BapEnvironment -FscmEnabled:$FscmEnabled).EnvName
        }
    }
    
    end {
        
    }
}