Matthijs's blog

Virtualization tools and stuff. By Matthijs ten Seldam

WakeUp-Machines – A PowerShell script for Wake On LAN

WakeUp-Machines – A PowerShell script for Wake On LAN

  • Comments 32
  • Likes

 

These days, I give a lot of presentations and demos on Windows Server 2012. Of course, many involve Hyper-V related demos. Although I run most demos from PowerShell, since now I can, I modify a lot in my demo environment. And since I frequently use my environment I need to be able to prepare my start my environment, prepare it, run the demos, revert to the state it was in when I started and finally shut down my environment.

At least the start, preparation (including tests), reverting and shutting down are all done using PowerShell scripts. Sometimes I have only 15 minutes to connect my machines to power and network. In that time, I don’t have time to manually start everything and use a document to follow what needs to be done. And what about some basic tests to verify everything is working correctly?

Think about it: starting the machines, waiting until they are alive, connecting through RDP to some of them and some virtual machines, starting VMconnect sessions, starting Hyper-V Manager, Server Manager, PowerShell console, configuring networks, etc, etc. Quite easy to forget some steps and hit issues during demos.

I normally don’t connect my presentation machine to the external monitor (large screen, beamer, etc.) until the environment is ready. But to show the power of automation, I did show a technical audience once how I start my environment (up to the point when it is ready). People were obviously impressed with the amount of automation I used.

Last month I presented at a large Microsoft event and got some questions from colleagues as well on how I started my machines without touching them. The script used Wake On LAN, something most machines support these days and which is on most of them on by default. I simply connect my machines to my switch and turn them on from my presentation machine.

In this post, I share my script for others to use. The script follows some PowerShell best practices:

· The name of the script is in the Verb-Noun form.

· The script is documented with comment based help.

The only thing you need to get it to work is the Wake On LAN tool. Other than that, you should be able to use it right away, once you have created the CSV file with machine names.

So how does it work?

The script uses a CSV file with machines you want to wake up. It starts with the first and works its way down the list. Not only does it wake the machines, it also sends echo requests to verify IP connectivity. It continues to send requests (number configurable) and then goes on to the next machine with wake up. I have developed a progress bar which displays progress on the wake up, the echo request and remaining phase of sending/receiving echo requests/replies.

    
WakeUp-Machines
  1. #######################################################
  2. ##
  3. ## WakeUp-Machines.ps1, v1.0, 2012
  4. ##
  5. ## Created by Matthijs ten Seldam, Microsoft
  6. ##
  7. #######################################################
  8.  
  9. <#
  10. .SYNOPSIS
  11. Starts a list of physical machines by using Wake On LAN.
  12.  
  13. .DESCRIPTION
  14. WakeUp-Machines starts a list of servers using a Wake On LAN tool. It then sends echo requests to verify that the machine has TCP/IP connectivity. It waits for a specified amount of echo replies before starting the next machine in the list.
  15.  
  16. .PARAMETER Machines
  17. The name of the file containing the machines to wake.
  18.  
  19. .PARAMETER Interface
  20. The IP address of the interface to use to wake up the machines.
  21.  
  22. .PARAMETER Subnet
  23. The subnet mask of the interface to use to wake up the machines.
  24.  
  25. .EXAMPLE
  26. WakeUp-Machines machines.csv 192.168.0.1 255.255.255.0
  27.  
  28. .EXAMPLE
  29. WakeUp-Machines c:\tools\machines.csv 192.168.0.1 255.255.255.0
  30.  
  31. .INPUTS
  32. None
  33.  
  34. .OUTPUTS
  35. None
  36.  
  37. .NOTES
  38. Make sure the Wake On LAN command line tool is available in the same location as the script!
  39.  
  40. The CSV file with machines must be outlined using Name, MAC Address and IP Address with the first line being Name,MacAddress,IpAddress.
  41. See below for an example of a properly formatted CSV file.
  42.  
  43. Name,MacAddress,IpAddress
  44. Host1,A0DEF169BE02,192.168.0.11
  45. Host3,AC1708486CA2,192.168.0.12
  46. Host2,FDDEF15D5401,192.168.0.13
  47.  
  48. .LINK
  49. http://blogs.technet.com/matthts
  50. #>
  51.  
  52.  
  53. param(
  54.     [Parameter(Mandatory=$true, HelpMessage="Provide the path to the CSV file containing the machines to wake.")]
  55.     [string] $Machines,
  56.     [Parameter(Mandatory=$true, HelpMessage="Provide the IP address of the interface to use for Wake On LAN.")]
  57.     [string] $Interface,
  58.     [Parameter(Mandatory=$true, HelpMessage="Provide the subnet mask of the interface to use for Wake On LAN.")]
  59.     [string] $Subnet
  60.     )
  61.  
  62.  
  63. ## Predefined variables
  64. $WolCmd=".\wolcmd.exe"
  65. $TimeOut = 30
  66. $Replies = 10
  67.  
  68. clear;Write-Host
  69.  
  70. ## Verify if WOL tool exists
  71. try
  72. {
  73.     Get-ChildItem $WolCmd | Out-Null
  74. }
  75. Catch
  76. {
  77.     Write-Host "$WolCmd file not found!";Write-Host
  78.     exit
  79. }
  80.  
  81. ## Read CSV file with machine names
  82. try
  83. {
  84.     $File=Import-Csv $Machines
  85. }
  86. Catch
  87. {
  88.     Write-Host "$Machines file not found!";Write-Host
  89.     exit
  90. }
  91.  
  92.  
  93. $i=1
  94. foreach($Machine in $File)
  95. {
  96.     $Name=$Machine.Name
  97.     $MAC=$Machine.MacAddress
  98.     $IP=$Machine.IpAddress
  99.  
  100.     ## Send magic packet to wake machine
  101.     Write-Progress -ID 1 -Activity "Waking up machine $Name" -PercentComplete ($i*100/$file.Count)
  102.     Invoke-Expression "$WolCmd $MAC $Interface $Subnet" | Out-Null
  103.  
  104.     $j=1
  105.     ## Go into loop until machine replies to echo
  106.     $Ping = New-Object System.Net.NetworkInformation.Ping
  107.     do
  108.     {
  109.         $Echo = $Ping.Send($IP)
  110.         Write-Progress -ID 2 -ParentID 1 -Activity "Waiting for $Name to respond to echo" -PercentComplete ($j*100/$TimeOut)
  111.         sleep 1
  112.         
  113.         if ($j -eq $TimeOut)
  114.         {
  115.             Write-Host "Time out expired, aborting.";Write-Host
  116.             exit
  117.         }
  118.         $j++
  119.     }
  120.     while ($Echo.Status.ToString() -ne "Success" )
  121.  
  122.     ## Machine is alive, keep sending for $Replies amount
  123.     for ($k = 1; $k -le $Replies; $k++)
  124.     {
  125.        Write-Progress -ID 2 -ParentID 1 -Activity "Waiting for $Name to respond to echo" -PercentComplete (100)
  126.        Write-Progress -Id 3 -ParentId 2 -Activity "Receiving echo reply"  -PercentComplete ($k*100/$Replies)
  127.        sleep 1
  128.     }
  129.     $i++
  130.     Write-Progress -Id 3 -Completed $true
  131.     $Ping=$null
  132. }
   

You can download the script, tool and sample machine.csv here.

 

#######################################################

Update, June 3rd 2012

I have updated my script to be able to do without the wolcmd tool. As pointed out in the comments, it is possible to do this from within PowerShell. So I did some research in MSDN and rewrote my script.

The switches have changed as well. You can now provide the time out, repeat and number of magic packets to send. Run Get-Help .\WakeUp-Machines.ps1 to get additional help (-detailed, –examples, –full all supported).

  
WakeUp-Machines
  1. #######################################################
  2. ##
  3. ## WakeUp-Machines.ps1, v1.1, 2012
  4. ##
  5. ## Created by Matthijs ten Seldam, Microsoft
  6. ##
  7. #######################################################
  8.  
  9. <#
  10. .SYNOPSIS
  11. Starts a list of physical machines by using Wake On LAN.
  12.  
  13. .DESCRIPTION
  14. WakeUp-Machines starts a list of servers using Wake On LAN magic packets. It then sends echo requests to verify that the machine has TCP/IP connectivity. It waits for a specified amount of echo replies before starting the next machine in the list.
  15.  
  16. .PARAMETER Machines
  17. The name of the file containing the machines to wake.
  18.  
  19. .PARAMETER TimeOut
  20. The number of seconds to wait for an echo reply before continuing with the next machine.
  21.  
  22. .PARAMETER Repeat
  23. The number of echo requests to send before continuing with the next machine.
  24.  
  25. .EXAMPLE
  26. WakeUp-Machines machines.csv
  27.  
  28. .EXAMPLE
  29. WakeUp-Machines c:\tools\machines.csv
  30.  
  31. .INPUTS
  32. None
  33.  
  34. .OUTPUTS
  35. None
  36.  
  37. .NOTES
  38. Make sure the MAC addresses supplied don't contain "-" or ".".
  39.  
  40. The CSV file with machines must be outlined using Name, MAC Address and IP Address with the first line being Name,MacAddress,IpAddress.
  41. See below for an example of a properly formatted CSV file.
  42.  
  43. Name,MacAddress,IpAddress
  44. Host1,A0DEF169BE02,192.168.0.11
  45. Host3,AC1708486CA2,192.168.0.12
  46. Host2,FDDEF15D5401,192.168.0.13
  47.  
  48. .LINK
  49. http://blogs.technet.com/matthts
  50. #>
  51.  
  52.  
  53. param(
  54.     [Parameter(Mandatory=$true, HelpMessage="Path to the CSV file containing the machines to wake.")]
  55.     [string] $Machines,
  56.     [Parameter(Mandatory=$false, HelpMessage="Number of unsuccesful echo requests before continuing.")]
  57.     [int] $TimeOut=30,
  58.     [Parameter(Mandatory=$false, HelpMessage="Number of successful echo requests before continuing.")]
  59.     [int] $Repeat=10,
  60.     [Parameter(Mandatory=$false, HelpMessage="Number of magic packets to send to the broadcast address.")]
  61.     [int] $Packets=2
  62.     )
  63.  
  64.  
  65. Set-StrictMode -Version Latest
  66.  
  67. clear;Write-Host
  68.  
  69. ## Read CSV file with machine names
  70. try
  71. {
  72.     $File=Import-Csv $Machines
  73. }
  74. Catch
  75. {
  76.     Write-Host "$Machines file not found!";Write-Host
  77.     exit
  78. }
  79.  
  80. function Send-Packet([string]$MacAddress, [int]$Packets)
  81. {
  82.     <#
  83.     .SYNOPSIS
  84.     Sends a number of magic packets using UDP broadcast.
  85.  
  86.     .DESCRIPTION
  87.     Send-Packet sends a specified number of magic packets to a MAC address in order to wake up the machine.  
  88.  
  89.     .PARAMETER MacAddress
  90.     The MAC address of the machine to wake up.
  91.  
  92.     .PARAMETER
  93.     The number of packets to send.
  94.     #>
  95.  
  96.     try
  97.     {
  98.         $Broadcast = ([System.Net.IPAddress]::Broadcast)
  99.  
  100.         ## Create UDP client instance
  101.         $UdpClient = New-Object Net.Sockets.UdpClient
  102.  
  103.         ## Create IP endpoints for each port
  104.         $IPEndPoint1 = New-Object Net.IPEndPoint $Broadcast, 0
  105.         $IPEndPoint2 = New-Object Net.IPEndPoint $Broadcast, 7
  106.         $IPEndPoint3 = New-Object Net.IPEndPoint $Broadcast, 9
  107.  
  108.         ## Construct physical address instance for the MAC address of the machine (string to byte array)
  109.         $MAC = [Net.NetworkInformation.PhysicalAddress]::Parse($MacAddress)
  110.  
  111.         ## Construct the Magic Packet frame
  112.         $Frame = [byte[]]@(255,255,255, 255,255,255);
  113.         $Frame += ($MAC.GetAddressBytes()*16)
  114.  
  115.         ## Broadcast UDP packets to the IP endpoints of the machine
  116.         for($i = 0; $i -lt $Packets; $i++) {
  117.             $UdpClient.Send($Frame, $Frame.Length, $IPEndPoint1) | Out-Null
  118.             $UdpClient.Send($Frame, $Frame.Length, $IPEndPoint2) | Out-Null
  119.             $UdpClient.Send($Frame, $Frame.Length, $IPEndPoint3) | Out-Null
  120.             sleep 1;
  121.         }
  122.     }
  123.     catch
  124.     {
  125.         $Error | Write-Error;
  126.     }
  127. }
  128.  
  129. $i=1
  130. foreach($Machine in $File)
  131. {
  132.     $Name=$Machine.Name
  133.     $MacAddress=$Machine.MacAddress
  134.     $IPAddress=$Machine.IpAddress
  135.  
  136.     ## Send magic packet to wake machine
  137.     Write-Progress -ID 1 -Activity "Waking up machine $Name" -PercentComplete ($i*100/$file.Count)
  138.     Send-Packet $MacAddress $Packets
  139.  
  140.     $j=1
  141.     ## Go into loop until machine replies to echo
  142.     $Ping = New-Object System.Net.NetworkInformation.Ping
  143.     do
  144.     {
  145.         $Echo = $Ping.Send($IPAddress)
  146.         Write-Progress -ID 2 -ParentID 1 -Activity "Waiting for $Name to respond to echo" -PercentComplete ($j*100/$TimeOut)
  147.         sleep 1
  148.         
  149.         if ($j -eq $TimeOut)
  150.         {
  151.             Write-Host "Time out expired, aborting.";Write-Host
  152.             exit
  153.         }
  154.         $j++
  155.     }
  156.     while ($Echo.Status.ToString() -ne "Success" )
  157.  
  158.     ## Machine is alive, keep sending for $Replies amount
  159.     for ($k = 1; $k -le $Repeat; $k++)
  160.     {
  161.        Write-Progress -ID 2 -ParentID 1 -Activity "Waiting for $Name to respond to echo" -PercentComplete (100)
  162.        Write-Progress -Id 3 -ParentId 2 -Activity "Receiving echo reply"  -PercentComplete ($k*100/$Repeat)
  163.        sleep 1
  164.     }
  165.     $i++
  166.     Write-Progress -Id 3 -Completed $true
  167.     $Ping=$null
  168. }
    
 
Comments
  • But there's script which doesn't require exe tool, why not use that approach?

  • I have rewritten my script to send the magic packets from code. So an external tool is no longer needed.

  • Is there a solution where Wake On Lan will work, on PCs, which have dynamic ip addresses, rather than static ip addresses?

  • Very nice, thanks for doing this with Windows resources instead of going to a separate binary.

  • This is very useful to me as I manage a lab of computers.  It's great this can be done without the wolcmd tool as well.

    Something that would take this a step further for me would be the ability to have each remote computer login to a local account from a PowerShell script.  Is this possible?  If so, how would I go about doing that?

  • @Dan

    The Wake On LAN will work on both because it only sends a magic packet to specific MAC addresses. The script uses IP-addresses only to wait for a response and then continue.

    @Ryan

    I would need more info as to what you want to achieve. Windows supports autologon, you can logon through a remote PowerShell session or invoke remote PowerShell scripts. is that what you are looking for?

  • I've tried both incarnations of your script and both fail with an error message:

    . : Property 'Count' cannot be found on this object. Make sure that it exists.

    At C:\temp\WakeUp-Machines.ps1:137 char:93

    +     Write-Progress -ID 1 -Activity "Waking up machine $Name" -PercentComplete ($i*100/$file. <<<< Count)

       + CategoryInfo          : InvalidOperation: (.:OperatorToken) [], RuntimeException

       + FullyQualifiedErrorId : PropertyNotFoundStrict

    In spite of the error the script does wake up a machine.  What am I missing?

  • Matthijs,

    Please allow me to more specific about what I want.  I want the machines to login to the desktop so that the computer becomes available to lab users that aren't given any login credentials.  I don't want to use auto login because I want to be able to control when the machines become available to the lab users.  Thanks for your help.

  • Dave,

    I'm betting that you only have one machine in your csv.  That's how I was able to re-create your problem anyways.

    If this is the case, the problem is line 72 (in the second incarnation).  The Import-Csv command will only return an array if you have multiple machines in your list.  If you only have one machine in your list it returns a scalar, which doesn't have a Count property.

    One way to fix this is to force the file variable to be an array like so:

    $File = @(Import-Csv $Machines)

    I hope this helps.

  • Dan,

    I too have a dynamic IP envirnment.  I looked up System.Net.NetworkInformation.Ping.Send(string) and it looks like it will take either a host name or an IP address.  I plan on giving it a host name.  You may want to do the same.  Then there is no longer any reason to have an IP address column in the CSV.

  • Ryan,

    That fix resolved my issue.  Thanks!

  • Guys,

    the IP address is not necessary at all for waking the machines. I use it to verify whether the specific machine has its IP-stack loaded, so basically if the wake up has succeeded. Indeed, if you only want to wake machines and are not interested in the result, you can do without.

  • Hi there,

    Another script for Wake On LAN (WOL). Supports both IPv4 and IPv6. Can be enhanced with a lot of other features such as reading files and user input. Works on my home network, but not tested on a more complex environment. If you test it, please share feedback. Thanks.

    <# ---- For security reason, the code is fully commented ----

    # Consider also possibility to read / parse an external file, or a user interface to collect addresses

    # Consider possibility to read from ARP tables, DHCP routing tables, DNS lists, Domain Controller, etc.

    <#

    $szMACAddress = [string]"AA-BB-CC-DD-EE-FF"

    $szMACAddress = [string]"AA:BB:CC:DD:EE:FF"

    $szMACAddress = [string]"AA BB CC DD EE FF"

    $szMACAddress = [string]"AABBCCDDEEFF"

    #>

    <# ---- For security reason, the code is fully commented ----

    $szMACAddress = [string]"AA-BB-CC-DD-EE-FF"

    # The "Magic Packet" starts with six pairs of 0xFF values

    [Byte[]]$szPacket = [Byte[]] (,0xFF * 6)

    # MAC addresses are defined by six pairs of hexadecimal values that could be separated by a dash (-),

    # a column (:), or a space ( ); the address could be also twelve hexadecimal values without separator

    if([Regex]::IsMatch($szMACAddress, '[A-Fa-f0-9]{2}\p{Pd}[A-Fa-f0-9]{2}\p{Pd}[A-Fa-f0-9]{2}\p{Pd}[A-Fa-f0-9]{2}\p{Pd}[A-Fa-f0-9]{2}\p{Pd}[A-Fa-f0-9]{2}'))

    {

       # MAC address has the form 'xx-xx-xx-xx-xx-xx'

       $szPacket += (($szMACAddress.split('-') | foreach {[byte] ('0x' + $_)}) * 16)

    }

    elseif([Regex]::IsMatch($szMACAddress, '[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}'))

    {

       # MAC address has the form 'xx:xx:xx:xx:xx:xx'

       $szPacket += (($szMACAddress.split(':') | foreach {[byte] ('0x' + $_)}) * 16)

    }

    elseif([Regex]::IsMatch($szMACAddress, '[A-Fa-f0-9]{2} [A-Fa-f0-9]{2} [A-Fa-f0-9]{2} [A-Fa-f0-9]{2} [A-Fa-f0-9]{2} [A-Fa-f0-9]{2}'))

    {

       # MAC address has the form 'xx xx xx xx xx xx'

       $szPacket += (($szMACAddress.split(' ') | foreach {[byte] ('0x' + $_)}) * 16)

    }

    elseif([Regex]::IsMatch($szMACAddress, '[A-Fa-f0-9]{12}'))

    {

       # MAC address has no separator between pairs of hexadecimal values

       $szPacket += for ($j = 0; $j -le 15; $j++) { for ($i = 0; $i -le ($szMACAddress.Length -1); $i += 2) {[byte] ('0x' + $szMACAddress[$i] + $szMACAddress[$i+1])}}

    }

    else

    {

       # MAC address format is not recognized

       Throw "ERROR: MAC Address is not recognized !"

    }

      ---- For security reason, the code is fully commented ----

    #>

    (1 / 2)

  • ( 2 / 2 )

    <# ---- For security reason, the code is fully commented ----

    # Verify if we support IPv6 or IPv4

    if([System.Net.Sockets.Socket]::OSSupportsIPv6)

    {

       Write-Output "Your system supports IPv6"

       # Create a new communication channel using the UDP protocol

       $netUDPClient = New-Object System.Net.Sockets.UdpClient([System.Net.Sockets.AddressFamily]::InterNetworkV6)

       # Connect to the network using port 9 (this is the standard for WOL) on the multicast "All routers" address

       # To send to multicast address "All hosts", use address "FF02::1" instead

       $netUDPClient.Connect([System.Net.IPAddress]::Parse("FF02::2"), 9)

    }

    elseif([System.Net.Sockets.Socket]::OSSupportsIPv4)

    {

       Write-Output "Your system supports IPv4"

       # Create a new communication channel using the UDP protocol

       $netUDPClient = New-Object System.Net.Sockets.UdpClient([System.Net.Sockets.AddressFamily]::InterNetwork)

       # Connect to the network using port 9 (this is the standard for WOL) on the broadcast address

       $netUDPClient.Connect(([System.Net.IPAddress]::Broadcast),9)    

    }

    else

    {

       Throw "ERROR: Neither IPv4, nor IPv6 are supported !"

    }

    # Send the magic packet to the network

    [void]$netUDPClient.Send($szPacket, $szPacket.Length)

    # Release managed and unmanaged resources

    [void]$netUDPClient.Dispose()

    # Remove used variables

    Remove-Variable netUDPClient, szPacket, szMACAddress

      ---- For security reason, the code is fully commented ----

    #>

    Cheers,

    Didier

  • Hello,

    I am totally new to all this scripting business.

    I would like to automate this script, ie run it without any input from me. So far, I have found a batch script that will run it without having to input the csv filename but I can't get it to automate so that I don't need to type in text to send as the wake up call. How can I do this?

    regards,

    Caroline

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment