Public/Get-CloudPCLaunchDetail.ps1

function Get-CloudPCLaunchDetail {
    <#
    .SYNOPSIS
        Gets launch details for one or more Windows 365 Cloud PCs.

    .DESCRIPTION
        Calls the Microsoft Graph v1.0 retrieveCloudPcLaunchDetail function for a Cloud PC.
        The response includes the Cloud PC launch URL and Windows 365 Switch compatibility
        details. When a username is available, the output also includes a Windows App
        launch URI using the ms-cloudpc:connect protocol. If -UserId is omitted, the
        cmdlet uses the signed-in Graph account as the launch username when available.

        Cloud PCs that are still provisioning might not have launch details yet. In that
        case, Graph can return 404 NotFound. The cmdlet emits a normal result row with
        LaunchDetailStatus = 'Unavailable' instead of writing an error.

        By default, the cmdlet queries /me/cloudPCs/{id}/retrieveCloudPcLaunchDetail. Use
        -UserId to query /users/{userId}/cloudPCs/{id}/retrieveCloudPcLaunchDetail instead.

    .PARAMETER CloudPC
        A WindowsCloudPC.CloudPC object (as returned by Get-CloudPC). Accepts pipeline input.

    .PARAMETER Id
        The Cloud PC ID (GUID) when you do not have a CloudPC object handy.

    .PARAMETER UserId
        Optional user ID or UPN for the /users/{userId}/cloudPCs/{id}/retrieveCloudPcLaunchDetail
        form. If omitted, the cmdlet uses /me/cloudPCs/{id}/retrieveCloudPcLaunchDetail and uses
        the signed-in Graph account for the generated Windows App launch URI.

    .EXAMPLE
        Get-CloudPCLaunchDetail -Id 'a20d556d-85f7-88cc-bb9c-08d9902bb7bb'

        Gets launch details for a Cloud PC that belongs to the signed-in user. The generated
        Windows App launch URI uses the signed-in Graph account as the username.

    .EXAMPLE
        Get-CloudPCLaunchDetail -Id 'a20d556d-85f7-88cc-bb9c-08d9902bb7bb' -UserId 'user@contoso.com'

        Gets launch details by using the /users/{userId}/cloudPCs/{id} endpoint form.

    .EXAMPLE
        Get-CloudPC | Get-CloudPCLaunchDetail | Format-Table CloudPcName,Windows365SwitchCompatible,WindowsAppLaunchUri

        Gets launch details for Cloud PCs returned by Get-CloudPC.
    #>

    [CmdletBinding(DefaultParameterSetName = 'ByObject')]
    [OutputType('WindowsCloudPC.CloudPCLaunchDetail')]
    param(
        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ByObject')]
        [PSTypeName('WindowsCloudPC.CloudPC')]
        [object]$CloudPC,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'ById')]
        [Alias('CloudPcId')]
        [string]$Id,

        [Parameter(ValueFromPipelineByPropertyName)]
        [Alias('AssignedUserUpn','UserPrincipalName')]
        [string]$UserId
    )

    begin {
        $graphContext = Connect-CloudPC
    }

    process {
        if ($PSCmdlet.ParameterSetName -eq 'ByObject') {
            $cloudPcId   = $CloudPC.Id
            $cloudPcName = if ($CloudPC.Name) { $CloudPC.Name } else { $CloudPC.Id }
            if (-not $PSBoundParameters.ContainsKey('UserId') -and $CloudPC.AssignedUserUpn) {
                $UserId = $CloudPC.AssignedUserUpn
            }
        }
        else {
            $cloudPcId   = $Id
            $cloudPcName = $Id
        }

        if (-not $cloudPcId) {
            Write-Error "Get-CloudPCLaunchDetail: Cloud PC Id is empty; nothing to query."
            return
        }

        $escapedCloudPcId = [uri]::EscapeDataString($cloudPcId)
        $launchUsername = if ([string]::IsNullOrWhiteSpace($UserId)) {
            if ($graphContext -and -not [string]::IsNullOrWhiteSpace($graphContext.Account)) {
                $graphContext.Account
            }
            else {
                $null
            }
        }
        else {
            $UserId
        }
        if ([string]::IsNullOrWhiteSpace($UserId)) {
            $uri = "https://graph.microsoft.com/v1.0/me/cloudPCs/$escapedCloudPcId/retrieveCloudPcLaunchDetail"
        }
        else {
            $escapedUserId = [uri]::EscapeDataString($UserId)
            $uri = "https://graph.microsoft.com/v1.0/users/$escapedUserId/cloudPCs/$escapedCloudPcId/retrieveCloudPcLaunchDetail"
        }

        try {
            $resp = Invoke-MgGraphRequest -Method GET -Uri $uri
        }
        catch {
            $errorMessage = $_.Exception.Message
            if ($errorMessage -match '\bNotFound\b|\b404\b') {
                [pscustomobject]@{
                    PSTypeName                                      = 'WindowsCloudPC.CloudPCLaunchDetail'
                    CloudPcId                                       = $cloudPcId
                    CloudPcName                                     = $cloudPcName
                    UserId                                          = if ([string]::IsNullOrWhiteSpace($UserId)) { 'me' } else { $UserId }
                    CloudPcLaunchUrl                                = $null
                    WindowsAppLaunchUri                             = $null
                    Windows365SwitchCompatible                      = $null
                    Windows365SwitchCompatibilityFailureReasonType   = $null
                    LaunchDetailStatus                              = 'Unavailable'
                    ErrorMessage                                    = $errorMessage
                    Raw                                             = $null
                }
                return
            }

            Write-Error -Message "Get-CloudPCLaunchDetail: query failed for $cloudPcName ($cloudPcId) -- $errorMessage" -Exception $_.Exception
            return
        }

        $windowsAppLaunchUri = $null
        if ($resp.cloudPcLaunchUrl -and -not [string]::IsNullOrWhiteSpace($launchUsername)) {
            $queryParts = [ordered]@{
                cpcid       = if ($resp.cloudPcId) { $resp.cloudPcId } else { $cloudPcId }
                username    = $launchUsername
                environment = 'PROD'
                source      = 'IWP'
                rdlaunchurl = $resp.cloudPcLaunchUrl
            }

            $encodedQuery = foreach ($entry in $queryParts.GetEnumerator()) {
                '{0}={1}' -f $entry.Key, [uri]::EscapeDataString([string]$entry.Value)
            }
            $windowsAppLaunchUri = 'ms-cloudpc:connect?' + ($encodedQuery -join '&')
        }

        [pscustomobject]@{
            PSTypeName                                      = 'WindowsCloudPC.CloudPCLaunchDetail'
            CloudPcId                                       = if ($resp.cloudPcId) { $resp.cloudPcId } else { $cloudPcId }
            CloudPcName                                     = $cloudPcName
            UserId                                          = if ([string]::IsNullOrWhiteSpace($UserId)) { 'me' } else { $UserId }
            CloudPcLaunchUrl                                = $resp.cloudPcLaunchUrl
            WindowsAppLaunchUri                             = $windowsAppLaunchUri
            Windows365SwitchCompatible                      = $resp.windows365SwitchCompatible
            Windows365SwitchCompatibilityFailureReasonType   = $resp.windows365SwitchCompatibilityFailureReasonType
            LaunchDetailStatus                              = 'Available'
            ErrorMessage                                    = $null
            Raw                                             = $resp
        }
    }

    end { }
}