At my current customer the Windows Server (2016) template is updated manually every time something has to be changed, added or updated in this image. After editing this image manually there are some scripts which must be run to clean up the event logs and some file directories.
Because this image was not updated frequently the servers which were deployed from this template had to install a lot of updates. This all took a lot of time and there is a chance that someone will make a mistake or forgets a taks.
This is why we decided to automate the creation of the Windows Server template using PowerCLi/PowerShell and SCCM. I would like to give you an overview of the things we did to achieveĀ to create a VM which is identical to the VM which is used at this moment.
We used the following components:
– PowerCLI is used for the following tasks:
- (Dis)Connect to vCenter
- Create the VM
- Reconfigure VM
- Convert VM to Template
-PowerShell is used for these tasks:
- Connect to Microsoft System Center Configuration Manger
- Add the VM to System Center as a device (MAC)
- Add the VM to the SCCM collection to which the Task Sequence is deployed.
- Create application packages and scripts which are used in the Task Sequence
-Microsft System Center Configuration Manager is used to:
- Create/build/deploy the Task Sequence to install the Operating System
- Install the drivers/middelware/applications/Windows Roles & Features and Windows updates
- Cleanup script at the end
We had to use an ISO as boot media because in this organization a password is required when using PXE as boot option, otherwise PXE boot is good alternative.
Here is a screenshot of how we built the Task Sequence (OS –> Features –> Middleware –> Windows Updates –> Software –> Finalize)
Finally we can run the Powershell script …. (Fill out the variables for your environment and the script does the rest for you!!!)
Each step contains a comment of the function of this part!!
<#
### Author: Roderik de Block
###
### Version : 1.0
###
### Functionality: Create VM, Install OS with SCCM task sequence and Convert VM to Template
#>
## Credentials
$Credential = Get-Credential
## Connect to vCenter server
$VIServer = "<YOUR vCENTER SERVER>"
Write-host "Connecting to vCenter" $VIServer -ForegroundColor Green
Connect-VIServer $VIServer -Credential $Credential
## Connect to SCCM Environment
$SiteCode = "<YOUR SCCM SITECODE>"
Write-host "Connecting to SCCM" -ForegroundColor Green
Import-Module (Join-Path $(Split-Path $env:SMS_ADMIN_UI_PATH) ConfigurationManager.psd1) -Scope Local -ErrorAction "SilentlyContinue"
Import-Module (Join-Path $(Split-Path $env:SMS_ADMIN_UI_PATH) ConfigurationManager.psd1) -Scope Local -ErrorAction "Stop"
#Import-Module (Join-Path $(Split-Path $env:SMS_ADMIN_UI_PATH) ConfigurationManager.psd1) -Scope Local #| Out-Null
if ((Get-Location).Drive.Name -ne $SiteCode) { Set-Location -path "$($SiteCode):" -ErrorAction "Stop" }
## VM Variables
$Date = Get-Date -Format MM-yyyy
$VMName = "WS2016-" + "$Date"
$Datastore = "<YOUR DATASTORE>"
$NumCPU = "1"
$MemoryGB = "4"
$DiskGB = "70"
$Disk2GB = "10"
$NetworkName = "<YOUR NETWORK>"
$ResourcePool = Get-Cluster
$ISO = "[<YOUR DATASTORE>] <YOUR>.iso"
## SCCM Variables
$CollectionName = "<YOUR SCCM SITE COLLECTION>"
$Siteserver = "<YOUR SCCM SITE SERVER>"
$CollectionId = Get-CMDeviceCollection -Name "All Systems"
$CollectionId1 = Get-CMDeviceCollection -Name "$CollectionName"
## Create VM
Write-host "Creating VM" -ForegroundColor Green
$VM = New-VM -Name $VMName.ToUpper() -Datastore $Datastore -NumCpu $NumCPU -MemoryGB $MemoryGB -DiskGB $DiskGB -DiskStorageFormat Thin -NetworkName $NetworkName -ResourcePool $ResourcePool -GuestId windows9Server64Guest
## Set VMXNET3 adapter
Get-NetworkAdapter -VM $VM | Set-NetworkAdapter -Type Vmxnet3 -Confirm:$false
## Add Disk to VM
New-HardDisk -VM $VM -Datastore $Datastore -CapacityGB $Disk2GB -StorageFormat Thin
## Enable CPU/Memory HotAdd and change boot order and force bios setup
$spec = New-Object VMware.Vim.VirtualMachineConfigSpec
$spec.CpuHotAddEnabled = $true
$spec.MemoryHotAddEnabled = $true
$spec.Firmware = 'efi'
$VM.ExtensionData.ReconfigVM($spec)
## Enable DirectPath I/O
$nicName = 'Network Adapter 1'
$directPath = $true
$nic = Get-NetworkAdapter -VM $VM -Name $nicName
$spec = New-Object VMware.Vim.VirtualMachineConfigSpec
$dev = New-Object VMware.Vim.VirtualDeviceConfigSpec
$dev.Operation = [VMware.Vim.VirtualDeviceConfigSpecOperation]::edit
$dev.Device = [VMware.Vim.VirtualDevice]$nic.ExtensionData
$dev.Device.UptCompatibilityEnabled = $directPath
$spec.DeviceChange += $dev
$vm.ExtensionData.ReconfigVM($spec)
## ADD SATA Controller and CD Drive
$spec2 = New-Object VMware.Vim.VirtualMachineConfigSpec
$spec2.DeviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec[] (2)
$spec2.DeviceChange[0] = New-Object VMware.Vim.VirtualDeviceConfigSpec
$spec2.DeviceChange[0].Device = New-Object VMware.Vim.VirtualAHCIController
$spec2.DeviceChange[0].Device.DeviceInfo = New-Object VMware.Vim.Description
$spec2.DeviceChange[0].Device.DeviceInfo.Summary = 'New SATA Controller'
$spec2.DeviceChange[0].Device.DeviceInfo.Label = 'New SATA Controller'
$spec2.DeviceChange[0].Device.Key = -102
$spec2.DeviceChange[0].Device.BusNumber = 0
$spec2.DeviceChange[0].Operation = 'add'
$spec2.DeviceChange[1] = New-Object VMware.Vim.VirtualDeviceConfigSpec
$spec2.DeviceChange[1].Device = New-Object VMware.Vim.VirtualCdrom
$spec2.DeviceChange[1].Device.Connectable = New-Object VMware.Vim.VirtualDeviceConnectInfo
$spec2.DeviceChange[1].Device.Connectable.Connected = $false
$spec2.DeviceChange[1].Device.Connectable.AllowGuestControl = $true
$spec2.DeviceChange[1].Device.Connectable.StartConnected = $false
$spec2.DeviceChange[1].Device.Backing = New-Object VMware.Vim.VirtualCdromRemotePassthroughBackingInfo
$spec2.DeviceChange[1].Device.Backing.Exclusive = $false
$spec2.DeviceChange[1].Device.Backing.DeviceName = ''
$spec2.DeviceChange[1].Device.ControllerKey = -102
$spec2.DeviceChange[1].Device.UnitNumber = 0
$spec2.DeviceChange[1].Device.DeviceInfo = New-Object VMware.Vim.Description
$spec2.DeviceChange[1].Device.DeviceInfo.Summary = 'New CD/DVD Drive'
$spec2.DeviceChange[1].Device.DeviceInfo.Label = 'New CD/DVD Drive'
$spec2.DeviceChange[1].Device.Key = -103
$spec2.DeviceChange[1].Operation = 'add'
$spec2.CpuFeatureMask = New-Object VMware.Vim.VirtualMachineCpuIdInfoSpec[] (0)
$vm.ExtensionData.ReconfigVM($spec2)
## Mount ISO
Get-CDDrive -VM $VM | Set-CDDrive -IsoPath $ISO -StartConnected:$true -Confirm:$false
## Change SCSIController to
Get-ScsiController -VM $VM | Set-ScsiController -Type ParaVirtual
# Get MAC Address
$Networkadapter = $vm | Get-NetworkAdapter
$MAC = $Networkadapter.ExtensionData.MacAddress
## Import VM to SCCM
Import-CMComputerInformation -ComputerName $VM -MacAddress $MAC
## Update Membership SCCM
$CollectionId = Get-CMDeviceCollection -Name "All Systems"
$wmi_params=@{ Path = "ROOT\SMS\Site_$($SiteCode):SMS_Collection.CollectionId='$($CollectionId.CollectionID)'"
Name = 'RequestRefresh'
ComputerName = $SiteServer}
Invoke-WmiMethod @wmi_params
## Wait for Device to be added to SCCM
Do {$ResourceID = (Get-CMDevice -Name $VM).resourceid
write-host "Waiting for device to be added to SCCM" -ForegroundColor green
Sleep -Seconds 10}
While ($ResourceID -eq $null)
## ADD VM to SCCM Collection
Add-CMDeviceCollectionDirectMembershipRule -CollectionName $CollectionName -ResourceId $ResourceID
## Update Membership SCCM
$wmi_params1=@{ Path = "ROOT\SMS\Site_$($SiteCode):SMS_Collection.CollectionId='$($CollectionId1.CollectionID)'"
Name = 'RequestRefresh'
ComputerName = $SiteServer}
Invoke-WmiMethod @wmi_params1
## Wait for adding VM to Collection
Do {$CheckCollectionMembership = Get-CMCollectionMember -CollectionName $CollectionName -Name $VM
Sleep -Seconds 10
write-host "Waiting for Device to be added to"$CollectionName -ForegroundColor Green
}
While ($CheckCollectionMembership -eq $null)
## Start VM
Write-host "Starting VM" -ForegroundColor Green
Start-VM -VM $VM -Confirm:$false
## Wait for VM to be Powered Off
Do{Write-host "Operating System is installing. Waiting for VM to shutdown" -ForegroundColor Green
$Powerstate = Get-VM -Name $VM
Sleep -Seconds 300
}until($Powerstate.PowerState -like "PoweredOff")
## Unmount CD Drive
Write-host "Unmounting CD Drive" -ForegroundColor Green
Get-CDDrive -VM $VM | Set-CDDrive -NoMedia -Confirm:$false
## Convert VM to Template
Write-host "Converting VM to Template" -ForegroundColor Green
Set-VM -VM $VM -ToTemplate -Confirm:$false
Disconnect-VIServer -Server $VIServer -Confirm:$false
Write-host "#############################################################" -ForegroundColor Green
Write-host "#############################################################" -ForegroundColor Green
Write-host "#############################################################" -ForegroundColor Green
Write-host "################# Task Succeeded ############################" -ForegroundColor Green
Write-host "#############################################################" -ForegroundColor Green
Write-host "#############################################################" -ForegroundColor Green
Write-host "#############################################################" -ForegroundColor Green
This is incredible!! What’s the normal run time for the script to create a server? I’m actually right now, investigating optimal ways to automate Windows Server template deployments using only Vmware/SCCM. Thank you!