Files
cloudhost/seediso/windows11/Configure-Windows.ps1
T
GuofangandClaude Sonnet 5.5 552a292894 Install OpenSSH Server from the Win32-OpenSSH zip
Carry the zip for the host architecture in the seed ISO and add
06-Install-OpenSSH-Zip-Admin.bat, which installs from it instead of
Windows Update. Update vm-images.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 14:28:13 +08:00

187 lines
6.6 KiB
PowerShell

param(
[switch]$InstallVirtIO,
[switch]$EnableAutomaticLogon,
[switch]$DisableAutoUpdate,
[switch]$CopyInstallers,
[switch]$NoPause
)
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
function Test-Administrator {
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = [Security.Principal.WindowsPrincipal]::new($identity)
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}
if (-not (Test-Administrator)) {
if (-not $PSCommandPath) {
throw 'Run this script from a PowerShell window opened as Administrator.'
}
$arguments = @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', "`"$PSCommandPath`"")
foreach ($name in $PSBoundParameters.Keys) {
$arguments += "-$name"
}
$elevated = Start-Process `
-FilePath 'powershell.exe' `
-ArgumentList $arguments `
-Verb RunAs `
-Wait `
-PassThru
exit $elevated.ExitCode
}
# The guest tools installer is an x86 program and the VirtIO ISO has no Arm64
# package for it, so on Arm64 Windows the Arm64 drivers are installed from their
# folders on the ISO instead. NetKVM is the network driver; without it the guest
# has no network.
function Install-VirtIOArm64Drivers {
$roots = (Get-PSDrive -PSProvider FileSystem).Root
$drivers = @(
@{ Folder = 'NetKVM'; Inf = 'netkvm.inf' },
@{ Folder = 'vioserial'; Inf = 'vioser.inf' },
@{ Folder = 'Balloon'; Inf = 'balloon.inf' },
@{ Folder = 'viorng'; Inf = 'viorng.inf' },
@{ Folder = 'vioinput'; Inf = 'vioinput.inf' },
@{ Folder = 'viogpudo'; Inf = 'viogpudo.inf' }
)
$installed = 0
foreach ($driver in $drivers) {
$inf = $roots |
ForEach-Object { Join-Path $_ "$($driver.Folder)\w11\ARM64\$($driver.Inf)" } |
Where-Object { Test-Path -LiteralPath $_ } |
Select-Object -First 1
if (-not $inf) {
if ($driver.Folder -eq 'NetKVM') {
throw 'The Arm64 NetKVM driver was not found on the attached media.'
}
continue
}
& pnputil.exe /add-driver $inf /install | Out-Host
# 3010 asks for a restart; 259 means that no device uses the driver.
if ($LASTEXITCODE -notin 0, 259, 3010) {
throw "pnputil failed to install $inf (exit code $LASTEXITCODE)."
}
$installed++
}
Write-Host "Installed $installed VirtIO Arm64 driver packages."
}
function Install-VirtIOGuestTools {
if ([Runtime.InteropServices.RuntimeInformation]::OSArchitecture -eq 'Arm64') {
Install-VirtIOArm64Drivers
return
}
$installer = Get-ChildItem `
-Path (Get-PSDrive -PSProvider FileSystem).Root `
-Filter 'virtio-win-guest-tools.exe' `
-File `
-ErrorAction SilentlyContinue |
Select-Object -First 1
if (-not $installer) {
throw 'virtio-win-guest-tools.exe was not found on the attached media.'
}
$process = Start-Process `
-FilePath $installer.FullName `
-ArgumentList '/quiet', '/norestart' `
-Wait `
-PassThru
if ($process.ExitCode -notin 0, 3010) {
throw "VirtIO guest tools installer exited with code $($process.ExitCode)."
}
}
function Enable-AutomaticLogon {
$winlogonPath = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon'
Set-ItemProperty -Path $winlogonPath -Name 'AutoAdminLogon' -Value '1' -Type String
Set-ItemProperty -Path $winlogonPath -Name 'DefaultUserName' -Value 'guofang' -Type String
Set-ItemProperty -Path $winlogonPath -Name 'DefaultPassword' -Value 'wang' -Type String
Set-ItemProperty -Path $winlogonPath -Name 'DefaultDomainName' -Value $env:COMPUTERNAME -Type String
Remove-ItemProperty -Path $winlogonPath -Name 'AutoLogonCount' -ErrorAction SilentlyContinue
}
function Disable-AutomaticUpdate {
$windowsUpdatePath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate'
$autoUpdatePath = Join-Path $windowsUpdatePath 'AU'
$storePath = 'HKLM:\SOFTWARE\Policies\Microsoft\WindowsStore'
$currentVersion = Get-ItemProperty `
-Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
$releaseVersion = $currentVersion.DisplayVersion
New-Item -Path $autoUpdatePath -Force | Out-Null
New-Item -Path $storePath -Force | Out-Null
# Never check automatically; manual checks and installs keep working.
Set-ItemProperty -Path $autoUpdatePath -Name 'NoAutoUpdate' -Value 1 -Type DWord
Set-ItemProperty -Path $autoUpdatePath -Name 'NoAutoRebootWithLoggedOnUsers' -Value 1 -Type DWord
Set-ItemProperty -Path $windowsUpdatePath -Name 'ExcludeWUDriversInQualityUpdate' -Value 1 -Type DWord
# Stay on the installed feature release instead of upgrading automatically.
if ($releaseVersion) {
Set-ItemProperty -Path $windowsUpdatePath -Name 'TargetReleaseVersion' -Value 1 -Type DWord
Set-ItemProperty -Path $windowsUpdatePath -Name 'TargetReleaseVersionInfo' -Value $releaseVersion -Type String
}
# 2 = never download Microsoft Store app updates automatically.
Set-ItemProperty -Path $storePath -Name 'AutoDownload' -Value 2 -Type DWord
# Windows may re-enable this task, so the policies above are the real switch.
Disable-ScheduledTask `
-TaskPath '\Microsoft\Windows\UpdateOrchestrator\' `
-TaskName 'Schedule Scan' `
-ErrorAction SilentlyContinue | Out-Null
}
function Copy-InstallerScripts {
$scripts = @(Get-ChildItem -Path $PSScriptRoot -Filter '*-Install-*.bat' -File)
if ($scripts.Count -eq 0) {
throw "No installer batch files were found next to $PSCommandPath."
}
$target = Join-Path ([Environment]::GetFolderPath('Desktop')) 'Installers'
New-Item -ItemType Directory -Path $target -Force | Out-Null
Copy-Item -Path $scripts.FullName -Destination $target -Force
# 05-Install-OpenSSH-Admin.bat installs from this zip when it is present.
$zips = @(Get-ChildItem -Path $PSScriptRoot -Filter 'OpenSSH-*.zip' -File)
if ($zips.Count -gt 0) {
Copy-Item -Path $zips.FullName -Destination $target -Force
}
}
$exitCode = 0
try {
if ($InstallVirtIO) {
Install-VirtIOGuestTools
}
if ($EnableAutomaticLogon) {
Enable-AutomaticLogon
}
if ($DisableAutoUpdate) {
Disable-AutomaticUpdate
}
if ($CopyInstallers) {
Copy-InstallerScripts
}
Write-Host 'Done.' -ForegroundColor Green
}
catch {
Write-Host "Failed: $($_.Exception.Message)" -ForegroundColor Red
$exitCode = 1
}
if (-not $NoPause) {
Read-Host 'Press Enter to exit' | Out-Null
}
exit $exitCode