Public/Get-CloudPCSnapshot.ps1

function Get-CloudPCSnapshot {
    <#
    .SYNOPSIS
        Returns snapshots for one or more Windows 365 Cloud PCs.

    .DESCRIPTION
        Calls the Microsoft Graph beta /deviceManagement/virtualEndpoint/cloudPCs/{id}/retrieveSnapshots
        endpoint and returns normalized WindowsCloudPC.Snapshot objects.

        The cmdlet accepts Cloud PC objects from Get-CloudPC through the pipeline
        or a Cloud PC ID through -Id. Use -All to retrieve every Cloud PC first
        and return snapshots with friendly Cloud PC names.

    .PARAMETER CloudPC
        A WindowsCloudPC.CloudPC object returned by Get-CloudPC, or a Cloud PC
        friendly name. Accepts pipeline input.

    .PARAMETER Id
        The Cloud PC ID when you do not have a CloudPC object available.

    .PARAMETER All
        Gets all Cloud PCs with Get-CloudPC, then returns snapshots for each one
        with CloudPcName populated from the Cloud PC object.

    .PARAMETER User
        Gets Cloud PCs for the specified user principal name, then returns
        snapshots for each one with CloudPcName populated from the Cloud PC object.

    .PARAMETER ResolveName
        Looks up the Cloud PC when using -Id so CloudPcName contains a friendly
        managed device name or display name instead of the ID.

    .EXAMPLE
        Get-CloudPCSnapshot -Id '8ab4e59b-1866-4ce9-8bc8-92856e61edf4'

        Lists snapshots for a single Cloud PC.

    .EXAMPLE
        Get-CloudPCSnapshot -Id '8ab4e59b-1866-4ce9-8bc8-92856e61edf4' -ResolveName |
            Format-Table CloudPcName,Status,SnapshotType,CreatedDateTime

        Lists snapshots and resolves the Cloud PC friendly name.

    .EXAMPLE
        Get-CloudPCSnapshot -CloudPC 'CFD-Vance-XS4KT'

        Looks up a Cloud PC by friendly name and lists its snapshots.

    .EXAMPLE
        Get-CloudPCSnapshot -User 'user@contoso.com' |
            Format-Table CloudPcName,Status,SnapshotType,CreatedDateTime

        Lists snapshots for Cloud PCs assigned to a user.

    .EXAMPLE
        Get-CloudPCSnapshot -All | Format-Table CloudPcName,Status,SnapshotType,CreatedDateTime

        Lists snapshots for every Cloud PC, including friendly Cloud PC names.

    .EXAMPLE
        Get-CloudPC | Get-CloudPCSnapshot | Format-Table CloudPcName,Status,SnapshotType,CreatedDateTime

        Lists snapshots for every Cloud PC returned by Get-CloudPC.

    .EXAMPLE
        Get-CloudPC -UserPrincipalName 'user@contoso.com' |
            Get-CloudPCSnapshot |
            Sort-Object CreatedDateTime -Descending

        Lists snapshots for a user's Cloud PCs.
    #>

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

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

        [Parameter(Mandatory, ParameterSetName = 'All')]
        [switch]$All,

        [Parameter(Mandatory, ParameterSetName = 'ByUser')]
        [Alias('UserPrincipalName','UPN')]
        [string]$User,

        [Parameter(ParameterSetName = 'ById')]
        [switch]$ResolveName
    )

    begin {
        Connect-CloudPC | Out-Null
    }

    process {
        if ($PSCmdlet.ParameterSetName -eq 'All') {
            Write-Verbose "Retrieving all Cloud PCs."
            $cloudPcs = @(Get-CloudPC)
            Write-Verbose "Found $($cloudPcs.Count) Cloud PC(s)."

            foreach ($pc in $cloudPcs) {
                $pcName = if ($pc.Name) { $pc.Name } else { $pc.Id }
                Write-Verbose "Retrieving snapshots for Cloud PC '$pcName' ($($pc.Id))."
                $snapshots = @($pc | Get-CloudPCSnapshot)
                Write-Verbose "Found $($snapshots.Count) snapshot(s) for Cloud PC '$pcName' ($($pc.Id))."
                $snapshots
            }
            return
        }

        if ($PSCmdlet.ParameterSetName -eq 'ByUser') {
            Write-Verbose "Retrieving Cloud PCs for user '$User'."
            $cloudPcs = @(Get-CloudPC -UserPrincipalName $User)
            Write-Verbose "Found $($cloudPcs.Count) Cloud PC(s) for user '$User'."

            foreach ($pc in $cloudPcs) {
                $pcName = if ($pc.Name) { $pc.Name } else { $pc.Id }
                Write-Verbose "Retrieving snapshots for Cloud PC '$pcName' ($($pc.Id))."
                $snapshots = @($pc | Get-CloudPCSnapshot)
                Write-Verbose "Found $($snapshots.Count) snapshot(s) for Cloud PC '$pcName' ($($pc.Id))."
                $snapshots
            }
            return
        }

        if ($PSCmdlet.ParameterSetName -eq 'ByObject') {
            if ($CloudPC -is [string]) {
                Write-Verbose "Resolving Cloud PC name '$CloudPC'."
                $matches = @(Get-CloudPC | Where-Object {
                    $_.Name -eq $CloudPC -or
                    $_.Id -eq $CloudPC -or
                    $_.ManagedDeviceId -eq $CloudPC -or
                    $_.AadDeviceId -eq $CloudPC -or
                    $_.AssignedUserUpn -eq $CloudPC
                })

                if ($matches.Count -eq 0) {
                    Write-Error "Get-CloudPCSnapshot: Cloud PC '$CloudPC' was not found. Pipe from Get-CloudPC, use -Id, or use -All."
                    return
                }

                if ($matches.Count -gt 1) {
                    Write-Error "Get-CloudPCSnapshot: Cloud PC '$CloudPC' matched $($matches.Count) Cloud PCs. Pipe the exact object from Get-CloudPC or use -Id."
                    return
                }

                $resolvedCloudPc = $matches[0]
                $cloudPcId = $resolvedCloudPc.Id
                $cloudPcName = if ($resolvedCloudPc.Name) { $resolvedCloudPc.Name } else { $resolvedCloudPc.Id }
                Write-Verbose "Resolved Cloud PC '$CloudPC' to '$cloudPcName' ($cloudPcId)."
            }
            else {
                $cloudPcId = $CloudPC.Id
                $cloudPcName = if ($CloudPC.Name) { $CloudPC.Name } else { $CloudPC.Id }
            }
        }
        else {
            $cloudPcId = $Id
            $cloudPcName = $Id
        }

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

        if ($ResolveName -and $PSCmdlet.ParameterSetName -eq 'ById') {
            $escapedNameLookupId = [uri]::EscapeDataString($cloudPcId)
            $nameLookupSelect = [uri]::EscapeDataString('id,managedDeviceName,displayName')
            $nameLookupUri = 'https://graph.microsoft.com/beta/deviceManagement/virtualEndpoint/cloudPCs/' +
                             $escapedNameLookupId +
                             '?$select=' +
                             $nameLookupSelect

            try {
                Write-Verbose "Resolving friendly name for Cloud PC '$cloudPcId'."
                $cloudPc = Invoke-MgGraphRequest -Method GET -Uri $nameLookupUri
                if ($cloudPc.managedDeviceName) {
                    $cloudPcName = $cloudPc.managedDeviceName
                }
                elseif ($cloudPc.displayName) {
                    $cloudPcName = $cloudPc.displayName
                }
                Write-Verbose "Resolved Cloud PC '$cloudPcId' to '$cloudPcName'."
            }
            catch {
                Write-Warning "Get-CloudPCSnapshot: name lookup failed for $cloudPcId -- $($_.Exception.Message)"
            }
        }

        $select = @(
            'id',
            'cloudPcId',
            'status',
            'createdDateTime',
            'lastRestoredDateTime',
            'snapshotType',
            'expirationDateTime',
            'healthCheckStatus'
        ) -join ','

        $escapedCloudPcId = [uri]::EscapeDataString($cloudPcId)
        $uri = 'https://graph.microsoft.com/beta/deviceManagement/virtualEndpoint/cloudPCs/' +
               $escapedCloudPcId +
               '/retrieveSnapshots?$select=' +
               [uri]::EscapeDataString($select)

        try {
            Write-Verbose "Querying snapshots for Cloud PC '$cloudPcName' ($cloudPcId)."
            $resp = Invoke-MgGraphRequest -Method GET -Uri $uri
        }
        catch {
            Write-Error -Message "Get-CloudPCSnapshot: query failed for $cloudPcName ($cloudPcId) -- $($_.Exception.Message)" -Exception $_.Exception
            return
        }

        $snapshots = @($resp.value)
        Write-Verbose "Found $($snapshots.Count) snapshot(s) for Cloud PC '$cloudPcName' ($cloudPcId)."

        $snapshots |
            Sort-Object -Property @{ Expression = { if ($_.createdDateTime) { [datetime]$_.createdDateTime } else { [datetime]::MinValue } }; Descending = $true } |
            ForEach-Object {
                $snapshot = $_

                [pscustomobject]@{
                    PSTypeName           = 'WindowsCloudPC.Snapshot'
                    Id                   = $snapshot.id
                    SnapshotId           = $snapshot.id
                    CloudPcId            = if ($snapshot.cloudPcId) { $snapshot.cloudPcId } else { $cloudPcId }
                    CloudPcName          = $cloudPcName
                    Status               = $snapshot.status
                    SnapshotType         = $snapshot.snapshotType
                    CreatedDateTime      = if ($snapshot.createdDateTime) { ([datetime]$snapshot.createdDateTime).ToLocalTime() } else { $null }
                    LastRestoredDateTime = if ($snapshot.lastRestoredDateTime) { ([datetime]$snapshot.lastRestoredDateTime).ToLocalTime() } else { $null }
                    ExpirationDateTime   = if ($snapshot.expirationDateTime) { ([datetime]$snapshot.expirationDateTime).ToLocalTime() } else { $null }
                    HealthCheckStatus    = $snapshot.healthCheckStatus
                    Raw                  = $snapshot
                }
            }
    }

    end { }
}