====== 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 = @" $($VMName.ToUpper()) true false local 0407:00000407 en-US en-US en-US en-US true true true Work 3 $AdministratorPassword 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 $Stage = 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 If ($Stage -in @(0, 2) -and $HeartBeat.PrimaryStatusDescription -eq 'OK') { $Stage++ } If ($Stage -eq 1 -and $HeartBeat.PrimaryStatusDescription -ne 'OK') { $Stage++ } Write-Host -NoNewLine "`rWaiting for VM to provision ($Seconds)[$Stage]" } Until ($Stage -ge 3) $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 } </code>