Deploy Hyper-V from USB stick with unattended file

When you have few Hyper-V hosts and when you have not the System Center products, you have to deploy your nodes from a USB stick or from PXE services such as WDS. These deployment methods often imply manual steps to configure the operating system. However, it is possible to automate most of the steps thanks to deployment tools (part of ADK) and some PowerShell scripts. This topic shows you how I deploy Hyper-V from USB stick with unattended file and scripts.

Requirements

To follow this topic, you need Windows Assessment and Deployment Kit (ADK). To make this topic, I have deployed the following feature:

You need also the ISO of Windows Server 2016. I have copied the install.wim (folder Sources of the ISO) on my local drive.

Prepare the USB stick

To prepare the USB stick, I use Rufus. Once you have run the tool, just select your ISO file, the USB Stick and the boot mode (Bios or UEFI). This topic is based on an UEFI configuration.

Prepare the unattended file

Once you have installed deployment tools, you can open it from start menu. Then click file | select Windows Image.

Next, browse your local drive to select install.wim.

Select the edition you want. For Hyper-V, I choose Windows Server 2016 Datacenter in Core edition.

Next create an answer file as below.

Select amd64_Microsoft-Windows-International-Core-WinPE (documentation here), and place it in 1. WindowsPE. Then specify language settings:

  • InputLocale: Keyboard layout
  • SystemLocale: system locale language
  • UILanguage: User Interface language
  • UserLocale: per-user settings for currency, time, numbers and so on

Next navigate to SetupUILanguage to specify the language used during Windows Setup.

Next, select amd64_Microsoft-Windows-Setup (documentation here) and place it in 1. WindowsPE.

Navigate to DiskConfiguration and right click on it and select add disk.

To create a partition, right click on CreatePartitions and select insert New CreatePartition.

To make the below configuration, I have followed this guide of Microsoft (because I have an UEFI):

Microsoft-Windows-Setup\DiskConfiguration\Disk DiskID = 0
WillWipeDisk = true
Microsoft-Windows-Setup\DiskConfiguration\Disk\ CreatePartitions\CreatePartition Order = 1
Size = 300
Type = Primary
Microsoft-Windows-Setup\DiskConfiguration\Disk\ CreatePartitions\CreatePartition Order = 2
Size = 260
Type = EFI
Microsoft-Windows-Setup\DiskConfiguration\Disk\ CreatePartitions\CreatePartition Order = 3
Size = 128
Type = MSR
Microsoft-Windows-Setup\DiskConfiguration\Disk\ CreatePartitions\CreatePartition Extend = true
Order = 4
Type = Primary
Microsoft-Windows-Setup\DiskConfiguration\Disk\ ModifyPartitions\ModifyPartition Format = NTFS
Label = WINRE
Order = 1
PartitionID = 1
Microsoft-Windows-Setup\DiskConfiguration\Disk\ ModifyPartitions\ModifyPartition Format = FAT32
Label = System
Order = 2
PartitionID = 2
Microsoft-Windows-Setup\DiskConfiguration\Disk\ ModifyPartitions\ModifyPartition Order = 3
PartitionID = 3
Microsoft-Windows-Setup\DiskConfiguration\Disk\ ModifyPartitions\ModifyPartition Format = NTFS
Label = HostOS
Letter = C
Order = 4
PartitionID = 4
Microsoft-Windows-Setup\ImageInstall\OSImage\InstallTo DiskID = 0
PartitionID = 4

In a single install.wim, there are multiple Windows images. So, we have to specify from which image Windows will be deployed. It is a little tricky but we can retrieve this information with the following dism command:

Dism /Get-ImageInfo /ImageFile:<Path/to/install.wim>

Thanks to this command, we can list images in install.wim and the index. In the below screenshot, the Windows Server 2016 Datacenter in Core edition is the index 3.

So, in InstallFrom node, I add the following metadata:

  • Key: /IMAGE/INDEX
  • Value: 3

Next navigate to ProductKey node (in UserData) and specify a product key.

In UserData node, set AcceptEula to true and provide FullName and Organization name.

Next add amd64_Microsoft-Windows-Shell-Setup (documentation here) to specialize and oobeSystem section.

In Specialize section, specify the timezone and the computer name. You can find here the allowed timezone format.

Move to oobeSystem section. In UserAccounts | AdministratorPassword node, specify an Administrator password as below

In the above screenshot, I configure an Auto Logon that will run 5 times. I make this configuration because later, I’ll run a PowerShell script with some reboots. We will see that in the next section.

In FirstLogonCommands, I add a SynchronousCommand. I specify the script that will customize my operating system. This cmd script calls a PowerShell script. We will see that in the next section.

Next add amd64_Microsoft-Windows-TerminalServices-LocalSessionManager (documentation here). I set fDenyTSConnections to false to enable the RDP connection.

Once you have finished to edit the answer file, you can save it on USB stick. You must name it autounattend.xml.

Post deployment scripts

In the USB Stick, I create a folder called Deploy. In this folder I have also created a folder called Binaries and Agent. In binaries, I have copied the drivers (as Dell, Mellanox and so on). In Deploy folder I have the following script:

  • ConfigureOS.cmd
  • Autodeploy.ps1
  • NodeConfiguration.xml

ConfigureOS.cmd

This script creates c:\temp\Deploy folder and copy the content of Deploy from USB stick to c:\temp\Deploy. Then the script AutoDeploy.ps1 is run.

mkdir c:\temp\Deploy
xcopy D:\Deploy\* C:\temp\Deploy /H /F /E
PowerShell -file c:\temp\deploy\AutoDeploy.ps1

NodeConfiguration.xml

This XML file contains network configurations (IP Address, netmask, Active Directory and so on). This XML is called in AutoDeploy.ps1.

<?xml version="1.0" encoding="UTF-8" />
<NodeConfiguration>
    <Network vSwitchName="SW-1G">
        <Management name="MGMT-22" ipaddr="10.138.22.12" netmask="24" gateway="10.138.22.1" dns="10.139.16.15,10.138.23.15" type="Untagged" vlanid=""/>
        <Cluster name="Cluster-100" ipaddr="192.168.100.12" netmask="24" type="Access" vlanid="100"/>
    </Network>
    <ActiveDirectory name="homecloud.net" />
</NodeConfiguration>

AutoDeploy.ps1

The AutoDeploy.ps1 script install drivers, features, agent, configure networks, MPIO and join Active Directory. A scheduled task is created to run the script after each reboot. The script knows where it is after each reboot thanks to step file. The script reads the step file and regarding the value, it runs a part of the script.


# Variables
$DeployPkg = "C:\temp\Deploy"
$ScriptDir = Split-Path $script:MyInvocation.MyCommand.Path
$StepFile  = $($DeployPkg + "\step.cfg")
$DellPkg   = $DeployPkg + "\Binaries\Dell\suu.cmd"
$XMLPath   = $DeployPkg + "\NodeConfiguration.xml"

if ((Test-Path $XMLPath) -like $False){
    Write-Host "Can't find the XML file located to $XMLPath. Exiting" -ForegroundColor Red
    Exit
}

# Get XML content
$XML = Get-Content $XMLPath

Write-Host "The AutoDeploy script is located in $ScriptDir" -ForegroundColor Green
Write-Host "The step file is $StepFile" -ForegroundColor Green

#### INITIALIZATION OF STEP FILE ####
if ((Test-Path $StepFile) -like $False){
    Write-Host "The step file doesn't exist. Creating it with 0 value" -ForegroundColor Green
    Set-Content -LiteralPath $StepFile -Value 0
}

# Get Step
$Step = Get-Content $StepFile
Write-Host "Current Step: 0. Deploying Dell Drivers and firmware" -ForegroundColor Green

#### DELL DRIVERS INSTALLATION R630 + FIRMWARE ####

if ($Step -like 0){

    # Set a schedule task to run this script on each OS start
    $action = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument '-command "& C:\temp\Deploy\Autodeploy.ps1"'
    $trigger =  New-ScheduledTaskTrigger -Atlogon
    Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "AutoDeploy" -Description "Server Deployment"

    # Test if dell drivers are found
    if ((Test-Path $DellPkg) -like $False){
        Write-Host "Can't find Dell package. Please update the variable DellPkg. Exiting." -ForegroundColor Red
        Exit
    }

     #Change the Power schema to high performance
    Write-Host "Change power schema to high performance" -ForegroundColor Green
    POWERCFG.EXE /S SCHEME_MIN
    
    #Change the value of step file
    Set-Content -LiteralPath $StepFile -Value 1
    
    # Driver installation
    cmd /c "$DellPkg -e"
    Restart-Computer
}

#### ROLE AND FEATURE INSTALLATION ####
if ($Step -like 1){
    Write-Host "Installing the current Windows Roles and Features:" -ForegroundColor Green
    Write-Host "           - Hyper-V + PowerShell cmdlets" -ForegroundColor Blue
    Write-Host "           - Failover Clustering + PowerShell cmdlets" -ForegroundColor Blue
    Write-Host "           - MPIO" -ForegroundColor Blue
    Write-Host "           - Active Directory PowerSHell cmdlets" -ForegroundColor Blue
    Install-WindowsFeature Hyper-V, Failover-Clustering, MultiPath-IO, RSAT-CLustering-Powershell, Hyper-V-PowerShell, RSAT-AD-PowerShell
    Set-Content -LiteralPath $StepFile -Value 2
    Restart-Computer -Force
}

#### NETWORK CONFIGURATION, MPIO AND AD ####
if ($Step -like 2){

    $SwitchName  = $XML.NodeConfiguration.Network.vSwitchName

    $MGMTNicName  = $XML.NodeConfiguration.Network.Management.Name
    $MGMTNicIP    = $XML.NodeConfiguration.Network.Management.ipaddr
    $MGMTNicMask  = $XML.NodeConfiguration.Network.Management.netmask
    $MGMTNicGW    = $XML.NodeConfiguration.Network.Management.gateway
    $MGMTNicDNS   = $XML.NodeConfiguration.Network.Management.dns
    $MGMTNicvType = $XML.NodeConfiguration.Network.Management.type
    $MGMTNicVlan  = $XML.NodeConfiguration.Network.Management.vlanid

    $ClusNicName  = $XML.NodeConfiguration.Network.Cluster.Name
    $ClusNicIP    = $XML.NodeConfiguration.Network.Cluster.ipaddr
    $ClusNicMask  = $XML.NodeConfiguration.Network.Cluster.netmask
    $ClusNicvType = $XML.NodeConfiguration.Network.Cluster.type
    $ClusNicVlan  = $XML.NodeConfiguration.Network.Cluster.vlanid

    # Creating vSwitch (SET)
    Write-Host "Creating Switch Embedded Teaming (name: $SwitchName) vSwitch with all physical NIC" -ForegroundColor Green
    New-VMSwitch -Name $SwitchName -NetAdapterName NIC1, NIC2, NIC3, NIC4 -EnableEmbeddedTeaming $True -AllowManagementOS $False > $Null

    # Creating vNIC Management
    Write-Host "Creating Management NIC (NIC name: $MGMTNicName)" -ForegroundColor Green
    Add-VMNetworkAdapter -SwitchName $SwitchName -ManagementOS -Name $MGMTNicName
    
    if ($MGMTNicvType -like "Untagged"){
        Write-Host "Configure vNIC $MGMTNicName to untagged" -ForegroundColor Green
        Set-VMNetworkAdapterVLAN -ManagementOS -VMNetworkAdapterName $MGMTNicName -Untagged
    }
    Elseif ($ClusNicvType -like "Access"){
        Write-Host "Configure vNIC $MGMTNicName to tagged (VID: $MGMTNicVlan)" -ForegroundColor Green
        Set-VMNetworkAdapterVLAN -ManagementOS -VMNetworkAdapterName $MGMTNicName -Access -VlanId $MGMTNicVlan
    }

    # Creating vNIC Cluster (Hearbeat + Live-Migration)
    Write-Host "Creating Cluster NIC (NIC name: $ClusNicName)" -ForegroundColor Green
    Add-VMNetworkAdapter -SwitchName $SwitchName -ManagementOS -Name $ClusNicName
    Write-Host "Tagging the $ClusNicName NIC" -ForegroundColor Green
    if ($ClusNicvType -like "Untagged"){
        Write-Host "Configure vNIC $ClusNicName to untagged" -ForegroundColor Green
        Set-VMNetworkAdapterVLAN -ManagementOS -VMNetworkAdapterName $ClusNicName -Untagged
    }
    Elseif ($ClusNicvType -like "Access"){
        Write-Host "Configure vNIC $ClusNicName to tagged (VID: $ClusNicVlan)" -ForegroundColor Green
        Set-VMNetworkAdapterVLAN -ManagementOS -VMNetworkAdapterName $ClusNicName -Access -VlanId $ClusNicVlan
    }

    # Disable VMQ because 1GB NIC
    Write-Host "Disabling VMQ on all NICs" -ForegroundColor Green
    Disable-NetAdapterVMQ -Name *

    # Enable Jumbo Frame on all NICs
    Write-Host "Enabling JumboFrame on all vNICs" -ForegroundColor Green
    Get-NetAdapterAdvancedProperty -Name * -RegistryKeyword "*jumbopacket" | Set-NetAdapterAdvancedProperty -RegistryValue 9014

    # Set IP addresses
    Write-Host "Set IP Address on vNICs $MGMTNicName ($MGMTNicIP/$MGMTNicMask, GW: $MGMTNicGW)" -ForegroundColor Green
    New-NetIPAddress -InterfaceAlias "vEthernet ($MGMTNicName)" -IPAddress $MGMTNicIP -PrefixLength $MGMTNicMask -Type Unicast -DefaultGateway $MGMTNicGW | Out-Null
    
    Write-Host "Set DNS on $MGMTNicName (DNS: $MGMTNicDNS)" -ForegroundColor Green
    Set-DnsClientServerAddress -InterfaceAlias "vEthernet ($MGMTNicName)" -ServerAddresses $MGMTNicDNS | Out-Null
    
    Write-Host "Set IP Address on vNICs $ClusNicName ($ClusNicIP/$ClusNicMask)" -ForegroundColor Green
    New-NetIPAddress -InterfaceAlias "vEthernet ($ClusNicName)" -IPAddress $ClusNicIP -PrefixLength $ClusNicMask -Type Unicast | Out-Null
 
    #Disable DNS registration of Storage and Cluster network adapter 
    Write-Host "Disabling register in DNS for $ClusNicName" -ForegroundColor Green
    Set-DNSClient -InterfaceAlias *$ClusNicName* -RegisterThisConnectionsAddress $False

#### CONFIGURE MPIO ####
    Write-Host "Activate SAS claim for MPIO" -ForegroundColor Green
    Enable-MSDSMAutomaticClaim -BusType SAS

#### ADD TO DOMAIN ####

    Write-Host "Add computer to the domain $($XML.NodeConfiguration.ActiveDirectory.name)" -ForegroundColor Green
    New-MSDSMSupportedHW -allApplicable
    $Credential = Get-Credential -Message "Credential for $($XML.NodeConfiguration.ActiveDirectory.name)"
    Add-Computer -DomainName $($XML.NodeConfiguration.ActiveDirectory.name) -Credential $Credential
    Set-Content -LiteralPath $StepFile -Value 3
    Restart-Computer
}

#### INSTALLATION AGENT ####
if ($Step -like 3) {

    # Add here the Agent installation
    ################################
    Set-Content -LiteralPath $StepFile -Value 4
    Restart-Computer
}

#### REMOVE AUTOLOGON , CHANGE ADMIN PWD ####
if ($Step -like 4){

    Write-Host "Delete automatic run script at startup" -ForegroundColor Green
    Unregister-ScheduledTask AutoDeploy

    Restart-Computer
}


Conclusion

This topic shows you an example of how we can automate the Hyper-V host’s deployment with free tools. If you have not System Center, you can use this method. With automation, you can get a consistent deployment more easily than manual deployment.

About Romain Serre

Romain Serre works in Lyon as a Senior Consultant. He is focused on Microsoft Technology, especially on Hyper-V, System Center, Storage, networking and Cloud OS technology as Microsoft Azure or Azure Stack. He is a MVP and he is certified Microsoft Certified Solution Expert (MCSE Server Infrastructure & Private Cloud), on Hyper-V and on Microsoft Azure (Implementing a Microsoft Azure Solution).

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

x

Check Also

Specialize Windows Server Hyper-V guest OS automatically

If you have not SC Virtual Machine Manager, you have no access to VM Template ...

How to deploy a converged network with Windows Server 2016

If you read the news regularly, you probably have heard something about converged network. This ...

Shared virtual hard disks in Hyper-V 2016

Microsoft brings a new feature to Hyper-V in Windows Server 2016 called VHD Set. This ...