Skip to main content
 

Logo-Text

Search
Home
  
ServerCare home > Posts > Watch-UPS; a Powershell UPS shutdown server management script  

 

 

January 05
Watch-UPS; a Powershell UPS shutdown server management script

Some time ago I figured out that on Server 2008, you can 'see' the UPS connected through USB as if it is a laptop battery. This opens it up to using WMI to 'read' the battery status, and as such makes it 'scriptable'. Based on this is wrote a VBScript script that watches the UPS and shuts down a list of servers after power is lost and the UPS kicks in. You can find that script here.

Both scripts are born out of frustration with the horrible software provided by APC, and they make it possible to shut down several servers in case of power failure.

   

Great hardware, terrible software

New version

After I posted the old version on my website one of the people leaving comments on it pointed out that it would make more sense to let systems be shut down at certain percentages of UPS time left per server, rather than a predefined amount of seconds after a certain level of power was left (as is the case in the VBScript version). It made sense so I intended to make a new version of the script.

Powershell

It would have been boring to do it in VBScript again, and as PowerShell is the way forward, I decided to make a similar script in PowerShell V2. Note that although the functionality is similar, there are a few differences compared to the previous version: this script doesn't bother sending an E-Mail, but rather logs a message in the application eventlog of the server running it. This seemed to make more sense, as during power failures its hard to rely on external servers being available at all.

Note that the script needs to be run with administrative privileges.

Download here

A zipfile containing the script and an example config .csv file can be downloaded here. [last version march 2013, 1.5]

I've updated the downloadable script to send out an E-Mail for each system it is shutting down. Didn't bother changing the below listing though ;-). Keep in mind that your mail server (exchange right? ;-) needs you to allow anonymous relay from your system, or otherwise you'll need to build in some authentication for sending these mails…

It was pointed out to me how smart it would be to have this also shut down Hyper-V cluster nodes and all the VM's running highly available on them: here's how to do that: http://www.servercare.nl/Lists/Posts/Post.aspx?ID=103

Also, as you can read in the comments below, Neil pointed out that for his 2012 Hyper-V host to shut down (it reported "Privilige not held") he needed to add the line:


$shutsys.psbase.Scope.Options.EnablePrivileges = $true

For those who simply want to take a quick peek at the script, here's the initial listing (for the latest version: always download the above linked file):

# Watch-UPS.ps1

# powershell v2 CP3

# Version: 1.0

# Date jan 2011

# Author:  Paul Weterings

# www.servercare.nl

   

# Description:

<#

This script monitors the UPS or Battery connected to the system in the

$computer varaiable. As soon as itdetects that the system is going offline

(hence starts using the UPS/Battery it will compare the percentage

power left to a a list of system names provided in the 'watch-ups.csv' file.

   

Each system in the .csv list has an associated percentage listed; if the

battery level has dropped below thepercentage listed for that system, the

script will send a shutdown request to that system.

   

The idea is to shut down the least critical systems first, providing them

a 'clean shutdown' rather then anabrupt power off when the UPS is empty.

Additionally; shutting down the least critical systems first will allow

other, possibly more critical systems (such as a NAS or SQL server for

example) to remain on the UPS longer, since it now has to cater to less

systems.

   

example contents of the .csv file:

   

systemname, percentage, status

game1, 80, 0

scom1, 80, 0

mail1, 50, 0

sql1, 10, 0

nas1, 5, 0

   

This means the game and scom servers will be shut down first, when the UPS

is at 80% power left. Since the UPS now has 2 less servers to support, there's

more power for the other servers to remain running during a power failure.

At 50% we stop the mail server, since typically an Exchange server needs some

time to shut down, we sent it the shutdown command fairly soon. We wait until

the very last drops of power left (resp 10% and 5%) to sent the sql and nas

server the shutdown command.

   

systemname = needs to be a (ip reachable) name of a system on the network.

percentage = the percentage of power left in the UPS at which we need to send

a shutdown command to this systemstatus = 0 means the system should be shut down,

1 means ' no action' (for test situations for example)

   

actions of this script are logged on screen and to the application eventlog of

the system running it.

#>

   

# the computer system that we will be measuring; the UPS is connected here.

# CHANGE THIS TO YOUR SYSTEM NAME THAT HAS THE UPS CONNECTED.

# or leave it '.' which is the local system

$computer = "."

   

$namespace = "root\CIMV2"

$batstat= @{"1"="battery is discharging";"2"="On AC";"3"="Fully Charged";"4"=`

"Low";"5"="Critical";"6"="Charging";"7"="Charging and High";"8"="Charging and`

 Low";"9"="Charging and Critical";"10"="Undefined";"11"="Partially Charged";}

   

# for eventlogging purposes:

# we simply want to know if our script was registered with the eventlog already

Get-EventLog -source Watch-UPS -LogName Application -ErrorAction `

silentlycontinue | Out-Null

   

if (-not$?)

{

    # there's an error, so we must not be listed yet, lets see to it we exist

    # for eventlogging

    New-EventLog -source Watch-UPS -LogName Application

}

   

   

# note that any error for Get-WMI object would not be a 'terminating error',

# since the script won't stop # you can't use try/catch or trap to catch any

# errors. I choose to be silent about the error and # handle te result to see

# if an error occurred.

$batinfo = Get-WmiObject -class Win32_Battery -computername $computer `

-ErrorAction silentlycontinue -namespace $namespace

   

# we want to check if we could reach a system, and if we did; did it return

# battery info?

# powershell is so great with stuff like this!

if ($? -and $batinfo)

{

     "Battery status on    : " + $computer +" status: "+ `

     $batstat.Get_Item([string]$batinfo.BatteryStatus)

     "Percentage left      : " + $batinfo.EstimatedChargeRemaining

   

    # Read an interpret the configuration comma seperated values file

    $path = Get-Location

    $lines = Import-Csv "$path\watch-ups.csv"

   

    "`nWatch-UPS will manage:"

    $lines

   

    "-------------------------------"

}

else

{

    if ($error)

    {

        "Could not find any battery/UPS information on system: " + $computer

        throw $error[0].Exception

    }

}

   

# Never ending story

while($true)

{

    $batinfo = Get-WmiObject -class Win32_Battery -computername `

    $computer -ErrorAction silentlycontinue -namespace $namespace

    if ($?) # No error?

    {   

        # Only check if we need to take action (status = 2 is not on AC)   

        if ($batinfo.BatteryStatus -ne 2)

        {

            # Iterate through all the systems we have found in the config file

            # and check their reboot-percentages against our UPS percentage

            foreach ($system in $lines)

            {

                # Do we have a winner?

                if ($batinfo.EstimatedChargeRemaining -le $system.percentage `

                -and $system.status -eq 0)

                {

                    # We are shutting down the system, lets log it in the

                    # eventlog and put it on screen

                    $date = Get-Date

                    $report = "$date : shutting down " + $system.systemname + `

                    " at " + $system.percentage +"% power left."

                    write-eventlog -logname Application -source Watch-UPS `

                    -eventID 3001 -entrytype Information -message $report `

                    -category 1

                    $report

                    # This assumes the script is being run with credentials

                    # that allow shutting down the remote system. If you want

                    # credentials:

                    # (gwmi win32_operatingsystem -ComputerName MyServer

                    # -cred (get-credential)

                    # where (get-credential) can be replaced with credentials

                    # (not a good idea!)

                     

                    # msdn.microsoft.com/en-us/library/aa394058(VS.85).aspx

                    $shutsys = gwmi win32_operatingsystem -ComputerName `

                    $system.systemname -ErrorAction silentlycontinue

                    if($?) # Did the WMI operation go OK?

                    {

                        # WMI has found the system, so we should be able to

                        # send it a shutdown command

                        $shutsys.Win32Shutdown(12)

                        # remember that we have sent this system a shutdown

                        # command, so we only do it once for each powerloss,

                        # we store this status (1) with the system info.

                        $system.status =1

                    }

                    else

                    {

                        "The system to shut down was not found"

                        # it wasn't there, so we ignore it from now on

                        $system.status =-1

                    }

                }           

            }

        }

        else

        {   # Since the UPS is appearantly on AC, all systems need to

            # be "shutdownable".

            # we basically " re-arm" the status after a potental powerloss

            # may have previously shut down systems but the script kept running

            # as power was restored.

            foreach ($system in $lines)

            {

                $system.status = 0  

            }

        }

    }

    else

    {

        $date = Get-Date

        "$date : No UPS system found - was it shut down?"

    }

     

    # Lets wait a second

    Start-Sleep -Seconds 1

}

  

Comments

Great

Thanks a lot!

I use it in a Windows Server 2008 Virtual Machine on an Esxi host.
I adapted it a little to call a putty plink and connect to my esxi host to shutdown all Virtual Machines and the host, instead of
$shutsys.Win32Shutdown(12)

Greets
Rene
 on 1/20/2011 16:23

Re: Great

Thanks for letting me know Rene, I like to hear from people using my tools. Now I know at least one person (other than me) is using it :-)

Paul
 on 1/21/2011 22:56

Looks Interesting

Hi,

Just found the script, looks interesting and better than heavy APC software.

Can it send soe kind of status message to another machine. I don't tend to be logged on to the console all the time?

Regards

Gordon
 on 1/26/2011 9:02

status message

Sure, I'll build it in. Should not be too hard to fix.

Paul
 on 2/2/2011 12:20

Add call to plink

How would I add the call to plink?  I need to call a script on my ESXi host to shutdown all the VMs as well.  What would I replace "$shutsys.Win32Shutdown(12) " with?

Thanks! :-)

-SilkBC
 on 6/30/2011 4:43

Re: Watch-UPS; a Powershell UPS shutdown server management script

Thanks nice script.

i need to shutdown a system using plink. But i am new to PowerShell. Can i enter the command directly or do i need some kind of run commend:

plink -l admin -p .... -h 192.....

or something like this?

run "plink -l admin -p .... -h 192..... "

Does the script work with a usv that ist connected via usb?
 on 11/1/2011 16:08

Call options

Hi

Can you make some notes on usage?

Rob
 on 11/14/2011 10:52

Can the PowerShell Version run as a scheduled task upon startup?

I'm wondering if now that you made a PS version, can that be setup as a scheduled task upon startup of the machine?

PS thanks for the great script.
 on 1/17/2012 20:06

scheduled task

Sure, to get this working, see this article:
http://technet.microsoft.com/en-us/library/ee176949.aspx

the answer you're looking for can be found at the end of the article.

Paul
 on 1/18/2012 12:32

script error

hi sir,, I am trying to run your script but
it gives me this error while running the script
Property 'status' cannot be found on this object; make sure it exists and is settable.
At C:\scripts\watch-ups.ps1:174 char:25
+                 $system. <<<< status = 0
    + CategoryInfo          : InvalidOperation: (status:String) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound
 on 4/8/2012 15:15
1 - 10Next

Add Comment

Spam Filter *


Please enter 4982 in this field. This will help prevent SPAM.

Title


Body *


Attachments