I am Program (and so can you!) – PowerCLI Edition

That was my best Stephen Colbert impression, catchy no? Anyway, on to networky things!

As everyone in the Twitterverse, and even in real life, has talked and talked and talked to no end about how we as network engineer type folk must learn Python and Ruby and whatever else or we are doomed to a sad, sad future at the unemployment line… I figured I would write-up this quick little post. I will not tell you that you need to learn to program anything, instead I will simply say that with a bit of Google-foo and some time you can probably figure some programming stuff out if you need to! And in that spirit, here’s how I figured out that PowerCLI was a thing and that I may need to learn how to use it at least a little bit:

I’ve been working on my lab setup at the office a lot lately. We’ve got a slick web front end we’re hosting in AWS (yay shadow IT!), and our AWS VPC VPNs back to our lab setup. We’re using that box in AWS to kick off scripts and automate provisioning of lab resources (Anyconnect user to access said lab, ACI tenant/user, and now some vCenter stuff). We got to a point where the VMware Perl SDK just wasn’t cutting the mustard (weird phrase… yay wiki: https://en.wiktionary.org/wiki/cut_the_mustard). It was working to clone some virtual machines, but it didn’t have anyway to create folders and assign permissions to those folders as to limit a lab user from screwing about with other people’s VMs. So after some time spent with my good friend Google it seemed that I was doomed… at least from a Linux tools perspective. BUT, PowerCLI kept coming up as an option. So I dusted off my Windows 7 VM and fired it up, installed PowerShell (and ninety-five million Windows updates….) and got PowerCLI installed. For the uninitiated PowerCLI is basically an extension of PowerShell with lots of Cmdlets (commandlets… I guess) which are essentially PowerShell scripts that you can call to poke a vCenter or ESXi box.

I must warn you that what follows is perhaps the most hellaciously bad PowerCLI script ever written, but it does indeed work, and serves my purpose (and that’s pretty much the point of this post)…

param([string]$key,[string]$vmqty)

$vmfolder = Get-Datacenter -Name DC1-ACI-Lab | Get-Folder -Name vm
New-Folder -Name $key -Location $vmfolder
New-VIPermission -Entity $key -Role Lab_User -Principal VSPHERE.LOCAL\$key<
while ($vmqty -gt 0){
 $vmname = [string]$key+[int]$vmqty
  if($vmqty % 2){
   New-VM -Name $vmname -VM Blade1_Gold_Clone -VMHost 10.255.252.15 -Location $key -LinkedClone -ReferenceSnapshot Blade1_Gold_Clone_Snapshot
  }
  else{
   New-VM -Name $vmname -VM Blade2_Gold_Clone -VMHost 10.255.252.16 -Location $key -LinkedClone -ReferenceSnapshot Blade2_Gold_Clone_Snapshot
  }
 [int]$vmqty = $vmqty - 1
 continue
}

So, what the hell is this thing doing? Well here’s the idea: I needed a way to provision X number of virtual machines with a naming convention of X+n in a folder called Y. Why you ask? Because X is basically what i’m calling my “key,” or unique identifier for the person who filled out the web form to provision the lab, and the number of VMs may be different depending on the lab topology they selected. Finally, I have two blades in my UCS Mini, and I wanted to be able to divvy up the provisioned VMs across them in a somewhat even fashion.

Okay, so ignoring programming for a moment, lets just think about these tasks I want to do in a methodical way. First thing that we need to consider is that we need some way to capture the key (X), and the quantity of VMs. Okay well that should be easy enough, a quick search lead me to the param option:

param([string]$key,[string]$vmqty)

So this line basically says that this script can take two parameters (in this case two strings), and assigns them to some variables. In PowerShell, the ‘$’ character precedes any variables.

Now that the script is able to accept the information gathered from the web form, what would the next task have to be? Well it seems to me that we need to create a folder before we start building VMs, that way we have somewhere to put them. There is a simple PowerCLI cmdlet available to create a new folder, named aptly enough “New-Folder.” This cmdlet takes a few arguments, such as folder name and location. If we leave out the location option the cmdlet goes ahead and makes a folder, but due to the default location, it creates a folder that isn’t for VMs (I believe it creates a “Hosts and Clusters” folder), so that’s no good. What we need to do is make sure that script knows where we want our folder to be created, so we can do that by assigning a variable, in this case called $vmfolder the appropriate location.

$vmfolder = Get-Datacenter -Name DC1-ACI-Lab | Get-Folder -Name vm

The above line creates our $vmfolder variable, and assigns it the output of the “Get-Datacenter” cmdlet. This cmdlet when run without assigning to a variable, basically gets a list of data centers in your environment, in this case, we are grabbing a specific data center (DC1-ACI-Lab), and then piping to also get the list of folders available in that data center. Specifically, we are grabbing the default folder named “vm.” This is the parent folder on the “VMs and Templates” page in vCenter. Basically we are assigning this specific path to the variable $vmfolder.

Next, we can go ahead and create a folder, this is pretty easy:

New-Folder -Name $key -Location $vmfolder

Another aptly named cmdlet… giving the folder a name of $key — which is our unique ID for the lab user, and placing the folder in the location we grabbed in the previous line.

Okay so we now have our folder created, but we also need to assign permissions for our user to this folder so that when they log into vCenter they can see the contents of said folder. Again, this turned out to be pretty straightforward:

New-VIPermission -Entity $key -Role Lab_User -Principal VSPHERE.LOCAL\$key

Cmdlt New-VIPermission basically just takes an “Entity” (a thing in vCenter, in this case our folder), a “Role” (as in a set of permissions), and a “Principal” which is our local user (created outside of this script, but in this case named exactly the same as the $key variable, and created in the vsphere.local SSO domain).

So basically so far, we’ve done a bunch of one-line things that I wouldn’t really call programming… just typing a single line instead of clicking some stuff in the GUI. Not too shabby.

Now that we’ve got our folder and our user has access to it, we just have to clone some VMs and dump them into the folder… that part is easy enough, but I’ve got to complicate things just a bit more… I want to evenly disperse my VMs across the blades. So, ignoring, PowerCLI/PowerShell for a moment, if I were to simply do this in the GUI, how would I divvy up the VMs? Well I’d try and do something sane, so I would probably put odd-numbered VMs on blade 1 and even-numbered VMs on blade 2. Simple enough right? Well there’s no magical cmdlet to parse a username and figure that out, so we have to do something a little simpler… lucky for us, this is pretty straightforward — whats an easy way to distinguish between odd and even that a computer would know about? How about simple math! That, plus a cmdlet that clones some VMs (well technically creates linked clones in this case), and thats how we end up with this:

while ($vmqty -gt 0){
 $vmname = [string]$key+[int]$vmqty
 if($vmqty % 2){
  New-VM -Name $vmname -VM Blade1_Gold_Clone -VMHost 10.255.252.15 -Location $key -LinkedClone -ReferenceSnapshot Blade1_Gold_Clone_Snapshot
  }
 else{
  New-VM -Name $vmname -VM Blade2_Gold_Clone -VMHost 10.255.252.16 -Location $key -LinkedClone -ReferenceSnapshot Blade2_Gold_Clone_Snapshot
 }
 [int]$vmqty = $vmqty - 1
 continue
}

First thing you’ll notice about this is that we’re starting off with a while loop — thats because for now I need to make two VMs, but what happens when I need to make 10? This loop will just iterate over and over until the $vmqty variable is decremented to zero. Directly under the start of the while loop, we’re creating a new variable $vmname, this is taking a string value of the $key and concatenating it with the current value of the $vmqty integer. Easy enough right? Next comes our math…. IF $vmqty is divisible by 2, do a thing, ELSE do a different thing. The one bit here that did throw me for a loop is that it seems that in PowerShell the section if($vmqty % 2) evaluates things weirdly. For example, if i set the vmqty variable to 2, I would assume that the first VM would be deployed to the ESXi host 10.255.252.15… thinking that 2 is divisible by 2, PowerShell would run the IF stanza, not the ELSE stanza…. seems to not be the case. I guess PowerShell evaluates that math to True, and therefore it should not satisfy the IF requirement, and therefore punts to the ELSE. In any case, that’s a minor thing easily figured out by simply running the script to see where the VMs end up getting created.

As for the actual VM creation, again, PowerCLI and the aptly-named cmdlets to the rescue:

New-VM -Name $vmname -VM Blade1_Gold_Clone -VMHost 10.255.252.15 -Location $key -LinkedClone -ReferenceSnapshot Blade1_Gold_Clone_Snapshot

In this case I’m cloning a VM called “Blade1_Gold_Clone” on VMHost (ESXi box) 10.255.252.15 in Location “$key” (the folder we created), and some extra syntax for the linked clone stuff. Easy does it.

Finally, before closing out the while loop, we need to decrement the $vmqty variable so that we know that we’ve created one of the VMs already.

Thats pretty much it! The script can be run from the PowerCLI prompt, and takes variables passed to it like so:

C:\creationscript.ps1 -key [insert string here] -vmqty [insert string/integer here]

Obviously this is assuming you’ve got your PowerCLI connected to vCenter etc., and you’ve dumped you script in the C:\ directory (probably not the greatest idea i suspect!).