This is an old revision of the document!
Virtual Machine Template (Windows) - Deployment and Customization
Param(
[Parameter(Mandatory=$True)]
[string]$VMName
)
$AdministratorPassword = "ChangeMe123!"
# Copy VHDX
$NewDiskPath = "D:\Hyper-V\Virtual Hard Disks\$($VMName.ToLower())-os.vhdx"
Copy-Item -Path "C:\Hyper-V\Virtual Hard Disk Template\win2025-template.vhdx" -Destination $NewDiskPath
# New VM
New-VM -Name $VMName.toUpper() -Generation 2 -MemoryStartupBytes 4GB -Path "C:\Hyper-V\Virtual Machines" -SwitchName "ConvergedSwitch"
Set-VMProcessor -VMName $VMName -Count 4
Set-VMFirmware -VMName $VMName -EnableSecureBoot On
# Attach Disk
Add-VMHardDiskDrive -VMName $VMName -Path $NewDiskPath -ControllerType SCSI
Set-VMFirmware -VMName $VMName -FirstBootDevice (Get-VMHardDiskDrive -VMName $VMName)
# Unattend
$Unattend = @"
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<settings pass="specialize">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<ComputerName>$($VMName.ToUpper())</ComputerName>
</component>
<component name="Microsoft-Windows-ServerManager-SvrMgrNc" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DoNotOpenServerManagerAtLogon>true</DoNotOpenServerManagerAtLogon>
</component>
<component name="Microsoft-Windows-DNS-Client" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<UseDomainNameDevolution>false</UseDomainNameDevolution>
<DNSDomain>deluh.basf.net</DNSDomain>
</component>
</settings>
<settings pass="oobeSystem">
<component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- German keyboard (QWERTZ) -->
<!-- 0407 = German (Germany) language ID -->
<!-- 00000407 = German keyboard layout -->
<InputLocale>0407:00000407</InputLocale>
<!-- Everything else US English -->
<SystemLocale>en-US</SystemLocale>
<UILanguage>en-US</UILanguage>
<UILanguageFallback>en-US</UILanguageFallback>
<UserLocale>en-US</UserLocale>
</component>
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<OOBE>
<HideEULAPage>true</HideEULAPage>
<HideOnlineAccountScreens>true</HideOnlineAccountScreens>
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
<NetworkLocation>Work</NetworkLocation>
<ProtectYourPC>3</ProtectYourPC>
</OOBE>
<UserAccounts>
<AdministratorPassword>
<Value>$AdministratorPassword</Value>
<PlainText>true</PlainText>
</AdministratorPassword>
</UserAccounts>
<TimeZone>W. Europe Standard Time</TimeZone>
</component>
</settings>
</unattend>
"@
$Disk = Mount-VHD -Path $NewDiskPath -Passthru
$Partition = Get-Partition -DiskNumber $Disk.Number -PartitionNumber 3
If (-not $Partition.DriveLetter) {
Set-Partition -DiskNumber $Disk.Number -PartitionNumber 3 -NewDriveLetter X
}
$Partition = Get-Partition -DiskNumber $Disk.Number -PartitionNumber 3
$Unattend | Set-Content -Path X:\Windows\Panther\Unattend.xml -Encoding UTF8
$Unattend | Set-Content -Path C:\Users\Administrator\Desktop\LastUnattend.xml -Encoding UTF8
Dismount-VHD -Path $NewDiskPath
# Start VM
Start-VM -Name $VMName
# Wait VM provisioned
$Seconds = 0
Do {
$Seconds++
Start-Sleep -Seconds 1
$HeartBeat = Get-VMIntegrationService -VMName $VMName -Name "Heartbeat" # https://learn.microsoft.com/en-us/powershell/module/hyper-v/get-vmintegrationservice?view=windowsserver2025-ps
Write-Host -NoNewLine "`rWaiting for VM to provision ($Seconds)"
} Until (($HeartBeat.PrimaryStatusDescription -eq 'OK'))
$Credential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString "$AdministratorPassword" -AsPlainText -Force) )
Invoke-Command -VMName $VMName -Credential $Credential -ScriptBlock {
Get-NetAdapter | Rename-NetAdapter -NewName "Access_10.58.5.19_b24"
Get-NetAdapter | New-NetIPAddress -IPAddress 10.58.5.19 -PrefixLength 24
Get-Netadapter | Set-DnsClientServerAddress -ServerAddresses 10.4.21.181,10.4.3.50,10.4.3.51
}