all windows templates patching in a datacenter

This post was originally published on this site

Hi LucD,

I have finally completed the below script to do windows updates on all templates in a datacenter using a DHCP vLan. Thanks for your support.

I need one fix in the script output. After updating windows I am writing output in a csv but that is not writing in correcting format. Every VMs output is written twice and leaving blank rows between every vms output. Sample output

 

 

 

Script:

 

$location=Get-Location
$CurrentDate = Get-Date -Format 'MM-dd-yyyy_hh-mm-ss'
$logfilelocation= "$location$($CurrentDate)logfile.txt"
$alltemplatesexportpath="$location$($CurrentDate)-templateswindows.csv"
$Outputfile = "$locationAllTemplatepatchstatusreport.csv as on dated $($CurrentDate).csv" 
$csvFiles = @()
Write-Host "Enter Administrator Credentials for logging into templates" -ForegroundColor Yellow
$cred=Get-Credential
Start-Transcript -Path $logfilelocation -NoClobber -Force -Confirm:$false
$script = @' 
$report = @()
$ErrorActionPreference = "SilentlyContinue"
If ($Error) {
    $Error.Clear()
}
$updatesession=New-Object -ComObject Microsoft.update.session 
$Criteria="IsInstalled=0 and Type=Software and IsHidden=0"
$searchresult=$updateSession.CreateupdateSearcher().Search("IsInstalled=0 and Type='Software' and IsHidden=0").Updates 
$report = if(-not $searchresult.Count){
New-Object -TypeName PSObject -property @{
KB = ''
InstallStatus = 'There are no applicable updates for this computer.'
}
}
else{
$pendingdownloads=$searchresult | Where-Object {$_.IsDownloaded -eq $false}
if(($pendingdownloads |Select-Object IsDownloaded).count -ne '0'){
$downloadercall=$updatesession.CreateUpdateDownloader()
$downloadercall.Updates=New-Object -ComObject Microsoft.update.updatecoll
foreach($pendingdownload in $pendingdownloads){
[void]$downloadercall.Updates.add($pendingdownload)
$downloadercall.Download() |Out-Null
[void]$downloadercall.Updates.RemoveAt(0)
}
}
$updatesession=New-Object -ComObject Microsoft.update.session
$Criteria="IsInstalled=0 and Type=Software and IsHidden=0"
$searchresult=$updateSession.CreateupdateSearcher().Search("IsInstalled=0 and Type='Software' and IsHidden=0").Updates
$downloadedupdates = $searchresult  | Where-Object {$_.IsDownloaded -eq $true}
$updatercall=$updatesession.CreateUpdateInstaller()
$updatercall.Updates= New-Object -ComObject Microsoft.update.updatecoll
foreach($singleupdate in $downloadedupdates){
[void]$updatercall.Updates.add($singleupdate)
$installstatus=$updatercall.install()
[void]$updatercall.Updates.RemoveAt(0)
New-Object -TypeName PSObject -property @{
KB = &{$kbnumb=$singleupdate.Title; $kbnumb.Substring($kbnumb.IndexOf("KB")).Trimend(")")}
InstallStatus = &{
if($installstatus.ResultCode -eq '2'){
    'KB Installed'
}
elseif($installstatus.ResultCode -eq '3'){
    'KB Install Succeeded with errors'
}
elseif($installstatus.ResultCode -eq '4'){
    'Kb Failed to install'
}
elseif($installstatus.ResultCode -eq '5'){
    'KBAborted'
}
elseif (-not $installstatus.ResultCode){
     'KB Failed to Download'
}
}
}
}
}
$report | ConvertTo-Csv -NoTypeInformation
'@
$tasks = @()
$alltemplates=Get-Datacenter  | Get-Template   |Select-Object @{N='Name';E={$_.Name}},@{N="Portgroup";E={((Get-View -Id $_.ExtensionData.Network).name)}},@{N="vCenter";E={([System.Net.Dns]::GetHostEntry($_.Uid.Split(“:”)[0].Split(“@”)[1])).HostName}}
$alltemplates|Export-Csv -Path $alltemplatesexportpath -NoTypeInformation -NoClobber -UseCulture
foreach($singletemplate in $alltemplates){
Write-Host "Marking Template Name $($singletemplate.Name) to VM"
Set-Template -Template $singletemplate.Name -ToVM -Confirm:$false |fl
$templatevm= Get-VM $singletemplate.Name
if(-not $templatevm.Name){
Write-Host "Setting Template $($singletemplate.Name) to VM Failed Moving to Next Template"
}
else{
Write-Host "Collecting DHCP PortGroup Name for Vlanid 2067 from VMhost $($templatevm.VMHost)"
$dhcpportgroup=Get-VirtualPortGroup -VMHost $templatevm.VMHost |?{$_.ExtensionData.config.DefaultPortConfig.Vlan.VlanId -eq '2067'}
Write-Host "Collecting Network Adapter for $($templatevm.Name)" 
$nic=Get-NetworkAdapter -VM $templatevm.Name
Write-Host "Adding Network Adapter to VM $($templatevm.Name) if not Present" 
if($nic -eq $null){
New-NetworkAdapter -VM $templatevm.Name -Portgroup $dhcpportgroup -Type Vmxnet3 -StartConnected -Confirm:$false
}
else{
Write-Host "Changing Portgroup to $($dhcpportgroup.Name) to VM $($templatevm.Name)" 
Get-NetworkAdapter -VM $templatevm.Name |Set-NetworkAdapter -Portgroup $dhcpportgroup -Confirm:$false |fl
}
Write-Host "Starting VM $($templatevm.Name) and wait in loop till GuestOperationsReady is true " 
Start-VM -VM $templatevm.Name -Confirm:$false |fl
while($templatevm.ExtensionData.Guest.GuestOperationsReady -ne "True"){
Start-Sleep -Seconds 3
$templatevm.ExtensionData.UpdateViewData("Guest.GuestOperationsReady")
}
Write-Host "Updating vmtools on $($templatevm.Name) if they are Outdated"
if($templatevm.ExtensionData.guest.toolsversionstatus -eq 'guestToolsNeedUpgrade'){
$timeoutSeconds = 900
$start = (Get-Date)
$task = Get-View -Id (Update-Tools -VM $templatevm.Name -NoReboot  -RunAsync).Id
while((New-TimeSpan -Start $start -End (Get-Date)).TotalSeconds -lt $timeoutSeconds -and 
($task.Info.State -eq [VMware.Vim.TaskInfoState]::running -or
$task.Info.State -eq [VMware.Vim.TaskInfoState]::queued)){
Sleep 5
$task.UpdateViewData()
}
if($task.Info.State -eq [VMware.Vim.TaskInfoState]::running){
$task.CancelTask()
}
elseif($task.Info.State -eq [VMware.Vim.TaskInfoState]::error){
Write-Error "Update Tools failed"
}
}
Write-Host "Waiting for GuestOperationsReady to be true on $($templatevm.Name)"
$templatevm.ExtensionData.UpdateViewData("Guest.GuestOperationsReady")
while($templatevm.ExtensionData.Guest.GuestOperationsReady -ne "True"){
Start-Sleep -Seconds 3
$templatevm.ExtensionData.UpdateViewData("Guest.GuestOperationsReady")
}
Write-Host "Performing Invoke Operation on $($templatevm.Name)"
$sInvoke = @{
VM            = $templatevm.Name
GuestCredential=$cred
ScriptText    = $script
ScriptType    = 'Powershell'
RunAsync      = $true
Confirm       = $false
}
$tasks += @{
VM = $templatevm.Name
Task = Invoke-VMScript @sInvoke
}
}
}
Write-Host "Invoke Operation is performed on all the templates and waiting for results to be collected"
while($tasks.Task.State -contains 'Running'){
sleep 2
Write-Host "waiting on Invoke Operation to Complete" -ForegroundColor Yellow
}
Write-Host "Passing tasks information to foreach loop"
$tasks |ForEach-Object -Process {
$vm=Get-VM -Name $_.VM
Write-Host "Stopping VM $($vm.Name) to Apply windows updates"
Stop-VMGuest -VM $vm.Name -Confirm:$false
while($vm.ExtensionData.Runtime.PowerState -ne 'poweredOff'){
Start-Sleep -Seconds 1
$vm.ExtensionData.UpdateViewData("Runtime.Powerstate")
}
Write-Host "Starting back the VM $($vm.Name) after applying updates"
Start-VM -VM $vm.Name -Confirm:$false
$vm.ExtensionData.UpdateViewData("Guest.GuestOperationsReady")
while($vm.ExtensionData.Guest.GuestOperationsReady -ne "True"){
Start-Sleep -Seconds 1
$vm.ExtensionData.UpdateViewData("Guest.GuestOperationsReady")
}
Write-Host "Performing Invoke operation on VM $($vm.Name) to check if all updates are installed"
$updatescheckscript=@'
$updateObject = New-Object -ComObject Microsoft.Update.Session
$updateSearcher = $updateObject.CreateUpdateSearcher()
$searchResults = $updateSearcher.Search("IsInstalled=0")
$timeoutValue = 1200
$startTime = Get-Date
while($searchResults.Updates.Count -ne '0' -and (New-TimeSpan -Start $startTime -End (Get-Date)).TotalSeconds -lt $timeoutValue){
Start-Sleep 1
$searchResults.Updates.Count
}
if((New-TimeSpan -Start $startTime -End (Get-Date)).TotalSeconds -ge $timeoutValue) {
'windows update completed Partially'
}
else{
'windows update completed sucessfully'
}
'@
$sInvoke = @{
VM            = $vm.Name
GuestCredential=$cred
ScriptText    = $updatescheckscript
ScriptType    = 'Powershell'
ErrorAction   = 'SilentlyContinue'
Confirm       = $false
}
$invokeresult=Invoke-VMScript @sInvoke
Write-Host "Result of invoke operation on VM $($vm.Name)"
Write-Host $invokeresult.ScriptOutput
Write-Host "Performing Stop operation on VM $($vm.Name) before converting to Template"
$maxCount = 3
$count = 0
while($count -lt $maxCount -and $vm.PowerState -ne 'PoweredOff' ){
Stop-VMGuest -VM $vm.Name -Confirm:$false
$count++
Sleep 300
$vm = Get-VM -Name $vm.Name
}
if($vm.PowerState -ne 'PoweredOff'){
Stop-VM -VM $vm -Confirm:$false
}
Write-Host "Updating report to csv"
if($_.Task.State -eq 'Success'){
$_.Task.Result.Scriptoutput | ConvertFrom-Csv |
Add-Member -MemberType NoteProperty -Name VM -Value $_.VM.Name -PassThru |
Add-Member -MemberType NoteProperty -Name State -Value $_.Task.State -PassThru
}
else{
New-Object -TypeName PSObject -Property @{
VM = $_.VM.Name
KB = ''
InstallStatus = ''
State = $_.Task.State
}
}
} | Select VM,State,KB,InstallStatus |Export-Csv -Path $Outputfile -NoTypeInformation -NoClobber -UseCulture
Write-Host "Saved results to csv file"
$csvFiles += $Outputfile
Write-Host "Converting Back to templates"
$alltemplates |ForEach-Object -Process {
Get-NetworkAdapter -VM $_.name |Set-NetworkAdapter -NetworkName $_.Portgroup -Confirm:$false
Set-VM -VM $_.Name -ToTemplate -Confirm:$false
}
Stop-Transcript
$csvFiles+=$logfilelocation
$csvFiles+=$alltemplatesexportpath
Send-MailMessage -From "" -To "" -Subject "Template Patching Info" ` -Body "The attachment contains templates patching status after script execution" ` -Attachments $csvFiles -SmtpServer ''

Leave a Reply

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