Public/Reset-CloudPCLocalAdminPassword.ps1

function Reset-CloudPCLocalAdminPassword {
    <#
    .SYNOPSIS
        Rotates the local admin password for one or more Windows 365 Cloud PCs.

    .DESCRIPTION
        Issues POST /deviceManagement/managedDevices('{managedDeviceId}')/rotateLocalAdminPassword
        against Microsoft Graph beta, which initiates a manual local admin password rotation on
        the underlying Intune managed device.

        The cmdlet accepts Cloud PC objects from Get-CloudPC, exact Cloud PC names, Cloud PC IDs,
        or managed device IDs. Use -ManagedDeviceId when you already have the Intune managedDevice ID.
        It supports -WhatIf / -Confirm and defaults to ConfirmImpact = 'High'. Use -Force to suppress
        the confirmation prompt in automation.

        Requires the DeviceManagementManagedDevices.PrivilegedOperations.All scope; the cmdlet
        automatically re-authenticates via Connect-CloudPC if the current Graph session does not
        already have it.

    .PARAMETER CloudPC
        A WindowsCloudPC.CloudPC object (as returned by Get-CloudPC), or an exact Cloud PC name,
        Cloud PC ID, or managed device ID. Accepts pipeline input.

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

    .PARAMETER ManagedDeviceId
        The Intune managedDevice ID to rotate the local admin password for directly.

    .PARAMETER Force
        Suppress the confirmation prompt. Equivalent to -Confirm:$false.

    .PARAMETER PassThru
        Emit a WindowsCloudPC.LocalAdminPasswordRotationResult object describing the outcome of each request.
        By default the cmdlet is silent on success.

    .EXAMPLE
        Reset-CloudPCLocalAdminPassword -CloudPC 'CPC-brad-U2O0S' -Force -PassThru

        Resolves a Cloud PC by exact name and rotates the local admin password for the underlying managed device.

    .EXAMPLE
        Reset-CloudPCLocalAdminPassword -Id 'f55ba1ae-4d31-4b41-a19f-5ca6fd5d8ffe' -Force -PassThru

        Resolves a Cloud PC by ID, then rotates the local admin password for its Intune managed device.

    .EXAMPLE
        Reset-CloudPCLocalAdminPassword -ManagedDeviceId 'bbfae1fc-af9b-4621-9477-454ee0afe22b' -Force -PassThru

        Sends the rotation request directly to an Intune managedDevice ID.
    #>

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

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

        [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'ByManagedDeviceId')]
        [Alias('IntuneManagedDeviceId')]
        [string]$ManagedDeviceId,

        [switch]$Force,

        [switch]$PassThru
    )

    begin {
        if ($Force -and -not $PSBoundParameters.ContainsKey('Confirm')) {
            $ConfirmPreference = 'None'
        }
        Connect-CloudPC -AdditionalScopes 'DeviceManagementManagedDevices.PrivilegedOperations.All' | Out-Null
    }

    process {
        if ($PSCmdlet.ParameterSetName -eq 'ByManagedDeviceId') {
            $cloudPcId       = $null
            $cloudPcName     = $ManagedDeviceId
            $managedDeviceId = $ManagedDeviceId
        }
        elseif ($PSCmdlet.ParameterSetName -eq 'ById') {
            $cloudPcMatches = @(Get-CloudPC | Where-Object { $_.Id -eq $Id })
            if ($cloudPcMatches.Count -eq 0) {
                Write-Error "Reset-CloudPCLocalAdminPassword: Cloud PC Id '$Id' was not found. Pass a Cloud PC object from Get-CloudPC, an exact Cloud PC name, or -ManagedDeviceId."
                return
            }
            if ($cloudPcMatches.Count -gt 1) {
                Write-Error "Reset-CloudPCLocalAdminPassword: Cloud PC Id '$Id' matched more than one object. Pipe the exact object from Get-CloudPC or use -ManagedDeviceId."
                return
            }

            $cloudPcId       = $cloudPcMatches[0].Id
            $cloudPcName     = if ($cloudPcMatches[0].Name) { $cloudPcMatches[0].Name } else { $cloudPcMatches[0].Id }
            $managedDeviceId = $cloudPcMatches[0].ManagedDeviceId
        }
        else {
            if ($CloudPC -is [string]) {
                if ([string]::IsNullOrWhiteSpace($CloudPC)) {
                    Write-Error "Reset-CloudPCLocalAdminPassword: Cloud PC name, Cloud PC Id, or managed device Id is empty; nothing to rotate."
                    return
                }

                $cloudPcMatches = @(Get-CloudPC | Where-Object { $_.Id -eq $CloudPC -or $_.Name -eq $CloudPC -or $_.ManagedDeviceId -eq $CloudPC })
                if ($cloudPcMatches.Count -eq 0) {
                    Write-Error "Reset-CloudPCLocalAdminPassword: Cloud PC '$CloudPC' was not found. Pass a Cloud PC object from Get-CloudPC, an exact Cloud PC name, a Cloud PC Id, or -ManagedDeviceId."
                    return
                }
                if ($cloudPcMatches.Count -gt 1) {
                    Write-Error "Reset-CloudPCLocalAdminPassword: Cloud PC '$CloudPC' matched more than one object. Pipe the exact object from Get-CloudPC or use -ManagedDeviceId."
                    return
                }

                $cloudPcId       = $cloudPcMatches[0].Id
                $cloudPcName     = if ($cloudPcMatches[0].Name) { $cloudPcMatches[0].Name } else { $cloudPcMatches[0].Id }
                $managedDeviceId = $cloudPcMatches[0].ManagedDeviceId
            }
            else {
                $cloudPcId       = $CloudPC.Id
                $cloudPcName     = if ($CloudPC.Name) { $CloudPC.Name } else { $CloudPC.Id }
                $managedDeviceId = $CloudPC.ManagedDeviceId
            }
        }

        if (-not $managedDeviceId) {
            Write-Error "Reset-CloudPCLocalAdminPassword: managed device Id is empty for Cloud PC '$cloudPcName'; nothing to rotate."
            return
        }

        $target = "Cloud PC '$cloudPcName' managed device ($managedDeviceId)"

        if (-not $PSCmdlet.ShouldProcess($target, 'Rotate local admin password')) { return }

        $escapedManagedDeviceId = [uri]::EscapeDataString($managedDeviceId)
        $uri          = "https://graph.microsoft.com/beta/deviceManagement/managedDevices('$escapedManagedDeviceId')/rotateLocalAdminPassword"
        $status       = 'Accepted'
        $errorMessage = $null

        try {
            Invoke-MgGraphRequest -Method POST -Uri $uri | Out-Null
            Write-Verbose "Reset-CloudPCLocalAdminPassword: rotation accepted for $target"
        }
        catch {
            $status       = 'Failed'
            $errorMessage = $_.Exception.Message
            if ($_.ErrorDetails.Message) {
                $errorMessage = "$errorMessage $($_.ErrorDetails.Message)"
            }
            Write-Error -Message "Reset-CloudPCLocalAdminPassword: rotation failed for $target - $errorMessage" -Exception $_.Exception
        }

        if ($PassThru) {
            [pscustomobject]@{
                PSTypeName      = 'WindowsCloudPC.LocalAdminPasswordRotationResult'
                CloudPcId       = $cloudPcId
                CloudPcName     = $cloudPcName
                ManagedDeviceId = $managedDeviceId
                Status          = $status
                RequestedAt     = [datetime]::Now
                ErrorMessage    = $errorMessage
            }
        }
    }

    end { }
}