Powershell Remote User Logoff / Reboot

When patching servers, any disconnected user sessions typically prevent the post-patch auto reboot. To remedy this you could use the following Powershell script :

 

foreach ($_ in get-content servers.txt) {(gwmi win32_operatingsystem -ComputerName $_).Win32Shutdown(4)}

 

The script uses an input file named servers.txt populated with the names of all the servers targeted by auto-patching GPOs to force a logoff of all users on each server.

 

The Win32Shutdown(4) is a value that can be changed for different purposes, so if you want to use a similar script from a command line you can use the following values :

0 – logoff

4 – forced logoff

1 – shutdown

5 – forced shutdown

2 – reboot

6 – forced reboot

8 – power off

12 – forced power off

 

I’ve gone a bit further and created the following script to create a countdown timer that will send a popup message every minute to every server in the input TXT file for $timeleft (15) minutes, then force logoff of all logged on users, active or disconnected.

 

param([int]$timeleft = 15)

[System.Reflection.Assembly]::LoadWithPartialName(“System.Diagnostics”)

$countdowntimer = new-object system.diagnostics.stopwatch

while($timeleft -gt 0)

{

  $countdowntimer.start()

  foreach ($_ in get-content servers.txt) {msg * /SERVER:$_ “A scheduled task running on our management server will be logging off all users on this server in order to do patching in $timeleft minutes, you should save your work and logoff.”}

  while ($countdowntimer.elapsed.minutes -lt 1) {write-progress -activity “Elapsed Time” -status $countdowntimer.elapsed}

  $countdowntimer.reset()

  $timeleft

}

foreach ($_ in get-content servers.txt) {(gwmi win32_operatingsystem -ComputerName $_).Win32Shutdown(4)}

Reader Comments

  1. nice work!! I’m not quite sure how to implement this so it runs automatically (and I presume servers.txt will have to be manually updated – infrequently)… but I’m aware of the problem this solves and it’s an elegant way to do it.

  2. I replaced the foreach command with invoke. Works. The msg does not include the timeleft var.

    When the timer passes reset the new timeleft decreases by one only once… each pass thereafter does not change the value.

    param([int]$timeleft = 5)

    [System.Reflection.Assembly]::LoadWithPartialName(“System.Diagnostics”)

    $countdowntimer = new-object system.diagnostics.stopwatch

    while($timeleft -gt 0)

    {

    $countdowntimer.start()

    invoke-command -ComputerName $compinput {msg * /SERVER:$_ “A scheduled task running on our management server will be logging off all users on this server in order to do patching in $timeleft minutes, you should save your work and logoff.”}

    while ($countdowntimer.elapsed.minutes -lt 1) {write-progress -activity “Elapsed Time” -status $countdowntimer.elapsed}

    $countdowntimer.reset()

    $timeleft—1

    }
    invoke-command -ComputerName $compinput {(gwmi win32_operatingsystem -ComputerName $_).Win32Shutdown(4)}

Leave a Reply