Wake on LAN for Hyper-V Guests

When you turn your VM off, it’s off.  As in off-off, off: equivalent to powering it off and pulling out all the cables.  This is why you can’t use Wake on LAN (WoL) for Hyper-V guests because the network adaptor for the VM is off-off.  Which is annoying if you have a bunch of VMs that you use on a regular basis; to boot them all you have to first RDP onto the Hyper-V host and then boot each one in turn – I know, I know, a real first world problem – but it annoys me!  There are a couple of products available to help with this but they are all pretty overpriced in my opinion, especially as they provide something so simple.

WoL is pretty simple: it’s a UDP broadcast to the entire network, usually on port 7, using something called a “magic packet”.  This packet contains the MAC address of the computer to start (amongst other data).  A network adaptor listens out for these packets and reacts by powering on the computer; this all happens within the network card and the computer never gets involved (it’s off-off remember).  WoL doesn’t wait for a response, there’s no encryption/security for it, and it has no additional options/configurations other than this.

This simplicity is great news though because it means that PowerShell can be used to recreate the missing WoL functionality for Hyper-V guests.  The below script sits on your Hyper-V host listening for magic packets.  When it receives one it checks to see if the MAC Address contained matches the MAC Address of one of the VMs that it is hosting, if so it turns it on.  Simple.

With this script I can now take out my Windows Phone, open the Wake-on-LAN app, send the magic packets, and boot up my entire lab before I’ve even made it up the stairs to my office   🙂

untitled

Set the script to run permanently on your Hyper-V host, as per the above screenshot.  Upon start the code will enumerate the MAC Addresses for all VMs detected locally and will then sit in the background reading any magic packet that it sees go out across the network and react accordingly.  All informational and error messages are written out to the console window.
Prerequisites:
  • UDP port 7 should opened on your Windows Server firewall – not usually a requirement because WoL is processed by the hardware, but here we are having to process it within the operating system.
  • PowerShell execution policy will need to be set to allow the script to run
  • The script currently runs in a logged on session, so a user session needs to stay open (I’ll attempt to address this in future versions).  I have my server logged on and locked to accommodate this.
  • A WoL app on your phone and/or computer to send the requests.  Some apps require specifying the IP address of the computer as well as it’s MAC Address.  If this is the case you can either specify the IP of your Hyper-V host or use 255.255.255.255.
The script is very much a work in progress project at the moment, but please try it and feedback to me any issues you have, or any other feedback, via the comments at the bottom.  As I improve it I’ll update this blog.  I’ve only tested the script on Windows Server 2016, using a non-admin account.

Dynamically set Computer name of Physical and Virtual Machines via CustomSettings.ini

I needed a clean way to set the hostname of physical and virtual machines in MDT via the customsettings.ini file. Sure I could use %SerialNumber% variable but if I deploy onto a virtual machine I would hit an error because the Serial Number of a VM tends to be too long for a hostname.

So rather than write a script and have additional steps in the Task Sequence I got the desired outcome with a few extra lines in the customsettings.ini:

Here’s how:

[Default]

Subsection=VM-%ISVM%

[VM-TRUEset ]

OSDComputerName=#REPLACE(“VM-%MACADDRESS001%”,”:”,””)#

[VM-FALSE]

OSDComputerName=PH-%SerialNumber%

The first line “Subsection=VM-%ISVM%” is added to the Default section of the customsettings.ini file. This line essentially tells ZTIGather to process another section in the ini file. The section we want to process is either VM-TRUE or VM-False. Which section is processed will depend on the in-built %IsVM% Task Sequence Variable.

The next part of the solution is to set the OSDComputerName variable.

For physical machines we simply user the %SerialNumber% variable. In the example I prefix the hostname with PH.

For Virtual machines we use the MAC address instead of the serial number. This ensures we stay within the 15 character hostname limit. We cannot use the %MACADDRESS001% variable directly, we first need to remove the “:” separators. To do this we can get ztigather to process code directly from the customsettings.ini. Anything inside “#” is treated as code, in our example we are simply using the Replace function to remove the “:”.

This works with an SCCM OSD Task Sequence too (with MDT integration).

Automating the Import of Office 2013 Language Packs into MDT with PowerShell

I recently needed to add Office Language Pack support into an MDT deployment and found this to be a looooong time consuming task…especially if you need to support a number of languages. So I ended up writing a PowerShell script to automate the steps.

Some background first. The following blog by Johan Arwidmark and Patrick Zovistoski is a great post and should be used in conjunction with my script:

deploymentresearch.com – Installing Office 2013 Language Packs in MDT

The high level steps to Install Office Language Packs with MDT are as follows:

  1. Obtain the Office 2013 Language Pack DVD media from the Microsoft VL website.
  2. Create the correct Folder structure for each language you need to import (containing all component language folders, setup files etc…)
  3. Update the config.XML for each language to run silent and suppress reboot.
  4. Import each language into MDT as an application.
  5. Add the Applications into the MDT Task Sequence.

My script automates the first 4 steps…so all you need to do manually is add the languages to the Task Sequence 🙂

So onto the script.  You need to do the following:

  1. Mount the two Office 2013 Language Pack ISO files on your MDT server.
  2. Create a staging folder on your server.  This location will be used to create the source folders for the Language packs.
  3. Copy the script and CSV file (Download from here) into the staging folder on your MDT Server.
  4. Update the varibles at the top of the script with details of your deployment share, path of the Language Pack DVD’s etc…SetVars
  5. Update the CSV file to reflect the languages you want to import.csv
  6. Run the script.

The script will create the source folders for each language you specify and update the config XML’s for each language:Staging afterFinally the script will import each language pack into your MDT deployment share:DSAfter

And that’s it… A good hour of work down to about 5 mins.

Download the script from here

Enjoy 🙂

Updating your NO-IP account with PowerShell

This is a little off topic from our usual stuff, but I could find no PowerShell examples anywhere on the web for this so thought it could be useful to someone else!

I run a server at home and have been using No-IP services for free DNS for years.  I had my Internet router configured to update NO-IP directly when my external IP address changed.  Recently though I changed the router for a new one, only to discover that it only supports updates to the DynDNS service, which is no longer free.

Rather than installing the No-IP agent onto my server I wondered if it would be possible to script it in PowerShell and run it as a scheduled task.  There are plenty of Linux and Python scripts on the web that do this, but none I could find in PowerShell; so I wrote my own 🙂

It’s simple to use, just follow the below steps:

  1. Create new EventLog source by running the following command from an elevated PowerShell window: New-EventLog -LogName “Application” -Source “NO-IP Updater”  –  all events logged by the script will appear in this log
  2. Add the below code to a PowerShell script file
  3. Modify the values at the top of the script with your own values
  4. Set it to run as a scheduled task

The script currently pushes an update to the No-IP servers on a schedule, even if your IP has not changed.  Also, it only logs in the Application EventLog the return code from No-IP and does not take any actions based on the value returned.  I’ll fix both of this points at some point in the future if they prove to be in demand.

Finally, it uses an external site in order to discover your external IP address.  This request can sometimes fail for no reason.  When this happens there is a routine in the script to try again from a different source.

 

# Set static content
$myUser = “MyUser”
$myPass = “MyPassword”
$myHost = “MyDomain.no-ip.org”

Write-Eventlog  -Logname ‘Application’ -Source ‘No-IP Updater’ -EventID 666 -EntryType Information -Message “Starting…”

# Fetch external IP
Write-Host “Fetching external IP…”
$myIP = (Invoke-WebRequest curlmyip.com).Content
$myIP = $myIP.Trim()

Write-Host “Value $myIP found, validating…”

# Validating IP
$IPCheck = [bool]($myIP -as [ipaddress])
Write-Host “Validation result: $IPCheck $myIP”

If ($IPCheck -eq $false)
{
    Write-Host “Failed to get external IP.  Trying with different host”
   
    $myIP = (Invoke-WebRequest ifconfig.me/ip).Content
    Write-Host “External IP: $myIP”

    Exit
}

Write-Host “External IP: $myIP”

# Build URL for update
$URL = “https://dynupdate.no-ip.com/dns?username=$myUser&password=$myPass&hostname=$myHost&ip=$myIP”

# Print output
Write-Host “Updating host $myHost with IP $myIP”

# Updated
$update = Invoke-WebRequest $URL

# Write to EventLog
$strToLog = “Error returned: $update`r`nFull HTTPS string used: $URL”
Write-Host “Writing to log: $strToLog”
Write-Eventlog  -Logname ‘Application’ -Source ‘No-IP Updater’ -EventID 666 -EntryType Information -Message $strToLog

Snippet #2 – Checking for free disk space with PowerShell

Inspired by Dan’s last post, I thought I would share some more PowerShell goodness. A very quick one to start with: How to check for free disk space with PowerShell.

As you would expect with PowerShell its amazingly simple. Here is the function:

Function GetFreeDiskSpace([string]$DriveLetter, [string]$Measurement)

{

$Drives=Get-WmiObject -Class Win32_LogicalDisk

foreach($Drive in $Drives)

{

If ($Drive.Name -eq $DriveLetter)

{

[int]$GLOBAL:Freespace=$Drive.FreeSpace/“1$Measurement

}

}

}

The function simply queries the WMI LogicalDisk class and then converts the fresspace to GB, MB or KB. For example, if you want to find out how much free disk space is available for the c:\ in Gigabytes, you would call the function as follows:

GetFreeDiskSpace “C:” “GB”

write-host “Freespace = $Freespace

That’s all for now 🙂