By Willis Gronwall — Windows Server Consultant

I recently had a situation where I needed to change the hostname, IP, and domain of around 200 VMs. No problem, I can just script that out, right? Well, the problem with scripting, in this case, was that the VMs were in an offline state (more on that below). I also couldn’t use VMware customization specs because those are only available during cloning or deployment operations. The answer came in the form of a clever cmdlet called Invoke-VMScript that let me execute code using VMware tools as the delivery mechanism. In this blog, I’ll talk about how VMScript saved me countless hours and I’ll give you some tips on using the command, but first a little background…

The VMs in my scenario had just been migrated to a new datacenter but they still had the IP configuration from their original location. The new datacenter had an entirely different IP scheme and, as a result, these VMs were effectively offline. Traditional scripting against offline machines like this isn’t really possible so you have to get a little creative. I’ve seen some folks embed custom scripts into the VMs prior to the move which will trigger upon reboot. I’ve also seen datacenters designed with mirrored subnets to ensure a graceful landing when migrating between the two. Each of these approaches has pros and cons which I won’t get into because they simply weren’t options for me. Another method I’ve seen in the field is to just console in and configure the VMs manually which might work on a small scale but isn’t practical when you start to consider hundreds of VMs.

Having no good options left, my savior came in the form of the Invoke-VMScript PowerCLI cmdlet which allowed me to leverage VMware Tools to run a script on every VM. In this case, the changes I wanted to make were simple, but since it’s a script block you have a ton of flexibility. You can customize what gets executed and it can be run on demand; these are two things you don’t get with customization specs. The key benefit for me was that I was able to execute code against VMs that were completely offline.

In an effort to not reproduce work I’ll direct you to the reference section at the end of this post for a basic summary of Invoke-VMScript and its parameters. Instead, I want to talk about some of the “tricks of the trade” that I discovered while using the command at scale.

The “Script Block” Requires a String Object

This is a pretty big deal because if you are trying to do anything complex it will become difficult to work strictly within a string. The biggest problem is that you can’t use variables in the string and if you want to automate things across hundreds of servers you’re going to need to use loops and variables at some point. One way to get around this is to rebuild the string during each pass of your loop. Take a look at the code below as a simple example of this technique. “$baseScript” is a here-string containing your script block and the “$script” line will be placed in your loop along with Invoke-VMScript.
$baseScript = @’
Rename-Computer -NewName $newName -Restart -Force
$script = $baseScript.Replace(‘$newName’,$VM.newName)

Errors Aren’t Always a Bad Thing

This one took some time to figure out so I think it’s worth sharing. Basically, the way Invoke-VMScript works is it takes some credentials you provide (hopefully a PSCredential) and uses that to gain access to the guest OS and ultimately execute the code. It uses that same credential to collect the output and exit codes once the script has finished and it presents those to you as a return in your PowerCLI session. In my particular case, it was required that I use a local admin credential. When Invoke-VMScript first authenticates to the guest OS the local credentials are bound to the hostname which is also bound to the domain. When, as part of the script, you change the hostname and domain (as I did) those credentials become invalid and you will get an error when it tries to collect the output at the end. In this case, however, that’s actually a good sign because it means the script successfully made the changes. Simply run a second pass and replace the script block with auditing code to confirm those changes. Of course, there are all kinds of legitimate errors you could encounter as well; just remember how the cmdlet uses the provided credentials when troubleshooting those errors.

Which Network Adapter to Configure?

If you’re doing any kind of network configuration on the guest OS you’re going to need to choose a logical adapter to configure and the guest may have more adapters than it has “physical” interfaces. I needed a way to identify only the adapters that corresponded to the “physical” interfaces of the VM. I also had the requirement of making this work with PowerShell v2 so the answer came in the form of WmiObjects but something similar could be done with Get-NetAdapter. A quick shout out to my Linux colleagues out there — VMScript does also support bash if you are working with Linux VMs. Anyway, the sample code below looks at the Service Name of all the adapters and identifies only the VMXNET / E1000 adapters, selecting the first in the array if more than one is returned. If you wanted to get a little fancier you could grab the MAC address from the VM and match that to the list of adapters, but that’s a discussion for another day. Now that you’ve identified your adapter and you’ve got it in the form of a WmiObject, you can use WMI methods to change the network configuration. Note that you cannot configure DNS using this method unless the adapter has IP enabled which basically means it needs to be ‘connected’ to a port group. I just connected everything to a port group with no uplink to get around this issue.
$wmiNet = Get-WmiObject Win32_NetworkAdapterConfiguration | ?
{$_.ServiceName -match
“vmx.*|E1.*”} if ($wmiNet.Count -ge 2) {
$wmiNet = $wmiNet[0]

I hope this post will help you navigate the capability of this neat feature from VMware. I am curious how the security community feels about running scripts through VMware Tools; it was an attack surface I wasn’t aware of previously.

Please let me know if you have any feedback or questions about this blog or if you want to see more of this kind of thing in the future.

Have More Questions? Contact our team at Taos. We’d be happy to help.