autounattend.xml filesCustom scripts in autounattend.xml files are a powerful mechanism to customize your Windows installation. Some sample scripts are shown below.
7z2606-x64.exe) in the root folder of all attached drives, then install 7-Zip silently. # This is a .ps1 script that runs in the system context, before user accounts are created.
foreach( $drive in [System.IO.DriveInfo]::GetDrives() ) {
if( $found = Join-Path -Path $drive.RootDirectory -ChildPath '7z*-x64.exe' -Resolve -ErrorAction 'SilentlyContinue' ) {
Start-Process -FilePath $found -ArgumentList '/S /D="C:\Program Files\7-Zip"' -Wait;
return;
}
}
'Cannot find any files that match pattern.' | Write-Warning;Configure formThis is a .ps1 script that runs when the first user logs on after Windows has been installed.
foreach( $drive in [System.IO.DriveInfo]::GetDrives() ) {
if( $found = Join-Path -Path $drive.RootDirectory -ChildPath 'Ninite*Installer.exe' -Resolve -ErrorAction 'SilentlyContinue' ) {
Start-Process -FilePath $found -Wait;
return;
}
}
'Cannot find any files that match pattern.' | Write-Warning;Configure formThis is a .ps1 script that runs in the system context, before user accounts are created.
$uri = [uri]::new( 'https://dl.google.com/chrome/install/chrome_installer.exe' );
$file = "$env:TEMP\{0}" -f $uri.Segments[-1];
[System.Net.WebClient]::new().DownloadFile( $uri, $file );
Start-Process -FilePath $file -ArgumentList '/silent /install' -Wait;
Remove-Item -LiteralPath $file -ErrorAction 'SilentlyContinue';Configure formThis is a .ps1 script that runs when the first user logs on after Windows has been installed.
$uri = [uri]::new( 'https://aka.ms/vs/17/release/vc_redist.x64.exe' );
$file = "$env:TEMP\{0}" -f $uri.Segments[-1];
[System.Net.WebClient]::new().DownloadFile( $uri, $file );
Start-Process -FilePath $file -ArgumentList '/install /passive /norestart' -Wait;
Remove-Item -LiteralPath $file -ErrorAction 'SilentlyContinue';Configure formThis is a .ps1 script that runs when the first user logs on after Windows has been installed.
if( [System.Environment]::OSVersion.Version.Build -lt 26100 ) {
'This script requires Windows 11 24H2 or later.' | Write-Warning;
return;
}
$timeout = [datetime]::Now.AddMinutes( 5 );
$exe = "$env:LOCALAPPDATA\Microsoft\WindowsApps\winget.exe";
while( $true ) {
if( $exe | Test-Path ) {
& $exe install --exact --id Mozilla.Firefox --silent --accept-package-agreements --accept-source-agreements --source winget --scope machine;
return;
}
if( [datetime]::Now -gt $timeout ) {
"File '${exe}' does not exist." | Write-Warning;
return;
}
"Waiting for '${exe}' to become available..." | Write-Host;
Start-Sleep -Seconds 1;
}Configure formThis is a .ps1 script that runs when the first user logs on after Windows has been installed.
$user = 'brave';
$repository = 'brave-browser';
$uri = $(
& {
$ProgressPreference = 'SilentlyContinue';
Invoke-WebRequest -Uri "https://api.github.com/repos/${user}/${repository}/releases?per_page=100" -UseBasicParsing -Headers @{
Accept = 'application/vnd.github.v3+json';
} | Select-Object -ExpandProperty 'Content' | ConvertFrom-Json;
};
) | Where-Object -FilterScript {
-not ( $_.draft -or $_.prerelease );
} | ForEach-Object -Process {
$_.assets.browser_download_url;
} | Where-Object -FilterScript {
$_ -match 'StandaloneSetup\.exe$';
} | ForEach-Object -Process {
return [uri]::new( $_ );
} | Select-Object -First 1;
if( -not $uri ) {
throw 'Cannot determine URL of latest Brave installer.';
}
$file = "${env:TEMP}\{0}" -f $uri.Segments[-1];
[System.Net.WebClient]::new().DownloadFile( $uri, $file );
Unblock-File -LiteralPath $file;
Start-Process -FilePath $file -Wait;
Remove-Item -LiteralPath $file -ErrorAction 'SilentlyContinue';Configure formThis is a .ps1 script that runs when the first user logs on after Windows has been installed.
$user = 'microsoft';
$repository = 'terminal';
$latest = $(
& {
$ProgressPreference = 'SilentlyContinue';
Invoke-WebRequest -Uri "https://api.github.com/repos/${user}/${repository}/releases" -UseBasicParsing -Headers @{
Accept = 'application/vnd.github.v3+json';
} | Select-Object -ExpandProperty 'Content' | ConvertFrom-Json;
};
) | Where-Object -FilterScript {
-not $_.draft -and -not $_.prerelease;
} | Select-Object -First 1;
$latest.assets.browser_download_url | Where-Object -FilterScript {
$_ -match '\.msixbundle$';
} | Select-Object -First 1 | ForEach-Object -Process {
$uri = [uri]::new( $_ );
$file = "$env:TEMP\{0}" -f $uri.Segments[-1];
[System.Net.WebClient]::new().DownloadFile( $uri, $file );
Add-AppxProvisionedPackage -Online -PackagePath $file -SkipLicense;
Remove-Item -LiteralPath $file -ErrorAction 'SilentlyContinue';
};Configure form\sources\sxs\microsoft-windows-netfx3-ondemand-package~*.cab instead of downloading it from Windows Update. # This is a .ps1 script that runs when the first user logs on after Windows has been installed.
foreach( $drive in [System.IO.DriveInfo]::GetDrives() ) {
if( $found = Join-Path -Path $drive.RootDirectory -ChildPath 'sources\sxs' -Resolve -ErrorAction 'SilentlyContinue' ) {
Enable-WindowsOptionalFeature -Online -FeatureName 'NetFx3' -Source $found -NoRestart;
return;
}
}
'Cannot find any files that match pattern.' | Write-Warning;Configure formping), in Windows Firewall. #
This is a .ps1 script that runs in the system context, before user accounts are created.
New-NetFirewallRule -DisplayName 'ICMPv4' -Profile 'Any' -Protocol 'ICMPv4' -Action 'Allow'; New-NetFirewallRule -DisplayName 'ICMPv6' -Profile 'Any' -Protocol 'ICMPv6' -Action 'Allow';Configure form
This is a .ps1 script that runs in the system context, before user accounts are created.
Set-NetFirewallProfile -DefaultInboundAction 'Block' -DefaultOutboundAction 'Allow' -Name '*';Configure form
This is a .ps1 script that runs in the system context, before user accounts are created.
$params = @{
Path = 'Registry::HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer';
};
New-Item @params -ErrorAction 'SilentlyContinue';
Set-ItemProperty @params -Name 'NoDriveAutoRun' -Type 'DWord' -Value $(
( 1 -shl 26 ) - 1; # 0x3FFFFFF
);
Set-ItemProperty @params -Name 'NoDriveTypeAutoRun' -Type 'DWord' -Value $(
( 1 -shl 8 ) - 1; # 0xFF
);Configure formThis is a .ps1 script that runs in the system context, before user accounts are created.
$out = & "$env:windir\System32\powercfg.exe" /DuplicateScheme 'e9a42b02-d5df-448d-aa00-03f14749eb61';
if( $out -match '\s([a-f0-9-]{36})\s' ) {
& "$env:windir\System32\powercfg.exe" /SetActive $Matches[1];
} else {
'Could not enable Ultimate Performance power scheme.' | Write-Warning;
}Configure formwinver.exe. # This is a .reg script that runs in the system context, before user accounts are created.
Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion] "RegisteredOrganization"="Foo" "RegisteredOwner"="Bar"Configure form
dir command. # This is a .cmd script that runs in the system context, before user accounts are created.
setx.exe DIRCMD "/A /O:GN /C /N" /mConfigure form
This is a .reg script that runs in the system context, before user accounts are created.
Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Remote Assistance] "fAllowToGetHelp"=dword:00000000Configure form
D:\Users (instead the default %SystemDrive%\Users), without moving the Public and Default profiles. # This is a .cmd script that runs in the system context, before user accounts are created.
mkdir D:\Users reg.exe add "HKLM\Software\Microsoft\Windows NT\CurrentVersion\ProfileList" /v ProfilesDirectory /t REG_SZ /d "D:\Users" /f reg.exe add "HKLM\Software\WOW6432Node\Microsoft\Windows NT\CurrentVersion\ProfileList" /v ProfilesDirectory /t REG_SZ /d "D:\Users" /fConfigure form
This is a .ps1 script that runs when the first user logs on after Windows has been installed.
Get-NetConnectionProfile -InterfaceAlias 'Ethernet*' | Set-NetConnectionProfile -NetworkCategory 'Private';Configure form
the latest features and security updatesduring Setup, which often feels agonizingly slow. This script makes Windows skip this tedious process by pausing Windows Update for one hour. #
This is a .ps1 script that runs in the system context, before user accounts are created.
$format = 'yyyy-MM-ddTHH\:mm\:ssK';
$now = [datetime]::UtcNow;
$start = $now.ToString($format);
$end = $now.AddHours(1).ToString($format);
$params = @{
LiteralPath = 'Registry::HKLM\Software\Microsoft\WindowsUpdate\UX\Settings';
Type = 'String';
Force = $true;
Verbose = $true;
};
'PauseFeatureUpdatesStartTime', 'PauseQualityUpdatesStartTime', 'PauseUpdatesStartTime' | foreach {
Set-ItemProperty @params -Name $_ -Value $start;
};
'PauseFeatureUpdatesEndTime', 'PauseQualityUpdatesEndTime', 'PauseUpdatesExpiryTime' | foreach {
Set-ItemProperty @params -Name $_ -Value $end;
};Configure formThis is a .ps1 script that runs in the system context, before user accounts are created.
foreach( $profile in 'DomainProfile', 'StandardProfile', 'PublicProfile' ) {
reg.exe add "HKLM\System\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\${profile}" /v DisableNotifications /d 1 /t REG_DWORD /f;
}Configure formThis is a .ps1 script that runs in the system context, before user accounts are created.
function New-Shortcut {
[CmdletBinding()]
param(
[string]
$FilePath,
[string]
$Target
);
$wsh = New-Object -ComObject 'WScript.Shell';
$shortcut = $wsh.CreateShortcut( $FilePath );
$shortcut.TargetPath = $Target;
$shortcut.Save();
}
New-Shortcut -FilePath 'C:\Users\Public\Desktop\Download Chrome.url' -Target 'https://www.google.com/chrome/';
New-Shortcut -FilePath 'C:\Users\Public\Desktop\System Properties.lnk' -Target '%windir%\System32\SystemPropertiesAdvanced.exe';Configure formC: drive. # This is a .ps1 script that runs in the system context, before user accounts are created.
Set-Volume -DriveLetter 'C' -NewFileSystemLabel 'Foobar';Configure form
background.png in the root folder of all attached drives and use it as the desktop wallpaper. # This is a .ps1 script to set the desktop wallpaper.
foreach( $drive in [System.IO.DriveInfo]::GetDrives() ) {
if( $found = Join-Path -Path $drive.RootDirectory -ChildPath 'background.png' -Resolve -ErrorAction 'SilentlyContinue' ) {
return [System.IO.File]::ReadAllBytes( $found );
}
}
'Cannot find any files that match pattern.' | Write-Warning;Configure formlockscreen.png in the root folder of all attached drives and use it as the lock screen image. # This is a .ps1 script to set the lock screen image.
foreach( $drive in [System.IO.DriveInfo]::GetDrives() ) {
if( $found = Join-Path -Path $drive.RootDirectory -ChildPath 'lockscreen.png' -Resolve -ErrorAction 'SilentlyContinue' ) {
return [System.IO.File]::ReadAllBytes( $found );
}
}
'Cannot find any files that match pattern.' | Write-Warning;Configure formThis is a .ps1 script to set the computer name.
$adapter = Get-NetAdapter -Name 'Ethernet*' -ErrorAction 'SilentlyContinue' | Select-Object -First 1;
if( $adapter ) {
return 'PC-' + ( $adapter.MacAddress -replace '-', '' );
} else {
"Could not find Ethernet adapter." | Write-Warning;
}Configure formThis is a .ps1 script to set the computer name.
[ipaddress] $addr = Get-NetIPAddress -InterfaceAlias 'Ethernet' -AddressFamily 'IPv4' -ErrorAction 'SilentlyContinue' | Select-Object -First 1 -ExpandProperty 'IPv4Address';
if( $addr ) {
return 'PC-{0}-{1}' -f $addr.GetAddressBytes()[-2, -1];
} else {
"Could not determine IP address." | Write-Warning;
}Configure formThis script is not needed anymore – just select the setting in the form.