This script has been written to optimize a scheduled reboot on all VMware VDI desktops. In a VDI environment, you have to keep in mind two important things :

  • ESX hosts
  • SAN infrastructure

If you plan a reboot on all virtual machines you will impact directly the performance of the ESX servers and/or the SAN storage.

The purpose is to schedule the reboot without impacting both hosts and storages.

This script requires :

  • Active Directory cmdlets
  • VMware PowerCLI
  • Create a folder C:\scripts where the script is located

This script needs to be updated if you use a date format different than “dd.MM.yyyy” (line 118)

This script do the following steps :

  • create an array $vdi_list_esx that contains the virtual machine names (the list is created with the cmdlet get-vm from the VMware PowerCLI tool)
  • create an array $vdi_list_ads that contains the virtual machine names from the Active Directory
  • if the virtual machine name is found in the array $vdi_list_ads :
    • check if the virtual machine is alive (WMI ping)
    • get the operating system uptime
    • create machine pools : each pool contains vdi that does not belong to the same ESX host and the same SAN storage. If the VDI has been restarted in less than 3 days, it is not added to the restart pool.
  • send by email a report with a table that contains the virtual machines and scheduled reboot information
  • create a Windows scheduled task for each pool to restart the vdi on the next Sunday (the first scheduled task is scheduled at 22:00:00, the others every 15 minutes)

 

Script :

Import-Module activedirectory
Add-PSSnapin VMware.VimAutomation.Core
Connect-VIServer -Server 'vSphereServerHostname.domain.local'

Function send_mail([string]$message,[string]$subject) {
	$emailFrom = "task@domain.local"
	$emailTo = "to@domain.local"
	$emailCC = "cc@domain.local"
	$smtpServer = "smtp.domain.local"
	Send-MailMessage -SmtpServer $smtpServer -To $emailTo -Cc $emailCC -From $emailFrom -Subject $subject -Body $message -BodyAsHtml
}

function create_pool($array_fct) {
	for ($i=0; $i -lt $array_fct.length; $i++){
		$sched_inc = 1
		$vdi_ri = $array_fct[$i].vdi
		
		if ($i -eq 0) {
			$array_fct[$i].pool = $sched_inc	
		}
		else {
			
			# parse existing pools
			do {
				$existing_pool_ri = $array_fct | ? { $_.pool -eq $sched_inc }
				if (($existing_pool_ri -match $array_fct[$i].vHost) -and ($existing_pool_ri -match $array_fct[$i].storage)) {
					$sched_inc++
					$exit_loop = $false
				}
				else {
					$array_fct[$i].pool = $sched_inc
					$exit_loop = $true
				}
			}while ( $exit_loop -eq $false )
		}
	}
}

$vdi_list_esx = Get-VM | select name,VMHost,DatastoreIdList
$vdi_list_ads = get-adobject -Filter {ObjectClass -eq "computer"} -SearchBase "OU=VMwareVDI,DC=domain,DC=local" -SearchScope Subtree -ResultSetSize $null | select Name
$array = @()

#Clean c:\script content
if (gci "c:\scripts\vdi_restart_pool*") {
	Remove-Item -Force "c:\scripts\vdi_restart_pool*"
}

#Clean sched tasks
$listtasks_arr = @()
$listtasks_cmd = "schtasks /query /fo LIST"
$listtasks = invoke-expression $listtasks_cmd
($listtasks | select-string "TaskName") | ? { $_ -match "VDI Reboot pool"} | % { $listtasks_arr += $_ }
$listtasks_arr = $listtasks_arr | % { ($_ -split "\\" )[1] }
$listtasks_arr | % { 
	$myCmd = "SCHTASKS /Delete /F /TN '$_' "
	$result = invoke-expression $myCmd
}

foreach ($entry in $vdi_list_ads) {
	if ($vdi_list_esx.Name -match $entry.Name) {
		$ad_entry = $entry.Name
		$strQuery = "select * from win32_pingstatus where address = '" + $ad_entry + "'"
		$wmi = Get-WmiObject -Query $strQuery
		if ($wmi.statuscode -eq 0) {
			try{
				$Booted = Get-WmiObject -Class Win32_OperatingSystem -Computer $ad_entry -ErrorAction Stop -WarningAction Stop
				$uptime = New-TimeSpan -Start $Booted.ConvertToDateTime($Booted.LastBootUpTime) -End (Get-Date)
				$uptime_str = $uptime.Days

				$esxhost = ($vdi_list_esx | where { $_.name -match $ad_entry} | select VMHost).VMHost
				$storage = ($vdi_list_esx | where { $_.name -match $ad_entry} | select DatastoreIdList).DatastoreIdList
			} catch {
				$uptime_str=999
			}
			
			if (($esxhost) -and ($uptime_str -gt 3)) {
				$Properties = @{vdi=$ad_entry.ToString();vHost=$esxhost.ToString();storage=($storage[0]).ToString();pool="";uptime=$uptime_str;reboot_time=""}
				$Newobject = New-Object PSObject -Property $Properties
				$array +=$newobject
			}				
		}
	}
}

$esxhost_arr = $array | select vHost -Unique
$storage_arr = $array | select storage -unique

create_pool($array)

$number_of_pool = ($array |select pool -Unique).count

#date calculation : next sunday
$date = Get-Date
for($i=1; $i -le 7; $i++)
{        
    if($date.AddDays($i).DayOfWeek -eq 'Sunday')
    {
	    $date = get-date $date.AddDays($i) -Hour "22" -Minute "00" -Second "00"
		break
    }
}

$html_head = "<style type='text/css'>
table {font-family:verdana,arial,sans-serif;font-size:12px;color:#333333;border-width: 1px;border-color: #729ea5;border-collapse: collapse;}
th {font-family:verdana,arial,sans-serif;font-size:12px;background-color:#acc8cc;border-width: 1px;padding: 8px;border-style: solid;border-color: #729ea5;text-align:left;}
tr {font-family:verdana,arial,sans-serif;background-color:#d4e3e5;}
td {font-family:verdana,arial,sans-serif;font-size:12px;border-width: 1px;padding: 8px;border-style: solid;border-color: #729ea5;}
</style>"

for ($p=1; $p -le $number_of_pool; $p++){
	"Add-PSSnapin VMware.VimAutomation.Core" | Out-File c:\scripts\vdi_restart_pool$p.ps1
	"Connect-VIServer -Server 'vSphereServerHostname.domain.local'" | Out-File c:\scripts\vdi_restart_pool$p.ps1 -Append
	$array | ? { $_.pool -eq $p } | select vdi | % { "restart-vm -vm '"+$_.vdi+"' "+' -Confirm:$false -RunAsync' } | Out-File -Append c:\scripts\vdi_restart_pool$p.ps1
	
	$datetime_start_delta = $p*15
	$sched=$date.AddMinutes($datetime_start_delta)
	
	$date_to_sched = get-date $sched -format "dd.MM.yyyy"
	$time_to_sched = ($sched.TimeOfDay.tostring() -split ":")[0..1] -join ":"
	
	$myCmd = "schtasks /create /tn 'VDI Reboot pool$p' /sc once /sd $date_to_sched /st $time_to_sched /F /RL HIGHEST /RU domain\username /RP 'Passw0rd' /TR 'powershell -executionpolicy bypass -file c:\scripts\vdi_restart_pool$p.ps1'"
	$result = invoke-expression $myCmd
}

$array | % {
	$pool_number = $_.pool
	$datetime_start_delta = $pool_number*15
	$_.reboot_time = $date.AddMinutes($datetime_start_delta)

}

$message = "<br><font face='Calibri' color='black'><i>Number of VDI that will be rebooted this sunday : </i><b>" + $array.count + "</b><br>"
$message += $array | select vdi,reboot_time,uptime,vHost,storage | ConvertTo-Html -Head $html_head
$subject = "[VDI] Scheduled Reboot list"
send_mail $message $subject
Reboot task optimization in a VMware Virtual Desktop Infrastructure

Leave a Reply

Your email address will not be published. Required fields are marked *