I recently had reason to reconfigure a VMware vRealize Automation (vRA) 6.2.2 appliance that was using the default (i.e. inbuilt) vRealize Orchestrator (vRO) 6 instance to use an external one instead, and came across an interesting problem that I felt was worth blogging.

The mysterious symptom I then observed was the busy cursor (commonly known as 'The Circle of Doom') spinning constantly and indefinitely when trying to add an advanced services endpoint in vRA. As you may be aware, changing to an external vRO server or vice-versa deletes any existing advanced services endpoints, so adding them again is a necessary step.

When adding the endpoint, selecting the plug-in was fine:

But on moving to the details tab, the dreaded circle appeared:

I also noticed that, when attempting to create a new Service Blueprint, there was no vRO workflow tree displayed in the vRA UI:

The problem transpired to be one of authorisation - the account I had specified for authentication under server configuration didn't actually have rights to log on to the vRO server. As I had tested connectivity and received a success message in the first step, I made the assumption that all was OK, but the test clearly can't be treated as an indication that everything is fine between the vRA and the vRO server.

After rectifying the underlying problem, I was then able to add advanced services endpoints, and the vRO workflow tree was populated correctly:

I hope this information is helpful to someone who may be troubleshooting a similar problem.

I am working on a vSphere 6 design for a customer, and they have requested that password expiration for the vCenter Appliance root password be disabled. By default this password expires after 365 days, so it could be easy to forget to change it before it does (it is possible to configure the appliance to notify you by email of impending password expiry, but for that to work you must remember to specify an email address for the root account first).

The vSphere 6 documentation details the command that you need to use to disable password expiration for the root user, but there are some prerequisite steps that aren't covered in the same place, so I though I'd document the whole process here.

First, decide whether you want to access the vCenter Server Appliance (vCSA) using SSH, or its Direct Console User Interface (DCUI). If you want to use SSH, you will need to enable SSH access. If this wasn't enabled in the deployment wizard when the appliance was deployed, use the vCSA DCUI console to enable it (F2 – Customize System - Troubleshooting Mode Options) or the Web Client under Home - System Configuration - Nodes – (select node) - Manage - Settings – Access

Use your favourite SSH client to connect to the vCSA and log on as root, or enable the shell using Alt-F1 at the vCSA DCUI console and do the same

Enable bash shell access. To do this, issue the "shell.set --enabled True" command to the appliance shell. Note that the default timeout for this enablement is 1 hour, after that time the bash shell will be automatically disabled. You can use the "shell.get" command at the appliance shell Command> prompt to show the time remaining (you can also check it in the Web Client). As an alternative, you can enable the bash shell under Troubleshooting Mode Options in the DCUI if you prefer

Start a bash shell using the "shell" command

Run "chage -l root". This will display the current settings for the root user:

Run "chage -M -1 root" to disable password expiration (i.e. set 'Maximum' to -1) for root

Last Thursday, Packt Publishing introduced the '$5 eBook Bonanza'. Basically this means you can order ANY eBook or video from their web site for just $ 5.00 – or £ 3.00 for us UK-based folks. That's a massive bargain! You can order as many eBooks or videos as you like for this price before the offer expires on 6 Jan 2015. The products are available in a range of formats and are DRM-free.

You can find out more about this great offer here. While you do that, I'm off to their web site to get some last minute Christmas presents for myself!

I was lucky enough to be able to attend the fourth annual UK VMware User Group (VMUG) meeting at the National Motorcycle Museum in Birmingham last week. I have for quite some time been a regular attendee at the London VMUGs, but have only been to one UK meeting before - that was in fact the first one ever, so one of the first things I noticed was how much the event has grown over the last three years.

VMUGs are a great opportunity to meet like-minded professionals, customers and vendors, and most importantly gain and share information about VMware and partner products and solutions and participate in the fantastic community that surrounds VMware virtualisation worldwide.

Unfortunately I wasn’t able to attend the ‘vCurry’ event and quiz the previous evening but the day started with an introduction from the always entertaining Alaric Davies, which included an update on the previous evening’s festivities, so I was soon up to speed with those!

Helpfully, a number of the sessions were recorded on video and the VMUG Committee have kindly uploaded these recordings to YouTube - the playlist can be found here. Slides from many of the sessions and links to the individual videos on YouTube have also been uploaded to the London VMUG workspace on Box.com here.

Joe Baguley, CTO of VMware EMEA, gave the opening keynote, which was entertaining, as Joe’s sessions always are. Joe titled this session ‘CTO Rant-as-a-Service’. It wasn’t so much of a rant, but it was a great view of what's going on in the industry from VMware’s point of view. One of the key themes was the significant decrease that will be seen in the time between the traditional IT refresh cycles going forward, and how the whole Software Defined Enterprise / SDDC concept supports that. He also talked about some of the exciting announcement that VMware made at VMworld, such as EVO:RAIL and EVO:RACK. I noted that Joe also commented that the VMUG is the best community event that he has involvement with, which reinforces my views on the significance and importance of VMUGs within the ecosystem. The video recording of this session can be found here.

The next session I attended was Julian Wood’s ‘The Unofficial Low Down on Everything Announced at VMworld’. I wasn’t able to attend VMworld this year, so I thought this would be a great opportunity for me to get an overview of pretty much all the new products and improvements that were announced at VMworld. I was right - Julian put together and presented an excellent, information-packed session, and to be honest I was struggling to make coherent notes without missing anything as the information was coming thick and fast, but fortunately Julian's comprehensive slides (each of which includes supporting links) are available here, and the video recording here.

The next session I selected was ‘What's Coming for vSphere in Future Releases’ presented by VMware’s Chief Technologist Duncan Epping. Duncan expanded on a number of the products that Joe had mentioned in his keynote and also detailed some exciting improvements to existing products and features we all know and love! You can find the slides for this session here and watch the video here.

The first session I attended after lunch was presented by my good friend (and colleague again) Jonathan Medd, and was entitled ‘Designing Real-Word vCO Workflows for vRealize Automation Center (vCAC)’. This session was one of the reasons I really wanted to attend the UK VMUG this year – Jonathan is an expert in his field whose sessions always draw a good crowd. I am lucky enough to have worked with him personally for a number of years on and off, and every time I speak to him I learn something new, so I was expecting a good session. I am going to be involved in vCAC and vCO at Xtravirt because of my interest and skills in scripting and automation, so I was quite excited about hearing tips from someone who has most definitely ‘been there and done that’. And I wasn’t disappointed..!

The space available in the mezzanine section for this session was overcommitted by at least 100%, and people were crowding round the table two rows deep in places, which to my mind demonstrates the community interest in automation based around vCAC and vCO. Jonathan ran this as an interactive session and got everybody to think about important aspects of designing an automation process that need to be considered at an early stage, and we discussed within the group the pros and cons of many of the possible approaches. As someone who is just getting up to speed with these products, I found it (as I expected to) an incredibly interesting and informative session – thanks Jonathan!

The next session I selected was ‘vSphere Availability Updates and Tech Preview’ by Lee Dilworth, Principal Systems Engineer at VMware. This was a great opportunity to brush up on the significant number of improvements that VMware have made, and continue to make, in this area. The slides for this session have been uploaded here and you can watch the video here.

After this, I went along to a partner session, ‘Re-thinking Storage by Virtualizing Flash and RAM’ by Frank Denneman, who is Chief Evangelist at Pernix Data. Pernix Data are doing some exciting things with their ‘FVP Cluster’ technology which allows any VM to remotely access flash RAM on any other vSphere host, enabling fault tolerant storage write acceleration, with pretty impressive results. FVP supports all VM operations with no impact on performance, so features such as vMotion, DRS, HA, snapshots, VDP and SRM continue to operate transparently. Nice!

The final session of the day was a hugely entertaining closing keynote by Chris Wahl, a double VCDX, prolific blogger, author and vExpert from Chicago who describes himself as a ‘Virtualization Whisperer’! This session was entitled ‘Stop Being a Minesweeper’, and in it Chris talked us through his journey into automation and included a number of good resources to help people begin the learning process - these are in the slides here. You can watch the video of the session here - I highly recommend that you do!

Jane has written a great review of the event on her blog here. The Committee are running a competition for new community speakers, known as ‘V-Factor’! Entrants will have the opportunity to give a 10-minute lightning talk at the London VMUG on 22 Jan 2015 and could win one of a number of great prizes. You can find more information here if you are interested in entering.

The next London VMUG is 22 Jan 2015 and the next UK event is provisionally scheduled for 19 November 2015. Both will be great events, so put the dates in your diary now!

The session was a lively group discussion and the audience comprised of a great mix of people ranging from PowerCLI beginners right through to experts. It was my job as Alan's 'beautiful assistant' [don't know about that!] to capture the useful information flying around the room on a flip chart. With impressive use of his deciphering skills, Alan has written up the result on his personal blog in a series of four excellent posts. There is some good stuff in there for PowerCLI scripters at all stages in the learning process. You can find these posts via the links below – thanks Al!

Last week I had a requirement to perform a DNS lookup within a PowerShell script, using a DNS server other than the one that the machine in question was configured to use. This turned out to be quite a challenge and, having found a library that looked like it would do it, I ended up having to call on the expertise of my good friend and PowerShell MVP Jonathan Medd to help me out!

As I'm sure I can't be the first person that has attempted this in PowerShell using the library in question (or something similar) I felt it was worth blogging the methodology…

I won't bore you with too much of the background, but I was amending a script that I wrote a few years back which used the .NET class System.Net.Dns to perform a DNS lookup. This worked fine until my ISP changed the behaviour of their DNS servers to return a search page in the event that the host record in question was not found. Although this is probably helpful to a human being, I wanted my script to raise an error in the event that the host record wasn't found - and this was no longer happening because my ISP's DNS now returns the IP address of the server delivering the search page instead. Therefore, to my script, the DNS lookup was appearing to be successful!

The obvious answer to this is to use a DNS server that doesn't behave in this way. Google's DNS is an example of this. I didn't want to change my entire infrastructure to use Google's DNS servers, so the most sensible approach was to have just the script use them. This is where the next problem became apparent – System.Net.DNS does not allow the use of alternative DNS servers.

After some Googling, I came across the excellent and free 'SimpleDNS DNS Client Library for .NET' from JH Software here. This allows you to specify DNS servers to perform lookups against. However (perhaps because I'm not a .NET programmer) I couldn't work out for the life of me how to set the required property, and that is where Jonathan helped me out. The methodology is described below. I can't take any credit for this – it's all his work

The first step is obviously to download the SimpleDNS library from here, and put at least the Dynamic Link Library 'JHSoftware.DnsClient.dll' into a suitable location on the local machine, say 'C:\PowerShell\Libraries'. (There is a .CHM help file included in the download that is worth including too.)

Here is the entire code snippet used. An explanation of each line follows in the text below.

The next step is to create an array of .NET IPAddress objects which are set to Google's DNS server (8.8.8.8 and 8.8.4.4). This is done using the 'Parse' static method which converts a string into an IP address:

The 'LookupHost' method requires an additional parameter because we are using 'RequestOptions' – an enumeration of the IP protocol version to be used, which is called DnsClient.IPVersion. The base class is System.Enum. To create this in PowerShell with the correct value you need use the method below, specifying the protocol version with the appropriate member name. There is a great post from Lincoln Atkinson on using enumerations in PowerShell here.

$IPVersion=[JHSoftware.DnsClient+IPVersion]::IPv4

Once this has been done, we have all the parameters we need to call the LookupHost method and return a result:

I wanted just the IP address as a string in a variable for the script to process further, and how to do this eluded me initially until I realised that the $HostAddress object was in fact being returned by the library as a System.Net.IPAddress object, and effectively as an array. The MSDN documentation for a similar method of the System.Net.Dns class, the GetHostAddresses method, helped me to work this out. So returning the IP address alone is simply a matter of:

$HostAddress[0].ToString()

…and that's it!

Jonathan and I decided that we would both blog this information as it could potentially be useful to a number of people. He has taken this a stage further and turned the code above into a very cool function, making it easy to drop into a script for future re-use (I will be incorporating this into my script!). You can see his blog post on this here.

If you have a need to configure remote Syslog logging for all (or perhaps a subset) of your vSphere hosts at the same time, PowerCLI can help!

Let's assume that your remote Syslog server has the IP address 192.168.1.10. Using the following PowerCLI one-liner, you can configure all hosts managed by a particular vCenter to send Syslog data to this server:

I was troubleshooting an issue a week or so ago in conjunction with a Citrix Technical Support Engineer, and thought I would blog a handy tip that my esteemed colleague Phil Reeve pointed out to me, which assisted in tracking down the problem.

So, let me set the scene for you. We had discovered a problem where one of our Citrix Web Interfaces was no longer notifying users of the impending requirement to change their password, during the period prior to the date on which they would be forced to do so by Active Directory.

Citrix Technical Support had been working on this problem on our behalf for a few weeks, and had been able to reproduce it successfully. I won't go into great detail here, as this is a post about troubleshooting Group Policy and not Citrix problems – suffice to say that Citrix had narrowed it down to an incorrectly set registry value on a Citrix Zone Data Collector (ZDC) that the Web Interface was reading and processing.

The value in question was HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\passwordexpirywarning. On the ZDC, it had been incorrectly set to 0. The next challenge was to find out what had done this in order to be able to change it back to the correct value, and make it stay that way.

The "passwordexpirywarning" value can be set by local or Active Directory Group Policy via the setting "Interactive Logon: Prompt user to change password before expiration". A quick check in the Local Group Policy Editor (gpedit.msc) on the affected ZDC indicated it was being set by Active Directory Group Policy, as it was greyed out:

So how to track down which Group Policy Object (GPO) was applying this incorrect value? "Try GPResult.exe or the Group Policy Results Wizard in the GPMC" I hear you shout! Of course, I tried these tools, but to no avail, as they have a "limitation" (shall we call it?) in that they don't reliably show all the registry values that Group Policy processes – and this was one such case. I have seen the same thing many a time over the years I have been working with Active Directory, and to be honest, am quite surprised that this is still the case after all this time.

So finally, to the point of this post. Below is the tip I mentioned, in the form of a procedure that can be used should you find yourself in a similar position (paths and GUIDs have been changed to protect the innocent!).

1. Open a Windows Explorer window on your domain's Sysvol folder. This will be something like "\\mydomain.com\Sysvol\mydomain.com"

2. In the Explorer Search field in this window, type the name of the registry value you are working with (in my case "passwordexpirywarning")

3. Press Enter and wait for the search to complete – it won't find anything, so you will then need to click Search in File Contents. Once the search completes, you should have one or more GptTmpl.inf files returned in the results, as shown below:

4. Open each of these files in turn in Notepad (or your preferred text editor), and search each for the registry value in question:

In the above screenshot, you can see that this is a "candidate" GPO as it is setting "passwordexpirywarning" to 0. Ignore any files that don't correspond to the value you are interested in

5. For each of the corresponding GptTmpl.inf files that contain the value in question, obtain the GPO GUID (Globally Unique Identifier) by inspecting the properties of the GptTmpl.inf file, as shown below, and copying and pasting the GUID part of the path, EXCLUDING the curly brackets {} into a temporary text file:

The aim of this is to build up a list of GUIDs for candidate GPOs

6. Now, if you are not already on a Windows 2008 R2 server with PowerShell and the Group Policy Management Console (GPMC) installed, log on to one, and open a PowerShell session

7. In the PowerShell session, type the following command:

Import-Module GroupPolicy

followed by

Get-GPO -GUID <GUID>

where <GUID> is the first GUID you collected in the text file above, for example

Get-GPO -GUID AD77FD0E-3E55-4B7B-AD7A-2C6B4E680F80

This should return the display name of the GPO corresponding to this GUID, as shown below:

8. Copy the display name of the GPO into a text file and repeat the Get-GPO command for each of the other GUIDs you collected in the previous text file, collecting each GPO's name in the new text file as you go

All that remains is to determine which of these GPOs are applied to the affected machine:

9. Log on to the affected machine as an administrator and run gpresult /v > gpresult.txt in a command prompt. This will create a text file with a detailed output from GPResult.exe

10. Open the gpresult.txt file in Notepad (or your preferred text editor), and search for the name of each GPO collected from the Get-GPO cmdlet. Once located, this will confirm which GPO(s) are applied to the affected machine

Armed with the information determined by this procedure, I was able to update another GPO that was applied only to the ZDC in question and set the "passwordexpirywarning" value to the correct number of days, and password notifications then worked as expected.

I was recently responsible for troubleshooting a problem where searching for certain virtual machines in the vSphere Client didn't always return the expected results. The problem was occurring in both our production and non-production environments and the symptom was as described below:

In the vSphere Client, in Hosts and Clusters view, selecting (for example) a Datacenter in the tree in the left pane, then typing the name of an existing VM into the "Name, State or Guest OS contains:" box on the Virtual Machines tab wouldn't always return the VM in the search results. In some cases the VM search would behave in this way when targeted at at the Datacenter level, in others at the vCenter level and in others still, at the cluster level. The "Search Inventory" box exhibited the same behaviour. It was, however, possible to target the search at the host on which the VM resided and have it returned consistently in the search results. Similar behaviour occurred when searching for VMs in VMs and Templates view, and in all cases the VM in question continued to be displayed in the tree in the left pane of the vSphere Client.

After some searching online, I decided to raise a call with VMware Support. The Engineer who called me back immediately knew the cause of the problem, and directed me to a VMware Knowledge Base article:

Neither of these observations were the case in our environments, but the underlying cause was the same – it was, as the article says, "due to a conservative Java Memory Pool setting on which the Tomcat service depends for various functions. This issue usually occurs when the number of virtual machines is more than 500, but is dependent on a number of factors in your environment." In our environments at the time we had 850 and 300 VMs respectively.

If you have a 64-bit vCenter (4.x) Server, increasing the value of the memory pool in the Java Memory Pool settings is an easy fix:

Double the number in the Initial and Maximum memory pool field (defaults are 256 and 1024 MB respectively)

Click OK

Verify that there are no tasks running in the environment

Restart the VirtualCenter Server service – this will also restart the VirtualCenter Management Webservices service, as the latter is dependent. Bear in mind that anybody running the vSphere Client will be logged off when the services restart

If you have a 32-bit vCenter Server, follow the instructions under "Additional Information" in the article.

I currently have a home lab based on Simon Gallagher's excellent vTARDIS. This is an HP ProLiant ML115 which runs 8 virtual ESXi hosts arranged into a cluster which in turn run 30 Linux virtual machines. As you can imagine, this takes quite a while to start up! For this reason, I wanted a way to start the physical host programmatically (either on a scheduled basis or remotely via my home automation interface), in preparation for working on it or doing some learning. So a PowerShell script was obviously the answer! Within the lab itself I have a PowerCLI script which automatically powers up the various components in the correct order (but that's another story...).

The script I developed is shown below. I decided to share it via my blog as a) it might be useful to someone and b) it demonstrates the use of some handy PowerShell 2 features, such as advanced functions, parameter sets, parameter validation and comment-based Help, along with other good stuff like regular expressions, error handling and writing to the Windows event log. In the distant past I used an ActiveX control called UltraWOL (from UltraJones software, who don't seem to be around anymore) and called it from VBScript scripts to facilitate Wake-on-LAN, but I wanted to find a nice 'PowerShell only' way of implementing it.

The script can be used in two ways: it can be provided with a CSV file which maps 'known' machines to their MAC addresses, and then used to wake up one of these machines by providing just the computer name as a parameter, or alternatively, simply by specifying a MAC address using the 'MACAddress' parameter, to have a Wake-on-LAN magic packet containing that MAC address sent. In either case, results are reported to standard output and also recorded in the Application event log.

The script is shown below, but for your convenience you can download a copy here (MD5 checksum: 0533313E71C42816217F948247C17F9E) to save you copying and pasting (and all the potential problems associated with doing that) should you want to make use of it. Below the listing is a brief outline of the operation of the script.

<#
.NOTES
================================================================================
Filename: Wake-Machine.ps1
Author: Nigel Boulton, http://www.nigelboulton.co.uk
Version: 1.00
Date: 10 Sep 2011
Mod dates:
Notes: See http://www.nigelboulton.co.uk/2011/09/
wake-on-lan-using-a-powershell-script/
for further details of this script
================================================================================
.SYNOPSIS
Wakes up a machine using Wake-on-LAN
.DESCRIPTION
If the ComputerName parameter is given and is the name of a known machine (specified
in the CSV file 'MACLookup.csv', stored in the same folder as this script), sends a
Wake-on-LAN magic packet containing the MAC address of that machine. The ComputerName
parameter is the default, so if omitted the script will expect any argument given to
be the name of a known machine
The format for the CSV file is as follows. Header information as shown is required in
this file:
ComputerName,MACAddress
pc1,00-16-da-2b-6f-b8
If the alternative MACAddress parameter is given and is a valid MAC address, sends a
Wake-on-LAN magic packet containing that MAC address. In this case the MACLookup.csv
file is not used and is not required to be present
Results are reported to standard output and also recorded in the Application event log
.EXAMPLE
Wake-Machine.ps1 -ComputerName PC1
Description
-----------
Sends a Wake-on-LAN magic packet for the known machine 'PC1'
.EXAMPLE
Wake-Machine.ps1 PC1
Description
-----------
Sends a Wake-on-LAN magic packet for the known machine 'PC1'
.EXAMPLE
Wake-Machine.ps1 -MACAddress 00-16-DA-2B-6F-B8
Description
-----------
Sends a Wake-on-LAN magic packet containing the MAC address 00-16-DA-2B-6F-B8
.LINK
http://www.nigelboulton.co.uk/2011/09/wake-on-lan-using-a-powershell-script/
#>[CmdletBinding(DefaultParameterSetName='ComputerName')]param([Parameter(Mandatory=$true,HelpMessage="Enter a known machine name. See Help for this script for further information",Position=0,ParameterSetName='ComputerName')][ValidateNotNullOrEmpty()][string]$ComputerName,[Parameter(Position=0,ParameterSetName='MACAddress')][ValidatePattern('^([0-9a-fA-F]{2}[:-]{0,1}){5}[0-9a-fA-F]{2}$')][string]$MACAddress)functionSend-MagicPacket{param([Parameter(Mandatory=$true,HelpMessage="Enter a valid MAC address")][ValidatePattern('^([0-9a-fA-F]{2}[:-]{0,1}){5}[0-9a-fA-F]{2}$')][string]$MAC)<#
.NOTES
================================================================================
Purpose: To send a Wake-on-LAN magic packet with a specified MAC address
Assumptions:
Effects:
Inputs:
$MAC: MAC address to include in packet
Calls:
Returns:
Notes: Based on http://thepowershellguy.com/blogs/posh/archive/
2007/04/01/powershell-wake-on-lan-script.aspx
================================================================================
.SYNOPSIS
Sends a Wake-on-LAN magic packet containing a specified MAC address
.DESCRIPTION
Sends a Wake-on-LAN magic packet containing a specified MAC address. The MAC
address is specified by the MAC parameter. The octets of the MAC address may
be separated by dashes '-', colons ':' or nothing
.EXAMPLE
Send-MagicPacket -MAC 00-16-DA-2B-6F-B8
Description
-----------
Sends a Wake-on-LAN magic packet containing the MAC address 00-16-DA-2B-6F-B8
.EXAMPLE
Send-MagicPacket -MAC 00:16:DA:2B:6F:B8
Description
-----------
Sends a Wake-on-LAN magic packet containing the MAC address 00-16-DA-2B-6F-B8
.EXAMPLE
Send-MagicPacket -MAC 0016DA2B6FB8
Description
-----------
Sends a Wake-on-LAN magic packet containing the MAC address 00-16-DA-2B-6F-B8
.LINK
http://www.nigelboulton.co.uk/2011/09/wake-on-lan-using-a-powershell-script/
#># Use regex to strip out separators (: or -) if present and split string every second character# Piping to Where-Object {$_} avoids empty elements between each pair of characters$MACArray=($MAC-replace'[-:]',[String]::Empty)-split'(.{2})'|Where-Object{$_}$MACByteArray=$MACArray|ForEach-Object{[Byte]('0x'+$_)}$UDPClient=New-ObjectSystem.Net.Sockets.UdpClient$UDPClient.Connect(([System.Net.IPAddress]::Broadcast),4000)$Packet=[Byte[]](,0xFF*6)$Packet+=$MACByteArray*16Write-Debug"Magic packet contents: $([bitconverter]::ToString($Packet))"[void]$UDPClient.Send($Packet,$Packet.Length)Write-Debug"Wake-on-LAN magic packet of $($Packet.Length) bytes sent to $($MAC.ToUpper())"}#***********************************************************************************# Start of script#Requires -Version 2Set-StrictMode-Version2# User configurable values$MACLookupFilePath=Join-Path(Split-Path-Parent$MyInvocation.MyCommand.Path)MACLookup.csv# Location and name of MAC address lookup file# Initialise variables$ComputerDisplayName=$Null;$Msg=$Null# Register event log source if required (requires admin rights)if(!(Test-PathHKLM:\SYSTEM\CurrentControlSet\Services\Eventlog\Application\$($MyInvocation.MyCommand.Name))){Try{New-EventLog-LogNameApplication-Source$MyInvocation.MyCommand.Name-ErrorActionStop}Catch{Write-Host"WARNING: Unable to register event log source. You must run this script at least once as an administrator to do this"-ForegroundColorRed-BackgroundColorBlack}}if($PsCmdlet.ParameterSetName-eq'ComputerName'){# Computer name specified# Import CSV file of MAC addresses for known machines into a hash table.# This makes it easy to check whether a given machine exists in this list,# and retrieve its MAC address if so$MACLookup=@{}Import-CSV-Path$MACLookupFilePath|ForEach-Object{$MACLookup[$_.ComputerName]=$_.MACAddress}# Find MAC address for machine in hash table (this is case insensitive)if($MACLookup.ContainsKey($ComputerName)-eq$True){$MACAddress=$MACLookup.Get_Item($ComputerName)$ComputerDisplayName="($($ComputerName.ToUpper()))"# Validate MAC addressif($MACAddress-notmatch'^([0-9a-fA-F]{2}[:-]{0,1}){5}[0-9a-fA-F]{2}$'){$Msg="ERROR: Invalid MAC address specified: '$MACAddress'. Verify the MAC address for the computer '$ComputerName' in the CSV file '$MACLookupFilePath'"Write-Host$Msg-ForegroundColorRed-BackgroundColorBlackTry{Write-EventLog-LogNameApplication-Source$MyInvocation.MyCommand.Name-EventID1001-EntryTypeError-Message$Msg-Category0}Catch{Write-Host"WARNING: Unable to write to event log. You must run this script at least once as an administrator to register the event log source"-ForegroundColorRed-BackgroundColorBlack}Throw"Invalid MAC address"}}else{$Msg="ERROR: Unrecognised computer name: '$($ComputerName.ToUpper())'. Add the computer name and MAC address to the CSV file '$MACLookupFilePath'"Write-Host$Msg-ForegroundColorRed-BackgroundColorBlackTry{Write-EventLog-LogNameApplication-Source$MyInvocation.MyCommand.Name-EventID1001-EntryTypeError-Message$Msg-Category0}Catch{Write-Host"WARNING: Unable to write to event log. You must run this script at least once as an administrator to register the event log source"-ForegroundColorRed-BackgroundColorBlack}Throw"Unrecognised computer name"}}Send-MagicPacket-MAC$MACAddress$Msg=("Wake-on-LAN magic packet sent to $($MACAddress.ToUpper()) $ComputerDisplayName").Trim()Write-Output$MsgTry{Write-EventLog-LogNameApplication-Source$MyInvocation.MyCommand.Name-EventID1000-EntryTypeInformation-Message$Msg-Category0}Catch{Write-Host"WARNING: Unable to write to event log. You must run this script at least once as an administrator to register the event log source"-ForegroundColorRed-BackgroundColorBlack}

The first section of the script is the comment-based Help which can be displayed by typing Get-Help .\Wake-Machine.ps1 in a PowerShell console in the usual way (I'd advise that you do this to help understand the operation of the script before attempting to implement it).

Following that are the definitions for the two possible parameters. Note their parameter set names - these are called 'ComputerName' and 'MACAddress' respectively. You will notice that the default parameter set has been set as 'ComputerName'. This means that if the parameter name is omitted, the script will expect any argument given to be the name of a known machine in the CSV file.

The CSV file must be called 'MACLookup.csv' and stored in the same folder as this script. The format for the CSV file is as follows - header information as shown below is required in the file. A sample CSV file is included with the downloadable script:

ComputerName,MACAddress
pc1,00-16-da-2b-6f-b8

If the 'MACAddress' parameter is given, a regular expression is then used with parameter validation (ValidatePattern) to verify that it is a valid MAC address. The octets of the MAC address may be separated by dashes '-', colons ':' or nothing.

Coming on to the main body of the script you will notice that I use

Set-StrictMode-Version2

This is a good practice which ensures that you don't get caught out by mis-typed variable names. I use this in all my scripts. For more information see this TechNet document.

The next significant operation is registering an event log source for the script in the Windows Application event log, if that hasn't already been done. This requires you to be an administrator, so it will be necessary to run the script once as an administrator if you will normally run it as a non-admin. If you don't do this, the script will continue without writing the result to the event log.

The next section is only executed if the 'ComputerName' parameter set is in operation: the MACLookup.csv file is read into a hash table, then this is queried to see whether the computer name specified by the parameter exists, and the corresponding MAC address is retrieved if so, and then validated by the same regular expression as before. If not, the script writes this fact to the event log and throws an error.

Finally, the 'Send-MagicPacket' function is called to send the Wake-on-LAN magic packet and the result is reported to standard output and the event log.

The Send-MagicPacket function is of course at the heart of the script functionality. It is based on a great post by The PowerShell Guy that you can find here (thanks /\/\o\/\/). I tweaked it slightly because I thought it would be more flexible to be able to specify the octets of the MAC address separated by either dashes, colons or nothing. I also added some comment-based Help so that you can easily copy and paste this function into other scripts or a PowerShell module, to make use of it elsewhere. You will notice that you can use the PowerShell common parameter 'Debug' to troubleshoot creation and sending of the magic packet if necessary. See Jeffery Hicks (The Lonely Administrator)'s excellent post here for more information on how and why you might do that.

I hope this script is of use to you (or perhaps the techniques within it) – please leave a comment to let me know if so.