Wednesday, May 13, 2015

PowerCLI for Modifying VM Network Adapters

A complex system of PowerShell and PowerCLI scripts manages the virtual machine lifecycle here. The scripts are remarkable in their implementation, and for years have been humming along just fine without much modification, even though the creator left two years ago.

Recent changes to the environment, however, caused portions of these scripts to break. So guess who gets to fix them? Correct: your humble correspondent.

Of course, I'm no PowerCLI god, or deity, or even apostle. I'm a spirited follower at best. Though to my credit, I've taken philosophy, logic, and ethics, and I have a beard, so you know, I'm qualified to debug and correct code.

So here's a fun overview of a problem I've dealt with lately, and what I learned in the process. Disclaimer: If you know everything about scripting already, you should have stopped reading by now.

The Problem

I replaced a few dozen vSwitches with a nice vDS, and there was much rejoicing. Except the scripting, which had been developed to use vSS cmdlets, was not happy. We had been using the following command to configure a newly-provisioned VM's network adapter:

We build the variables based on the relevant information in the request for a new VM. So things like $vlan are defined based on the ultimate location of the VM. But when you're working with a vDS (and more importantly a VDPortGroup) you can't use vSS cmdlets.

The Solution

So after some research and a lot of trial and error, we ended up with this:

The difference is significant. We're not just talking about changing a single cmdlet, or swapping out a single switch. We have to change our whole approach. Instead of just setting the port group, we need to define the VDPortGroup that we want the VM's interface to connect to, and that means identifying the vDS itself. So I built the $pg variable to contain that information. And $na holds the information need to properly identify the network adapter we want to modify.

Logging: it's fannnnntastic!
You'll notice some additional lines that echo the values for these variables, and the output of a get-networkadapter command, into a logfile. I set this up to debug a problem I was having (see below). This logging was crucial to helping me see where things were breaking, and I ended up leaving these cmdlets in place in case things go south again. NB: Logging is always a good idea.


The Problem with the Solution

However. I was getting some really strange results with this script when run as part of the automation system. I could run these commands in a PowerCLI window without a problem. The VM's network adapter would be configured exactly the way I intended. But when the same script was run under the context of a service account, the VM's network adapter would be configured to connect to the vDS but not to any port group. And the logging confirmed that: the portgroup value in the get-networkadapter output was blank. It was the kind of thing that drives you bonkers. I mean really.

The Solution to the Problem with the Solution

It occurred to me that maybe the problem was related to the modules that were loaded under the service account's profile. So I logged into the script host using the service account and ran the PowerCLI 6 R1 installer. (I had previously upgraded PowerCLI from 4.x to 5.8 R something (and PowerShell from 2 to 4) with my own administrative credentials.) And I even had another administrator do the same. After both of these actions, all of the scripting started working as expected.

If you ever run into weirdness with certain cmdlets after you upgrade PowerCLI, PowerShell, or both, you should consider re-running the respective installer for each user profile on your scripting host.

Epilogue

You've probably seen some syntax that you disagree with here. PowerCLI-wise, I mean. The process to modernize these scripts is a slow one, and many of the piped-output to piped-output bits will go away. There's always a faster way to get things done when you're scripting. But performance and elegance are always secondary to function. Always.