*** NOTE – This is based on legacy Horizon 7.x PowerCLI – please see the more recent post for logging off desktops with pending image tasks! ***

One drawback with Horizon View is that it does not have the ability (through the GUI) to automate user logoffs or reboots on a daily/weekly basis. Thankfully, VMware has written some decent PowerShell snap-ins so we can script such tasks.

Automated logoffs are useful in instant and linked cloned scenarios (hopefully everyone is using instant clones and admiring how awesome they are) where you have to deploy an image update immediately and logoff existing sessions later. Thankfully for my blog readers, I have written such a script to do that. This script utilizes the Get-RemoteSession and Send-SessionLogoff Horizon PowerCLI commandlets. Unfortunately there is no PowerCLI commandlet to send messages to the active sessions, so I had to convert each session’s machine name to a string and do a ForEach loop to pipe those names into the msg.exe command.

The other unique thing about this script is that it only does half of the pool at a time (using some variable array magic). The way it’s currently written, it sends a message to half of the sessions that warns them they will be logged off in 15 minutes, warns them again at 60 seconds prior to logoff, logs the first half of sessions off, and then repeats the process with the second half of sessions.

Also, if you want to run this script real-time versus scheduling it in Task Scheduler, I have included Write-Host commands along the way so you can actually see which sessions are being warned and logged off throughout the whole process.

The only thing you’ll need to do before running the script is adjust the 3 variables at the top: $PoolName (name of the Horizon pool), $FirstWarning (How long of a warning the users get before logoff), and $FinalWarning (The second warning time the users get before logoff). Run this or schedule it on a Connection Server and you’re good to go! Enjoy!

###### VMware Horizon Logoff Script ######
###### Created by Nick Burton 10/9/2017 ######

# This script logs off any active sessions for a particular pool. This is useful for enforcing image updates.
# Simply set the pool name and two warning times below! Schedule it with task manager on a Connection Server.

#### KNOWN ISSUES ####
# Currently this script will NOT work if only one session exists due to the array usage in the variables. An IF statement could fix this.
# If two or three sessions exist, this script will logoff all sessions due to the half calculation and array locations starting at 0.
######################

# First, set the poolname below:
$PoolName = “POOL NAME HERE”

# Next, set the first warning time prior to the reboot in seconds. 15 minutes = 900 seconds.
$FirstWarning = 900

# Finally, set the final warning time prior to the reboot in seconds.
$FinalWarning = 60

###################################################################
# DO NOT EDIT ANYTHING BELOW!!! #
###################################################################

# Get first warning time minus final warning time in order to send second message at appropriate time
$WaitTime = $FirstWarning – $FinalWarning
$FirstWarningMinutes = $FirstWarning / 60

# Add all VMware snap-ins for View PowerCLI
Add-PSSnapin *vmware*

# Get all sessions for pool defined in PoolName variable and populate new sessions variable with string data
# Only strings can be accepted for upcoming msg command (VMware hasn’t introduced a message cmdlet)

$sessions = Get-RemoteSession -Pool_id $PoolName | %{$_.DNSName}
$sessionHalf = $sessions.count/2
Write-Host “Here are ALL of the sessions we are logging off:” -ForegroundColor Green
$sessions | Write-Host -ForegroundColor Green

# Populate logoff variable for use later
$Logoffs = Get-RemoteSession -Pool_id $PoolName

# Send first message to first half of sessions in pool using msg.exe using a foreach loop
Write-Host “Sending message to these sessions for pending reboot in $FirstWarningMinutes minutes:” -ForegroundColor Green
$sessions[0 .. $sessionHalf] | Write-Host -ForegroundColor Green
ForEach ($session in $sessions[0 .. $sessionHalf]) {msg /server:$session * “You will be logged off in $FirstWarningMinutes minutes! Please save all work!”}

# Wait for first warning time minus final warning time
Write-Host “Pausing for $WaitTime seconds…” -ForegroundColor Green
Start-Sleep -Seconds $WaitTime

# Send final warning
Write-Host “Sending message to these sessions for pending reboot in $FinalWarning seconds:” -ForegroundColor Green
$sessions[0 .. $sessionHalf] | Write-Host -ForegroundColor Green
ForEach ($session in $sessions[0 .. $sessionHalf]) {msg /server:$session * “You will be logged off in $FinalWarning seconds! Please save all work!”}
Start-Sleep -Seconds $FinalWarning

# Send the logoffs to half the sessions!
Write-Host “Logging off the following sessions!” -ForegroundColor Green
$sessions[0 .. $sessionHalf] | Write-Host -ForegroundColor Green
$Logoffs[0 .. $sessionHalf] | Send-SessionLogoff

# Wait two minutes for desktops to become available, etc. before doing the next half
Write-Host “Waiting two minutes for desktops to become available… there will likely be some errors thrown in a bit since some incoming sessions are already logged off – no big deal.” -ForegroundColor Green
Start-Sleep -Seconds 120

# Send first message to last half of sessions in pool using msg.exe using a foreach loop
Write-Host “Sending message to these sessions for pending reboot in $FirstWarningMinutes minutes:” -ForegroundColor Green
$sessions[$sessionHalf .. $sessions.count] | Write-Host -ForegroundColor Green
ForEach ($session in $sessions[$sessionHalf .. $sessions.count]) {msg /server:$session * “You will be logged off in $FirstWarningMinutes minutes! Please save all work!”}

# Wait for first warning time minus final warning time
Write-Host “Pausing for $WaitTime seconds…” -ForegroundColor Green
Start-Sleep -Seconds $WaitTime

# Send final warning message to last half of sessions in pool using msg.exe using a foreach loop
Write-Host “Sending message to these sessions for pending reboot in $FinalWarning seconds:” -ForegroundColor Green
$sessions[$sessionHalf .. $sessions.count] | Write-Host -ForegroundColor Green
ForEach ($session in $sessions[$sessionHalf .. $sessions.count]) {msg /server:$session * “You will be logged off in $FinalWarning seconds! Please save all work!”}

# Send logoffs to the other half! This will likely have a single error since the median session has already been logged off.
Write-Host “Logging off the following sessions!” -ForegroundColor Green
$sessions[$sessionHalf .. $sessions.count] | Write-Host -ForegroundColor Green
$Logoffs[$LogoffHalf .. $Logoffs.count] | Send-SessionLogoff

Write-Host “Script COMPLETE!” -ForegroundColor Green
# SCRIPT END