Hello Community,
I wanted to share a script I've been working on hoping that other people find it useful and perhaps some PowerCLI specialists collaborate in making it better and more adequate as I’m not an expert PowerCLI scripter.
I've decided to attempt scripting a solution that will keep many Windows Server Templates updated without user interaction.
Disclaimer: I am not a PowerCLI expert; much of what I have scripted for this and other solutions was gathered from what others shared and learning as I go.
The Tasks involved: Convert template to VM, Power On VM, run Windows Update remotely, reboot VMGuest, shut VM down and finally convert back to template.
What's required on Templates:
Note: This Windows Update Module includes the Get-WUInstall PowerShell command which is the one used to install patches remotely.
Here's what is working for me:
# Connect to vCenter
Connect-VIServer "vCenterServer"
# Convert template to VM
Set-Template -Template W2K12Template -ToVM -Confirm:$false -RunAsync
Start-sleep -s 15
#Start VM - I've seen some converted templates that prompt with the VMQuestion, so adding the command to answer with the default option was my response to it.
Start-VM -VM W2K12Template | Get-VMQuestion | Set-VMQuestion -DefaultOption -Confirm:$false
Start-sleep -s 45
#Create variables for Guest OS credentials - This is needed for the Invoke-VMScript cmdlet to be able to execute actions inside the Guest.
#If you don't want to enter the Guest OS local administrator password as clear text in the script, follow the steps on following link to create a file and store it as an encrypted string: Using PowerShell credentials without being prompted for a password - Stack Overflow
$Username = "administrator"
$OSPwd = cat C:\Scripts\OSPwd.txt | convertto-securestring
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $OSPwd
#The following is the cmdlet that will invoke the Get-WUInstall inside the GuestVM to install all available Windows updates; optionally results can be exported to a log file to see the patches installed and related results.
Invoke-VMScript -ScriptType PowerShell -ScriptText "Get-WUInstall –WindowsUpdate –AcceptAll –AutoReboot" -VM W2K12Template -GuestCredential $Cred | Out-file -Filepath C:\WUResults.log -Append
Start-sleep -s 45
#Optionally restart VMGuest one more time in case Windows Update requires it and for whatever reason the –AutoReboot switch didn’t complete it.
Restart-VMGuest -VM W2K12Template -Confirm:$false
#On a separate scheduled script or after a desired wait period, Shutdown the server and convert it back to Template.
Shutdown-VMGuest –VM W2K12Template -Confirm:$false –RunAsync
Start-sleep -s 120
Set-VM –VM W2K12Template -ToTemplate -Confirm:$false
The script can be scheduled to run a couple of days after Patch Tuesdays or whenever you desire.
I really hope PowerCLI leaders can take a look at this and suggest best practices, help improve or advance it with more capabilities.
All feedback and questions are welcomed.
--
Jorge.
This script still working well for you?
I am curious also.
Great Post! This was just what I needed to get going on the automation of updating our templates. Thanks!
I customized the script a bit to add some additional waits/checks and parameters for the vCenter server and the template to update. I also added some lines to update VMware Tools, if needed. You would call it by specifying .\MyScript.ps1 VMtemplate vcenter.mydowmain.com. Once converted to a VM, your VM needs to be pingable via a DNS name that matches the name of the VM, so be sure to allow that through Windows Firewall. Note: I gave it a 2-hour window to settle down after rebooting so that the VM could be shut down and converted back into a template in the same script. Adjust this time for your own environment as-needed.
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True,Position=1)]
[string]$TemplateVMName,
[Parameter(Mandatory=$true,Position=2)]
[string]$VCenterServer
)
Write-Output "Script started $(Get-Date)"
# Check for PowerCLI
If ( (Get-PSSnapin VMware.VimAutomation.Core -ErrorAction SilentlyContinue) -eq $null) {
Add-PSSnapin VMware.VimAutomation.Core
}
Connect-VIServer $VCenterServer
# Convert the template to a VM
Set-Template -Template $TemplateVMName -ToVM -Confirm:$false
Start-Sleep -Seconds 20
# Start the VM. Answer any question with the default response
Write-Output "Starting VM $TemplateVMName"
Start-VM -VM $TemplateVMName | Get-VMQuestion | Set-VMQuestion -DefaultOption -Confirm:$false
# Wait for the VM to become accessible after starting
do
{
Write-Output "Waiting for $TemplateVMName to respond...`r`n"
Start-Sleep -Seconds 10
} until(Test-Connection $TemplateVMName -Quiet | ? { $True } )
Write-Output "$TemplateVMName is up. Resting for 2 minutes to allow the VM to `"settle`"."
Start-Sleep 120 # Wait additional time for the VM to "settle" after booting up
# Update VMware tools if needed
Write-Output “Checking VMware Tools on $TemplateVMName"
do
{
$toolsStatus = (Get-VM $TemplateVMName | Get-View).Guest.ToolsStatus
Write-Output "Tools Status: " $toolsStatus
sleep 3
if ($toolsStatus -eq "toolsOld")
{
Write-Output "Updating VMware Tools on $TemplateVMName"
Update-Tools -VM $TemplateVMName -NoReboot
} else { Write-Output "No VMware Tools update required" }
} until ( $toolsStatus -eq ‘toolsOk’ )
# Build guest OS credentials
$username="$TemplateVMName\Administrator"
$password=cat TemplatePass.txt | ConvertTo-SecureString
$GuestOSCred=New-Object -typename System.Management.Automation.PSCredential -argumentlist $username, $password
<#
The following is the cmdlet that will invoke the Get-WUInstall inside the GuestVM to install all available Windows
updates; optionally results can be exported to a log file to see the patches installed and related results.
#>
Write-Output "Running PSWindowsUpdate script"
Invoke-VMScript -ScriptType PowerShell -ScriptText "ipmo PSWindowsUpdate; Get-WUInstall –AcceptAll –AutoReboot -Verbose | Out-File C:\PSWindowsUpdate.log -Append" -VM $TemplateVMName -GuestCredential $GuestOSCred #| Out-file -Filepath C:\WUResults.log -Append
Write-Output "Waiting 45 seconds for automatic reboot if updates were applied"
Start-Sleep -Seconds 45
do
{
Write-Output "Waiting for $TemplateVMName to respond...`r`n"
Start-Sleep -Seconds 10
} until(Test-Connection $TemplateVMName -Quiet | ? { $True } )
Write-Output "$TemplateVMName is up. Waiting 2 hours for large updates to complete before final reboot."
Start-Sleep -Seconds 7200
#Restart VMGuest one more time in case Windows Update requires it and for whatever reason the –AutoReboot switch didn’t complete it.
Write-Output "Performing final reboot of $TemplateVMName"
Restart-VMGuest -VM $TemplateVMName -Confirm:$false
do
{
Write-Output "Waiting for $TemplateVMName to respond...`r`n"
Start-Sleep -Seconds 10
} until(Test-Connection $TemplateVMName -Quiet | ? { $True } )
Write-Output "$TemplateVMName is up. Resting for 2 minutes to allow the VM to `"settle`"."
Start-Sleep 120 # Wait additional time for the VM to "settle" after booting up
# Shut down the VM and convert it back to a template
Write-Output "Shutting down $TemplateVMName and converting it back to a template"
Shutdown-VMGuest –VM $TemplateVMName -Confirm:$false
do
{
Write-Output "Waiting for $TemplateVMName to shut down...`r`n"
Start-Sleep -Seconds 10
} until(Get-VM -Name $TemplateVMName | Where-Object { $_.powerstate -eq "PoweredOff" } )
Set-VM –VM $TemplateVMName -ToTemplate -Confirm:$false
Write-Output "Finished updating $TemplateVMName"
Write-Output "Script completed $(Get-Date)"
J Kolekes. Very nice script. The only major problem that I am facing is that the Invoke-VMScript -ScriptType PowerShell -ScriptText "Get-WUInstall –WindowsUpdate –AcceptAll –AutoReboot" -VM MyTemplate -GuestCredential $Cred | Out-file -Filepath C:\WUResults.log -Append
keeps throwing an error. I have investigated in depth and still can't figure why it doesn't work. If you can assist that would be fine. The rest of the script runs excellent!
Much Thanks!!!
James
What kind of error did you get?
I've had issues trying to use these scripts too but the output file tells me nothing...
ScriptOutput
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
Are you building your Guest OS credentials beforehand? I see where you're calling it Get-Content.
Are you doing something like this before running the script? Read-Host -AsSecureString | ConvertFrom-SecureString | Out-File C:\TemplatePass.txt
If so, could you just add that Read-Host string to the script to prompt to create the password variable? Then break it down at the end of the script.
Hello
DO you have any updated script that can automate windows template patching in vCenter 7.xx ?
Am I the only one that I got stuck a lot earlier?
When getting to: "Set-Template -Template W2K12Template -ToVM -Confirm:$false -RunAsync"
I get the below error:
Set-Template : 7/19/2022 5:25:52 PM Set-Template Object reference not set to an instance of an object.
At line:1 char:1
+ Set-Template -Template $TeplateVMName -ToVM -Confirm:$false –RunAsync
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (W10-tmpl-test:TemplateImpl) [Set-Template], ViError
+ FullyQualifiedErrorId : Client20_VMServiceImpl_ConvertTemplateToVm_ViError,VMware.VimAutomation.ViCore.Cmdlets.Commands.SetTemplate
or
PS C:\Scripts> Set-Template -Template W10-tmpl-test -ToVM -Confirm:$false -RunAsync
Set-Template : 7/19/2022 5:29:52 PM Set-Template Object reference not set to an instance of an object.
At line:1 char:1
+ Set-Template -Template W10-tmpl-test -ToVM -Confirm:$false -RunAsync
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (W10-tmpl-test:TemplateImpl) [Set-Template], ViError
+ FullyQualifiedErrorId : Client20_VMServiceImpl_ConvertTemplateToVm_ViError,VMware.VimAutomation.ViCore.Cmdlets.Commands.SetTemplate
==============================
Any idea on this?