Private/Set-Win32ToolkitGuestAutoLogon.ps1

function Set-Win32ToolkitGuestAutoLogon {
    <#
    .SYNOPSIS
        Configures Windows AutoLogon in the guest (Winlogon registry) over PowerShell Direct.
    .DESCRIPTION
        Safety net for the interactive Hyper-V test mode: with AutoLogon configured, any BOOT of the
        guest lands directly on the logged-in `w32admin` desktop — so if a checkpoint (or a reboot) ever
        shows the login screen, the guest can recover to a usable desktop without anyone typing a
        password (see Confirm-Win32ToolkitGuestDesktop, which reboots to trigger this). Sets
        AutoAdminLogon=1 + DefaultUserName/DefaultPassword/DefaultDomainName and clears any
        AutoLogonCount limit. HOST-ONLY.
 
        SECURITY: DefaultPassword is stored in cleartext under HKLM\...\Winlogon — acceptable for a
        throwaway lab VM (the base VHDX is already treated as a secret), but do not reuse this account
        or image elsewhere.
    .PARAMETER VMName
        The test VM.
    .PARAMETER Credential
        The guest local-admin credential to auto-log-on.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$VMName,
        [Parameter(Mandatory)] [ValidateNotNull()]        [pscredential]$Credential
    )

    $sam = $Credential.UserName.Split('\')[-1]
    $pw  = $Credential.GetNetworkCredential().Password

    Invoke-Command -VMName $VMName -Credential $Credential -ScriptBlock {
        param($user, $pass)

        # 1) Registry baseline (works offline).
        $k = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon'
        Set-ItemProperty -Path $k -Name 'AutoAdminLogon'    -Value '1'               -Type String
        Set-ItemProperty -Path $k -Name 'DefaultUserName'   -Value $user             -Type String
        Set-ItemProperty -Path $k -Name 'DefaultPassword'   -Value $pass             -Type String
        Set-ItemProperty -Path $k -Name 'DefaultDomainName' -Value $env:COMPUTERNAME -Type String
        Remove-ItemProperty -Path $k -Name 'AutoLogonCount' -ErrorAction SilentlyContinue

        # Windows 11 ships "passwordless" ON (DevicePasswordLessBuildVersion=2), which SUPPRESSES classic
        # username/password AutoLogon even with AutoAdminLogon=1. Set it to 0.
        $pl = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\PasswordLess\Device'
        if (-not (Test-Path $pl)) { New-Item -Path $pl -Force | Out-Null }
        Set-ItemProperty -Path $pl -Name 'DevicePasswordLessBuildVersion' -Value 0 -Type DWord

        # Full token for remote/PS-Direct local admins so these HKLM writes (and Autologon.exe) do not
        # silently fail at MEDIUM integrity — the most likely reason earlier attempts didn't stick. NOTE:
        # it only takes effect for the NEXT PS-Direct session, so it's ALSO baked into the golden image
        # (unattend) — this in-guest write is for VMs provisioned before that.
        Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' -Name 'LocalAccountTokenFilterPolicy' -Value 1 -Type DWord -ErrorAction SilentlyContinue

        # 2) Sysinternals Autologon — configures AutoLogon and stores the password as the LSA 'DefaultPassword'
        # secret. Best-effort HARDENING only (not more reboot-proof than the registry values — Winlogon
        # checks the LSA secret then falls back to plaintext). Needs guest internet AND a full token.
        try {
            $exe = Join-Path $env:TEMP 'Autologon64.exe'
            New-Item -Path 'HKCU:\Software\Sysinternals\Autologon' -Force -ErrorAction SilentlyContinue | Out-Null
            Set-ItemProperty -Path 'HKCU:\Software\Sysinternals\Autologon' -Name 'EulaAccepted' -Value 1 -Type DWord -ErrorAction SilentlyContinue
            [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
            Invoke-WebRequest -Uri 'https://live.sysinternals.com/Autologon64.exe' -OutFile $exe -UseBasicParsing -ErrorAction Stop
            & $exe -accepteula $user $env:COMPUTERNAME $pass 2>&1 | Out-Null
            if ($LASTEXITCODE) { Write-Warning "Autologon returned $LASTEXITCODE (740 = still token-filtered; needs a fresh session after LocalAccountTokenFilterPolicy)." }
        }
        catch {
            Write-Warning "Sysinternals Autologon step skipped ($($_.Exception.Message)); using the registry method only."
        }
    } -ArgumentList $sam, $pw -ErrorAction Stop
}