Public/Resize-CloudPC.ps1
|
function Resize-CloudPC { <# .SYNOPSIS Resizes one or more Windows 365 Cloud PCs to a target service plan. .DESCRIPTION Issues POST /deviceManagement/virtualEndpoint/cloudPCs/{id}/resize against Microsoft Graph v1.0 to upgrade or downgrade a Cloud PC to a target service plan with a different vCPU and storage configuration. Graph returns 204 No Content when the asynchronous resize request is accepted. When -UseMaintenanceWindow is specified, the cmdlet creates a Microsoft Graph beta cloudPcBulkResize action instead: POST /deviceManagement/virtualEndpoint/bulkActions with scheduledDuringMaintenanceWindow set to true. This routes one or more Cloud PCs through assigned Cloud PC maintenance windows. The resize action requires the CloudPC.ReadWrite.All scope. No Microsoft.Graph.DeviceManagement.Administration module dependency is required because WindowsCloudPC sends the request through Invoke-MgGraphRequest from Microsoft.Graph.Authentication. The target service plan can be supplied by ID, exact display name, or a WindowsCloudPC.ServicePlan object returned by Get-CloudPCServicePlan. .PARAMETER CloudPC A WindowsCloudPC.CloudPC object returned by Get-CloudPC, or an exact Cloud PC name, Cloud PC ID, managed device ID, Azure AD device ID, or assigned user principal name. Accepts pipeline input. .PARAMETER Id The Cloud PC ID when you do not have a CloudPC object available. .PARAMETER TargetServicePlanId The target Windows 365 service plan ID. Alias: ServicePlanId. .PARAMETER TargetServicePlanName The exact display name of the target Windows 365 service plan. The cmdlet resolves it with Get-CloudPCServicePlan before sending the resize request. .PARAMETER TargetServicePlan A WindowsCloudPC.ServicePlan object returned by Get-CloudPCServicePlan. .PARAMETER Force Suppress confirmation prompts. Equivalent to -Confirm:$false. .PARAMETER UseMaintenanceWindow Create a Microsoft Graph beta cloudPcBulkResize action and schedule it according to assigned Cloud PC maintenance windows. Pipeline input is collected and submitted as one bulk action. .PARAMETER PassThru Emit a WindowsCloudPC.ResizeResult object describing the accepted resize request. By default the cmdlet is silent on success. .EXAMPLE Get-CloudPC -Name 'CPC-BRAD-01' | Resize-CloudPC -TargetServicePlanName 'Cloud PC Enterprise 4vCPU/16GB/128GB' -WhatIf Previews resizing a Cloud PC to a target service plan by exact display name. .EXAMPLE Resize-CloudPC -Id 'b0a9cde2-e170-4dd9-97c3-ad1d3328a711' ` -TargetServicePlanId '30d0e128-de93-41dc-89ec-33d84bb662a0' ` -Force ` -PassThru Sends a resize request for a single Cloud PC by ID and emits the request result. .EXAMPLE $plan = Get-CloudPCServicePlan -DisplayName 'Cloud PC Enterprise 8vCPU/32GB/256GB' Get-CloudPC -Type Dedicated | Resize-CloudPC -TargetServicePlan $plan -WhatIf Resolves a target service plan object once, then previews resizing every dedicated Cloud PC. .EXAMPLE Resize-CloudPC -CloudPC 'CPC-ENT-0M94O' ` -ServicePlanId '9ecf691d-8b82-46cb-b254-cd061b2c02fb' ` -UseMaintenanceWindow ` -PassThru Submits a single Cloud PC resize as a bulk resize action that uses assigned maintenance windows. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High', DefaultParameterSetName = 'ByObject')] [OutputType('WindowsCloudPC.ResizeResult')] param( [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ByObject')] [object]$CloudPC, [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'ById')] [Alias('CloudPcId')] [string]$Id, [Alias('ServicePlanId')] [string]$TargetServicePlanId, [Alias('ServicePlanName','TargetSku','Sku')] [string]$TargetServicePlanName, [PSTypeName('WindowsCloudPC.ServicePlan')] [object]$TargetServicePlan, [switch]$Force, [switch]$UseMaintenanceWindow, [switch]$PassThru ) begin { if ($Force -and -not $PSBoundParameters.ContainsKey('Confirm')) { $ConfirmPreference = 'None' } Connect-CloudPC -AdditionalScopes 'CloudPC.ReadWrite.All' | Out-Null $resolvedTargetPlanId = $null $resolvedTargetPlanName = $null $maintenanceWindowTargets = [System.Collections.Generic.List[object]]::new() if ($PSBoundParameters.ContainsKey('TargetServicePlan')) { if (-not $TargetServicePlan.Id) { throw 'Resize-CloudPC: TargetServicePlan must include an Id property.' } $resolvedTargetPlanId = $TargetServicePlan.Id $resolvedTargetPlanName = if ($TargetServicePlan.DisplayName) { $TargetServicePlan.DisplayName } else { $TargetServicePlan.Id } } elseif ($PSBoundParameters.ContainsKey('TargetServicePlanId')) { if ([string]::IsNullOrWhiteSpace($TargetServicePlanId)) { throw 'Resize-CloudPC: TargetServicePlanId is empty.' } $resolvedTargetPlanId = $TargetServicePlanId $resolvedTargetPlanName = $TargetServicePlanId } elseif ($PSBoundParameters.ContainsKey('TargetServicePlanName')) { if ([string]::IsNullOrWhiteSpace($TargetServicePlanName)) { throw 'Resize-CloudPC: TargetServicePlanName is empty.' } $matches = @(Get-CloudPCServicePlan -DisplayName $TargetServicePlanName) if ($matches.Count -eq 0) { throw "Resize-CloudPC: Target service plan '$TargetServicePlanName' was not found." } if ($matches.Count -gt 1) { throw "Resize-CloudPC: Target service plan '$TargetServicePlanName' matched more than one service plan. Use -TargetServicePlanId." } $resolvedTargetPlanId = $matches[0].Id $resolvedTargetPlanName = $matches[0].DisplayName } else { throw 'Resize-CloudPC: Supply -TargetServicePlanId, -TargetServicePlanName, or -TargetServicePlan.' } } process { try { $targetPc = if ($PSCmdlet.ParameterSetName -eq 'ById') { Resolve-CloudPCTarget -Id $Id -CommandName 'Resize-CloudPC' } else { Resolve-CloudPCTarget -CloudPC $CloudPC -CommandName 'Resize-CloudPC' } } catch { Write-Error -ErrorRecord $_ return } $target = "Cloud PC '$($targetPc.Name)' ($($targetPc.Id))" $action = if ($UseMaintenanceWindow) { "Schedule resize to service plan '$resolvedTargetPlanName' ($resolvedTargetPlanId) through maintenance windows" } else { "Resize to service plan '$resolvedTargetPlanName' ($resolvedTargetPlanId)" } $requestedAt = [datetime]::Now $status = 'Accepted' $errorMessage = $null if (-not $PSCmdlet.ShouldProcess($target, $action)) { if ($PassThru) { [pscustomobject]@{ PSTypeName = 'WindowsCloudPC.ResizeResult' CloudPcId = $targetPc.Id CloudPcName = $targetPc.Name TargetServicePlanId = $resolvedTargetPlanId TargetServicePlanName = $resolvedTargetPlanName Status = 'WhatIf' RequestedAt = $null ErrorMessage = $null } } return } if ($UseMaintenanceWindow) { $maintenanceWindowTargets.Add($targetPc) return } $escapedCloudPcId = [uri]::EscapeDataString($targetPc.Id) $uri = "https://graph.microsoft.com/v1.0/deviceManagement/virtualEndpoint/cloudPCs/$escapedCloudPcId/resize" $body = @{ targetServicePlanId = $resolvedTargetPlanId } | ConvertTo-Json -Depth 4 -Compress try { Invoke-MgGraphRequest -Method POST -Uri $uri -Body $body -ContentType 'application/json' | Out-Null Write-Verbose "Resize-CloudPC: resize accepted for $target" } catch { $status = 'Failed' $errorMessage = $_.Exception.Message if ($_.ErrorDetails -and $_.ErrorDetails.Message) { $errorMessage = "$errorMessage $($_.ErrorDetails.Message)" } Write-Error -Message "Resize-CloudPC: resize failed for $target - $errorMessage" -Exception $_.Exception } if ($PassThru) { [pscustomobject]@{ PSTypeName = 'WindowsCloudPC.ResizeResult' CloudPcId = $targetPc.Id CloudPcName = $targetPc.Name TargetServicePlanId = $resolvedTargetPlanId TargetServicePlanName = $resolvedTargetPlanName Status = $status RequestedAt = $requestedAt ErrorMessage = $errorMessage } } } end { if (-not $UseMaintenanceWindow -or $maintenanceWindowTargets.Count -eq 0) { return } $cloudPcIds = @($maintenanceWindowTargets | ForEach-Object { $_.Id }) $body = @{ '@odata.type' = '#microsoft.graph.cloudPcBulkResize' displayName = "Resize Cloud PCs to $resolvedTargetPlanName" cloudPcIds = $cloudPcIds targetServicePlanId = $resolvedTargetPlanId scheduledDuringMaintenanceWindow = $true } | ConvertTo-Json -Depth 5 -Compress $uri = 'https://graph.microsoft.com/beta/deviceManagement/virtualEndpoint/bulkActions' $status = 'Accepted' $errorMessage = $null $requestedAt = [datetime]::Now $bulkAction = $null try { $bulkAction = Invoke-MgGraphRequest -Method POST -Uri $uri -Body $body -ContentType 'application/json' Write-Verbose "Resize-CloudPC: maintenance-window bulk resize accepted for $($cloudPcIds.Count) Cloud PC(s)" if ($bulkAction.status) { $status = $bulkAction.status } } catch { $status = 'Failed' $errorMessage = $_.Exception.Message if ($_.ErrorDetails -and $_.ErrorDetails.Message) { $errorMessage = "$errorMessage $($_.ErrorDetails.Message)" } Write-Error -Message "Resize-CloudPC: maintenance-window bulk resize failed for $($cloudPcIds.Count) Cloud PC(s) - $errorMessage" -Exception $_.Exception } if ($PassThru) { foreach ($targetPc in $maintenanceWindowTargets) { [pscustomobject]@{ PSTypeName = 'WindowsCloudPC.ResizeResult' CloudPcId = $targetPc.Id CloudPcName = $targetPc.Name TargetServicePlanId = $resolvedTargetPlanId TargetServicePlanName = $resolvedTargetPlanName Status = $status RequestedAt = $requestedAt ErrorMessage = $errorMessage UseMaintenanceWindow = $true ScheduledDuringMaintenanceWindow = $true BulkActionId = $bulkAction.id RawBulkAction = $bulkAction } } } } } |