Category Archives: Scripting

Server QA Scripts – Settings Configuration Tool


Following on from my Server QA Scripts post, I have since created a GUI form to make the configuration a lot easier to do.  The tool is fully written in PowerShell and is uploaded to my GitHub page.

To Use


To use, just open a PowerShell windows and start the `QA-Settings-Configurator.ps1` script.

You’ll be presented with the following window.

QA GUI Page 1

Click the Set Check Location button and location the folder where your scripts are located.  Once done, you will be able to change the language (if any) and a base settings file (if any others exist).  Click Import Settings when done.

The script will then scan the scripts folder and load the selected settings file.

Select Required Checks

The next page lists all the checks available and automatically selects all the checks enabled in the settings file.

QA GUI Page 2

You can select or deselect any check that you feel best suits your environment.  For example, if you don’t use McAfee Antivirus in your environment, you wouldn’t want to check for the McAfee Antivirus agent, therefore, you would deselect the COM01 check.

Once you have finished your selections, click the Next > button.

QA Check Values

The third page has several tabs, one tab for each of the check sections.  Within each of the these tabs is a list of the checks you selected, as well as the required settings for each check.

QA GUI Page 3

Double-click each of the settings and change the values that you require for your environment, remembering to check the settings in each of the tabs.

When you are happy with your settings, select the final page.

Generate QA

QA GUI Page 4

Now that you have completed your changes, enter a short code for your QA script file.  This will be the unique code that the QA compiler will use to name your QA script file.  For example, a short code of “ACME” will generate a QA script called “QA_ACME_v3.xx.xxxx.ps1“.  Also enter a company name for the HTML reports that are generated.

Click Save Settings when you are ready, and enter a filename to save your settings as.  Generate a compiled QA script from this page too by clicking the Generate QA Script button.


Other functions

If you happen to loose your settings file, you can restore them by clicking the Restore Settings File button.  Select the location of a compiled QA script and the function will recreate the settings INI file for you.


Out-FancyHTML Function


For a larger project of mine, I needed to produce some fancy looking HTML reports from the data I collected.  An example of this output can be seen below.:



The screen shot shows the results of the code below running on my local laptop:

Get-Process | Select Name, CPU, Handles, Path, Company, FileVersion | Sort Name



The code takes the results of any table based output, formats it as a HTML table, then runs various colour highlighting on specific cells depending on the rules you specify.

For example, the following code will find any cell in the “Handles” column and checks the values found.  If the value is between 50 and 100, it will be coloured in pale yellow (#FFFFC0)

$html = Set-CellColour -InputObject $html -Filter 'Handles -lt 100 -and Handles -gt 50' -Colour '#ffffc0'

Several colour filters can be applied as shown in the code below.



The following code will produce something similar, and will get you on your way, you can also download it below…

# Need to set some variables first....
[string]$dt1    = (Get-Date -Format 'yyyy/MM/dd HH:mm')
[string]$un     = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name.ToLower()
[string]$server = '[server/report name here]'
[string]$report = '[report name here]'

# Vaguely based on 
Function Set-CellColour
    Param ( [Object[]]$InputObject, [string]$Filter, [string]$Colour, [switch]$Row )
        $Property = ($Filter.Split(' ')[0])
        If ($Filter.ToUpper().IndexOf($Property.ToUpper()) -ge 0)
            $Filter = $Filter.ToUpper().Replace($Property.ToUpper(), '$value')
            Try { [scriptblock]$Filter = [scriptblock]::Create($Filter) } Catch { Exit }
        } Else { Exit }

        ForEach ($input In $InputObject)
            [string]$line = $input
            If ($line.IndexOf('<tr><th') -ge 0)
                [int]$index = 0
                [int]$count = 0
                $search = $line | Select-String -Pattern '<th>(.*?)</th>' -AllMatches
                ForEach ($match in $search.Matches)
                    If ($match.Groups[1].Value -eq $Property) { $index = $count }
                If ($index -eq $search.Matches.Count) { $index = -99; Break }

            If ($line -match '<tr><td')
                $line = $line.Replace('<td></td>','<td> </td>')
                $search = $line | Select-String -Pattern '<td(.*?)</td>' -AllMatches
                If (($search -ne $null) -and ($search.Matches.Count -ne 0) -and ($index -ne -99))
                    $value = ($search.Matches[$index].Groups[1].Value).Split('>')[1] -as [double]
                    If ($value -eq $null) { $value = ($search.Matches[$index].Groups[1].Value).Split('>')[1] }
                    If (Invoke-Command $Filter)
                        If ($Row -eq $true) { $line = $line.Replace('<td>', ('<td style="background:{0};">' -f $Colour)) }
                        Else {
                            [string[]]$arr = $line.Replace('><','>■<').Split('■')
                            If ($arr[$index + 1].StartsWith('<td'))
                                $arr[$index + 1] = $arr[$index + 1].Replace($search.Matches[$index].Value, ('<td style="background:{0};">{1}</td>' -f $Colour, $value))
                                $line = [string]::Join('', $arr)
            Write-Output $line

    { }

# CSS for the output table...
[string]$css = @'
    html body       { font-family: Verdana, Geneva, sans-serif; font-size: 12px; height: 100%; margin: 0; overflow: auto; }
    #header         { background: #0066a1; color: #ffffff; width: 100% }
    #headerTop      { padding: 10px; }
    .logo1          { float: left;  font-size: 25px; font-weight: bold; padding: 0 7px 0 0; }
    .logo2          { float: left;  font-size: 25px; }
    .logo3          { float: right; font-size: 12px; text-align: right; }
    .headerRow1     { background: #66a3c7; height: 5px; }
    .serverRow      { background: #000000; color: #ffffff; font-size: 32px; padding: 10px; text-align: center; text-transform: uppercase; }
    .sectionRow     { background: #0066a1; color: #ffffff; font-size: 13px; padding: 1px 5px!important; font-weight: bold; height: 15px!important; }
    table           { background: #eaebec; border: #cccccc 1px solid; border-collapse: collapse; margin: 0; width: 100%; }
    table th        { background: #ededed; border-top: 1px solid #fafafa; border-bottom: 1px solid #e0e0e0; border-left: 1px solid #e0e0e0; height: 45px; min-width: 55px; padding: 0px 15px; text-transform: capitalize; }
    table tr        { text-align: center; }
    table td        { background: #fafafa; border-top: 1px solid #ffffff; border-bottom: 1px solid #e0e0e0; border-left: 1px solid #e0e0e0; height: 55px; min-width: 55px; padding: 0px 10px; }
    table td:first-child   { min-width: 175px; text-align: left; }
    table tr:last-child td { border-bottom: 0; }
    table tr:hover td      { background: #f2f2f2; }
    table tr:hover td.sectionRow { background: #0066a1; }

# Page header rows...
[string]$body = @"
<div id="header"> 
    <div id="headerTop">
        <div class="logo1">ACME</div>
        <div class="logo2">$report</div>
        <div class="logo3">&nbsp;<br/>Generated by $un on $dt1</div>
        <div style="clear:both;"></div>
    <div style="clear:both;"></div>
<div class="headerRow1"></div>
<div class="serverRow">$server</div>
<div class="headerRow1"></div>

# Get a list of processes, and convert to HTML... 
[string[]]$html = Get-Process | Select Name, CPU, Handles, Path, Company, FileVersion | Sort Name | ConvertTo-Html -Head $css -Body $body

# Colour some  cells depending on the filters, filters can contain any valid forumla...
$html = Set-CellColour -InputObject $html -Filter 'Handles -lt 100 -and Handles -gt 50' -Colour '#ffffc0'
$html = Set-CellColour -InputObject $html -Filter 'Handles -gt  99'                     -Colour '#ffc0c0'
$html = Set-CellColour -InputObject $html -Filter 'Handles -lt  51'                     -Colour '#c0ffc0'
$html = Set-CellColour -InputObject $html -Filter 'Name    -eq "ccmexec"'               -Colour 'Gray' -Row
$html = Set-CellColour -InputObject $html -Filter 'Name    -eq "chrome"'                -Colour '#c0c0ff'

# Output the entire HTML to a text file...
$html += '<table><tr><td class="sectionRow">&nbsp;</td></tr></table>'
$html | Out-File .\Out-FancyHTML_Result.html


Have a play, see what you think.  Either leave a comment below, or on the Reddit thread here.






One of the easiest ways to get a list of installed applications on a machine is to use the WMI class Win32_Product.  This enumerates every application.  For example, the following code will display a table with the name of the installed application and it’s version number:

Get-WmiObject -Class Win32_Product | Select Name, Version

Great, now let’s filter it to find a specific application.  I’ll search for any application I have installed with ‘zip’ in the name:

Get-WmiObject -Class Win32_Product -Filter { Name like "%zip%" } | Select Name, Version

Name                                       Version
----                                       -------
7-Zip 9.38 (x64 edition)         

Just what we wanted, but there is a problem.


If you now look at your Application event log, you will notice a huge list of entries that say something like

Windows Installer reconfigured the product. Product Name: [name]. Product Version: [version]. Product Language: 1033. Manufacturer: [manufacturer]. Reconfiguration success or error status: 0.

You will see one entry for every single application you have installed (and a few more).  You may also notice that the command above took quite a long time to run.  In some cases I have seen it take up to 5 minutes to return just one entry.

This is a well known and blogged about topic, Microsoft even has a Knowledge Base article on it: KB974524.



My solution to this problem is a PowerShell function that searches the registry for the same information.  This has the benefit of being incredibly fast.

It works by searching the two locations where applications put their uninstall details.



The first is for 32-bit applications, the second for 64-bit applications.

To use my function simply call it with the following command.  Again, searching for any application with ‘zip’ in the name:

Win32_Product -serverName $env:computername -displayName 'zip'

This will return just the version number :

My function will only return the version number of the first application it finds that matches the input value.  You could expand on this and return all application names and version numbers using a hashtable or an array.  I’ll leave that up to you.


$regSearch = 'Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
Function Win32_Product
    Param ([string]$serverName, [string]$displayName)
        $reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $serverName)
        $regKey = $reg.OpenSubKey($regSearch)
        If ($regKey) { [array]$keyVal = $regKey.GetSubKeyNames() }
    Catch { Return $null }

    $found = $false
    If (($regKey) -and ($keyVal.Count -gt 0)) {
        ForEach ($app In $keyVal) {
            $appKey = $regKey.OpenSubKey($app).GetValue('DisplayName')
            If ($appKey -like ('*' + $displayName + '*')) {
                $found = $true
                [string]$verCheck = $regKey.OpenSubKey($app).GetValue('DisplayVersion')
                If (-not $verCheck) { $verCheck = '0.1' } }
        If ($found -eq $false) {
            If ($regSearch -like '*Wow6432Node*') {
                $regSearch = $regSearch.Replace('Wow6432Node', '')
                $verCheck = Win32_Product -serverName $serverName -displayName $displayName
            Else { $verCheck = $null } }
    Else { $verCheck = $null }
    Return $verCheck


‘New’ Item Cleaner


The ‘New >‘ item on the right-click menu is quite handy.  It can list a huge amount of document types for you to create new documents.  Most of the time these work, other times they don’t.



As this list grows when new applications are installed, it rarely shrinks when an application is removed.  This can lead to dead links.

Another issue with this list is custom desktops for Citrix XenApp users, or other users on locked down, permissions based terminal servers.  There may be links in the list that a particular user doesn’t have access to.



One solution is to just ignore it.  Users may log tickets sayinIg they can’t access an application they are not allowed to have, causing you to waste time and effort on chasing your tails.

While I was working with a particular Citrix XenApp install quite a few years ago, I created a script in KIX that would remove the entire list of applications, except for “Text Document”  I have recently updated this script for PowerShell and thought that I would share it with you.


 As you can see from the screen shots, all the new document links have gone except for the only one we want.


The Script

The script is listed below…

# Get all keys under the root branch...
$RegKeyItems = Get-ChildItem
ForEach ($RegKey in $RegKeyItems)
    # Check if it starts with a full stop "."
    If ($RegKey.PSChildName.StartsWith('.'))
        # Get all keys under current branch...
        $RegSubKeyItems = Get-ChildItem -Path $RegKey.PSChildName
        ForEach ($RegSubKey in $RegSubKeyItems)
            # Looking for "ShellNew"...
            If ($RegSubKey.PSChildName -eq 'ShellNew')
                # Make sure not to remove Shortcut Links or Text Documents
                If (($RegKey.PSChildName -ne '.lnk') -and ($RegKey.PSChildName -ne '.txt'))
                    # Display and delete key...
                    write-host "Deleting: $RegSubKey"
                    Remove-Item -Path \$RegSubKey -Recurse -ErrorAction SilentlyContinue
                # Same as above, checking one level deeper...
                $RegSubSubKeyItems = Get-ChildItem -Path $RegSubKey.PSChildName -ErrorAction SilentlyContinue
                ForEach ($RegSubSubKey in $RegSubSubKeyItems)
                    # Looking for "ShellNew"...
                    If ($RegSubSubKey.PSChildName -eq 'ShellNew')
                        # Make sure not to remove Shortcut Links or Text Documents
                        If (($RegSubKey.PSChildName -ne '.lnk') -and ($RegSubKey.PSChildName -ne '.txt'))
                            # Display and delete key...
                            write-host "Deleting: $RegSubSubKey"
                            Remove-Item -Path \$RegSubSubKey -Recurse -ErrorAction SilentlyContinue
# Also remove that annoying Briefcase link...
Remove-Item -Path '\Briefcase\ShellNew' -Recurse -ErrorAction SilentlyContinue


If you have any suggestions for improvements, or want to share your tweaks, leave a comment below.



download-fileNew Item Cleaner.ps1


Power Up Your HomeLab With PowerCLI


As a follow on to the shut down home-lab script, I have also written a power up script for when power is restored to your home-lab and you want to get it up and running again quickly.  This script will assume your hosts are powered on and waiting, but nothing else has happened yet.


The Script

The script can be downloaded below, make sure you read though it fully and understand how it works.  I am not responsible for anything going wrong.!

At the top of the script, you will need to change the settings to match your own home-lab.

[string]$vCenter_Server                   = "svr-vc"
[string]$vCenter_Server_UserName          = "administrator"
[string]$vCenter_Server_Password          = "********"

[string]$Known_ESX_Host                   = ""
[string]$Known_ESX_Host_UserName          = "root"
[string]$Known_ESX_Host_Password          = "********"

[string[]]$Critical_Servers_Startup_Order = ("live-dc","svr-dc","svr-sql", "svr-vc")


  1. The name of your virtual centre server
  2. The username you use to connect to your virtual centre server
  3. The password for the above username
  4. .
  5. The IP address of one of your ESXi hosts. This is explained below
  6. The username of the root account of for the above host
  7. The password for the root account
  8. .
  9. The servers in your critical folder, in the order they should be powered up


Starting from the known host (the same as in the shut down script), it will power up your critical servers in the order specified on line 9.  Once they are fully powered on (it checks that the VMware tools are available) it will check and wait for the VMware VirtualCenter Server service to be up and running.  Once running, the script will then start all the remaining virtual machines.



Download the script from the link below, and again, make sure you test it before putting it live.!


Shutdown Your HomeLab With PowerCLI


If you run a large home-lab like I do, shutting it down in an emergency can be a slow process, especially if you are panicking about your UPS staying up long enough.  You  DO have a UPS don’t you.!?

I had written a PowerCLI script for myself that was custom to my specific lab.  I have since made it more generic so that everyone can use it if they wanted to.  The script does rely on one specific criteria for your home-lab though.


Home-Lab Layout

vc-foldersWithin the vCenter VMs and Templates view, you can organise your VMs into folders to help separate them into logical groups.  This has no bearing on the VMs themselves, its purely for your information.

As you can see by this image, I have a folder called Critical that I use to hold my important VMs.  These are…

  • live-dc : The DHCP and DNS server for my home network,
  • svr-dc : The domain controller, as well as DHCP and DNS for my private lab network,
  • svr-sql : The Microsoft SQL server for my private network.  This holds all the databases for my lab,
  • svr-vc : The VMware virtual centre server for my home-lab.

This Critical folder is important in my script, as it controls which VMs are not shut down immediately, but must wait until the end and be shut down in a specific order.


The Script

The script can be downloaded below, make sure you read though it fully and understand how it works.  I am not responsible for anything going wrong.!

At the top of the script, you will need to change the settings to match your own home-lab.

[string]$vCenter_Server                    = "svr-vc"
[string]$vCenter_Server_UserName           = "administrator"
[string]$vCenter_Server_Password           = "********"

[string]$Known_ESX_Host                    = "esx1"
[string]$Known_ESX_Host_UserName           = "root"
[string]$Known_ESX_Host_Password           = "********"

[string]$Critical_Folder                   = "Critical"
[string[]]$Critical_Servers_Shutdown_Order = ("svr-vc", "svr-sql", "svr-dc", "live-dc")

[int]$Shutdown_TimeOut = 180 # Seconds


  1. The name of your virtual centre server
  2. The username you use to connect to your virtual centre server
  3. The password for the above username
  4. .
  5. The name of one of your ESXi hosts.  This is explained below
  6. The username of the root account of for the above host
  7. The password for the root account
  8. .
  9. The critical folder mentioned above (not case sensitive)
  10. The servers in your critical folder, in the order they should be shut down in
  11. .
  12. How long to wait for a clean shut down before killing power to a virtual machine


Specific ESXi Host Name

The reason for a specific host name in the script, is so that when you come to power on your home-lab, you will know exactly which host will hold your critical VMs.  The script will migrate all powered off VMs as well as your critical ones to this host before powering everything down.  If you have a number of hosts it’s helpful if you don’t need to check each one for your domain controller is virtual centre server.



Download the script from the link below, and again, make sure you test it before putting it live.!


More Pushover Configuration


After successfully configuring Pushover with PRTG, I moved on to configuring my Synology DiskStation (easy) and VMware vCenter (harder) installation to also notify me via Pushover.


Register An Application

In both cases, I registered a new application within the Pushover console.  I did this so that each alert would come though with their own specific icon and description.  See my previous blog posting on how to do this.

I used the following icons for each application :

  • synology Synology
  • vmware VMware


Synology DiskStation Notifications

syno-notifications-1This is was the easiest change to make, simply…

  1. Login to your DiskStation,
  2. Open the Control Panel,
  3. Select the Notification option,
  4. Choose either (or both) of the following tabs…
    • Email
    • Push Service
  5. Enter your Pushover API email address
    • [user-key]+a=[api-key]


VMware vCenter Alerts

Setting up these alerts were a lot harder.  It took a lot of trial and error.  Hopefully this will save you the same hassle.

Before you start, make sure you have the Microsoft dotNet framework version 4 or later installed, as well as PowerShell 4 or later.  Next, we’ll create a folder and two files on your vCenter server…

  1. On the C: drive, create a folder called Scripts,
  2. Create two plain text files and enter the following code…


"c:\windows\system32\windowspowershell\v1.0\powershell.exe" c:\scripts\send-alert.ps1 -title '%1' -message '%2'



$parameters = @{
    token    = "[api-key]"
    user     = "[user-key]"
    priority = "0"
    title    = $title
    message  = $message

$parameters | Invoke-RestMethod -Uri "" -Method Post

Remember to enter your own api-key and user-key into the .ps1 script file.


vmware-definitionsvCenter Configuration

Next we need to configure VMware vCenter to run the batch file whenever an alert condition is met.  At last count there are 68 different alert definitions within vCenter.  You could change each on manually, but that would take all day.  If you have VMware PowerCLI installed (and you really should have) you can script this.

Manual Steps…

  1. vmware-alarm-4Open VMware vSphere Client, and login to your vCenter server,
  2. Select the very top level entry in the tree view on the left,
  3. Choose the Alarms tab along the top,
  4. Click the Definitions button,
  5. The list of 68+ definitions are list.  Double-click on one of them.  I’ll choose Datastore usage on disk,
  6. Select the Actions tab,
  7. Click Add,
  8. From the new entry that appears,
    • change “Send a notification email
    • to “Run a command
  9. Enter the following code into the Configuration column…
c:\scripts\send-alert.bat "{targetName}  >  {eventDescription}" "{triggeringSummary}"
  1. Click OK
  2. Rinse and repeat for the remaining definitions, or…

Automatic Script…

  1. Open a PowerCLI command window, and connect to your vCenter server
  2. Copy and paste the following code, and let it run…
$al = Get-AlarmDefinition
ForEach ($a in $al) {
    Get-AlarmDefinition -Name $a | New-AlarmAction -Script -ScriptPath 'c:\scripts\send-alert.bat "{targetName}  >  {eventDescription}" "{triggeringSummary}"'

Make sure you get the single quotes ( ‘ ) and double-quotes ( ” ) correct

For a list of variables you can use, check the VMware documentation.



Once you have alerts working, you should get alerts through looking something like this…


Push Notifications With PRTG And Pushover


I was looking for a way for my PRTG installation to alert me whenever I was away from home.  I initially thought of using an “email to SMS message” service, and started looking into this option.  I was then pointed to an API push service called Pushover.

Setting this up to work with PRTG is amazingly simple.  The instructions and scripts I use are taken from this blog, just cleaned up a bit and with more screenshots.



A Pushover account,

Install Python for Windows onto your PRTG server.  Choose Python 2, not 3.

An Android or Apple device with the Pushover app installed,

store-google   store-apple


Pushover Instructions

Once you have created your account and verified your email address, you need to create an application in-order to get an API key.

  1. pushover - 1From your main home page,
  2. Next to Your Applications, click Register An Application,
  3. Fill out the displayed form, my entries were…
    • PRTG
    • Application
    • (blank)
    • This image (click to enlarge) – PRTG-75x75
  4. Tick the Terms Of Service check box and click Create Application.

Once the application has been created, you will be presented with an API Key.  You will need this later, along with your User Key.


Device Setup

Depending on your device, download the appropriate app and install it.  Log in to the app, and check the settings to see if you need to change anything, specifically the Edit Quiet Hours.


Python Scripts

Log on yo your PRTG server, and install Python.  This is a very straight forward installation…

python install - 1   python install - 2   python install - 3   python install - 4   python install - 5

Once done, we need to create two plain text files into the following folder…

C:\Program Files (x86)\PRTG Network Monitor\Notifications\EXE


File 1 – pushover.bat

Create a file called pushover.bat and enter the following code…

C:\Python27\Python "C:\Program Files (x86)\PRTG Network Monitor\Notifications\EXE\" %*

Change the listed paths if required.


File 2 –

Create a file called and enter the following code…

import httplib, urllib, sys
apiKey = sys.argv[1]
userKey = sys.argv[2]
priority = sys.argv[3]
title = sys.argv[4]
msg = ''
for count, arg in enumerate(sys.argv):
    if count > 4:
        msg += arg + '\n'
conn = httplib.HTTPSConnection("")
conn.request("POST", "/1/messages.json",
        "token": apiKey,
        "user": userKey,
        "message": msg,
        "title": title,
        "priority": priority,
    { "Content-type": "application/x-www-form-urlencoded" })
response = conn.getresponse()


PRTG Setup

Part one

  1. From your PRTG console,
  2. Click Setup > Account Settings > Notifications,
  3. Click the ‘Add New Notification‘ button on the bottom left,
  4. Add a name for your notification – PRTG – ALERT,
  5. Scroll down and check the Execute Program box,
  6. Choose pushover.bat from the dropdown,
  7. Enter the parameters as follows:

[api-key] [user-key] [priority] “%device – %name : %status” “ALERT/ERROR” “Msg: %message”

Example:  abcd1234efgh5678 ab12cd34ef56gh78 0 “%device – %name : %status” “ALERT/ERROR” “Msg: %message”

(For a list of priority values, check out the Pushover FAQs)

  1. Click Save,
  2. Click Test, and you should receive an alert on your device almost immediately.

 pushover - alarmtest

I have three alerts set up..

  • ALL OK


Part two

Now that we have an alert or three setup, we need to make sure your devices start using them.  For my small setup, I set my notifications at the top level, at the Local Probe…

  1. From the PRTG console, select the Local Probe device (or other device if you want more control),
  2. Select the Notifications tab along the top,
  3. Click Add State Trigger,
  4. From the new trigger then appears, choose the options that bests suits you and your environment.  The no notification drop down should list the alerts you setup above.

prtg notifications - 1

The values I have are shown below…

prtg notifications - 2


Errors / Issues / Troubleshooting

When I initially set this up the alerts were not coming though.  In order to troubleshoot this issue, I ran the script manually.  Do do this…

  1. Open a command prompt on your PRTG server,
  2. Type the following command into the window…
C:\Python27\Python "C:\Program Files (x86)\PRTG Network Monitor\Notifications\EXE\" [api-key] [user-key] 0 "Manual Test" "Ignore This Message"

…making sure to enter your API Key and User Key in the correct locations

  1. You should see no output if everything works OK.

In my case, I got SSL handshake errors.  To fix this, I just visited the Pushover API site ( in order for my server to download the SSL certificate.


More Pushover Configuration

I have added another post about using Pushover notifications with my Synology DiskStation and VMware vCenter Alerts.  Read about it here.

NTP Time Service


Network Time Protocol (NTP) is a networking protocol for clock synchronization between computer systems…  NTP is intended to synchronize all participating computers to within a few milliseconds of Coordinated Universal Time (UTC).

Taken from Wikipedia

Making sure you have a reliable and accurate time source can help with mitigating issues on your network.



One of my ESXi hosts was replaced recently, and using my automatic build script I had the new replacement up and running quite quickly.  What my script doesn’t do however, is configure the NTP settings with the host and because of this, it’s internal clock was about 6 hours ahead of the rest of the network.

My PRTG Network Monitoring system is a VM that, via DRS, can move freely amongst my three hosts depending on the current workload.  At 5:00 am the other morning, my monitoring server was moved to the offending host.  This screwed up its monitoring results.

As the VM was migrated to the new host, the time suddenly jumped forward by about 6 hours.  The OS then took over sometime after and jumped back to the correct time.  This time flip-flop caused large gaps in the monitoring results, and left me scratching my head for a while figuring out why.!


How to fix the above issues

There were two issues at play here, the host NTP settings were not correct, and the guest VM was set to synchronise it’s time with the host.


Configure Host NTP Settings

To enable configure each hosts time setting…

  1. Select your host (if you have more than one, you will need to do this on each one in turn),
  2. Select the Configuration tab along the top of the main section,
  3. Down the left hand menu, near the middle, select Time Configuration,
  4. Click Properties at the top right,
  5. From the first window that appears, make sure the NTP Client Enabled option is selected,
  6. Click Options,
  7. From the General item, choose the middle option, choose Start and stop with host,
  8. Select the NTP Settings item, click Add, and enter the IP address or host name of your chosen NTP host,
  9. Tick the option Restart NTP service to apply changes.


NTP-2   NTP-3

If you don’t have an internal NTP time source, the best option is to use, and  See the link below for more information, or for geographically local sources…


Disable ‘synchronise guest time with host’

I had my home-lab domain configured to use a specific time source, and to push that out to all the Windows machines.  This is a good idea to configure.  What I had done wrong, was allow the virtual machines guests to have their clocks synchronised with the hosts.  This is generally a bad idea.

  1. Right-click a virtual machine, choose Edit Settings…,
  2. Choose the Options tab along the top,
  3. Select the VMware Tools settings,
  4. Un-tick the Synchronize guest time with host option.

While you are there, it’s also a good idea to tick the other option : Check and upgrade Tools during power cycling.


If you have a lot of hosts to change, this is going to be a very tedious task.  Thankfully, there is a script that will handle this for you.

The script below will set both options for you:

  • Line 12: Check and upgrade Tools during power cycling,
  • Line 13: Synchronize guest time with host.

Save the script as a .PS1 file, and execute it within PowerCLI.  Remember to change the first line to enter your vCenter server name or IP address.


# Query for the VM guests
$VMGuests = Get-VM

# Loop through your VM guests, set the VM Tools upgrade checkbox and the Sync Time checkbox to true
ForEach ($VMGuest in $VMGuests) {
    $spec = New-Object VMware.Vim.VirtualMachineConfigSpec
    $spec.changeVersion = $VMGuest.ExtensionData.Config.ChangeVersion
    $ = New-Object VMWare.Vim.ToolsConfigInfo
    $ = "upgradeAtPowerCycle"
    $ = $false

    # Apply the changes
    $MyVM = Get-View -Id $VMGuest.Id

So, there you have it.  Make sure all your VM hosts and guest are using the correct time, and are synchronised with a reliable time source.

Temporary Internet Files


If you manage Windows terminal servers, or Citrix XenApp servers you will know all about temporary internet files, and the amount of space they take up on your servers and user profile folders.

There are ways to reduce the impact on profile folders by using GPOs to not copy the folder about, but this still could leave them on the server.


Removal Script

I have customised a script I found somewhere on the internet to remove the temporary internet folders for all users on a particular server.  The script enumerates C:\USERS for every user and if a Temporary Internet Files folder exists, it is deleted and a new one created in its place.  This is a quicker method than removing specific files.  It will also delete and recreate the users Temp folder.


The Script

@ECHO Started: %time%
@PUSHD "C:\Users"

FOR /D %%F IN (*.*) DO (
    IF EXIST "%%F\AppData\Local" (
        @ECHO Processing user: %%F
        @PUSHD "%%F\AppData\Local"

        IF EXIST "Microsoft\Windows\Temporary Internet Files" (
            @ECHO Removing directory 'Temporary Internet Files'
            @PUSHD "Microsoft\Windows"
            IF [%%F] == [%username%] (
                @PUSHD "Temporary Internet Files"
                FOR /F %%E IN ('DIR /AD /B') DO (
                    @PUSHD %%E
                    FOR /F %%G IN ('DIR /AD /B') DO (
                        @ECHO Removing %%G
                        RD /S /Q %%G
            ) ELSE (
                RD /S /Q "Temporary Internet Files"
                @ECHO Creating new 'Temporary Internet Files' directory
                MD "Temporary Internet Files"

        IF EXIST "Temp" (
            @ECHO Removing directory 'TEMP'
            IF [%%F] == [%username%] (
                @PUSHD "Temp"
                FOR /F %%E IN ('DIR /AD /B') DO (
                    @PUSHD %%E
                        FOR /F %%G IN ('DIR /AD /B') DO (
                            @ECHO Removing %%G
                            RD /S /Q %%G
                ) ELSE (
                    RD /S /Q "Temp"
                    @ECHO Creating new 'TEMP' directory
                    MD "Temp"
@ECHO Finished: %time%


Instructions For Use

  1. Select the code above and save as plain text file with an extension of either .CMD or .BAT
  2. Copy to one of your terminal servers
  3. Right click and choose Run As Administrator

If your users folder is not in C:\Users, make sure you change line 3 above to the correct location.