PowerShell Tip: Don’t forget the type!

Powershell

So I’m sitting here building a bunch of virtual machines using PowerCLI. I decided to start with a spreadsheet into which I’ve collected many of the important things about a virtual machine:

SNAGHTML840494

Since I don’t actually build VMs every day, and I haven’t focused on a build process (like I should, I know!) yet, this spreadsheet was a first draft of a build process and it was made for humans, not machines. What does this mean? Well, obviously, by looking at the Memory column, any of you would guess that the unit of measurement is gigabytes. However, VMware happens to measure memory in megabytes.

Long story short, I wrote a quick one-liner in PowerShell to “spec out” the newly-cloned virtual machines using this spreadsheet. As I said, it’s not a build process yet, but it will be when I’m done. Baby steps. The one-liner looks like this ($t is the variable that holds the data obtained from the spreadsheet, using a simple Import-Csv cmdlet):

   1: $t | % { Set-VM -VM $_.name -NumCpu $_.cpu -MemoryMB $_.Memory }

Once I started that running, I quickly realized that 8MB VMs would do me no good. Smile So, I amended my script to this:

   1: $t | % { Set-VM -VM $_.name -NumCpu $_.cpu -MemoryMB ( $_.Memory * 1024 ) }

That’s when I got a really weird error:

Set-VM : Cannot bind parameter ‘MemoryMB’. Cannot convert value "888888888888888888888888888888888888888888888888888888

88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

888888888888888888" to type "System.Int64". Error: "Value was either too large or too small for an Int64."

Yuck! That one threw me for a loop for a moment until I realized the important lesson of the day. When you use a technique like Import-Csv, the resulting object is a bunch of strings! What happens when you multiply a string by a number in PowerShell? Yes, it’s effectively a concatenation. So in my case, the number 8 followed by one-thousand-and-twenty-three of the same. Nice, when that’s what you intended. That was not my intent this time!

So to round this post out with a fix, here’s the right way to get the intended result, which was to turn “8” into “8192”:

   1: $t | % { Set-VM -VM $_.name -NumCpu $_.cpu -MemoryMB ( [int]$_.Memory * 1024 ) }

Note the “[int]” there before the $_. That will convert the resulting property to an integer BEFORE performing the multiplication. That’s the key here, it has to happen before (in the order of precedence), otherwise I end up with a bunch of 8’s. Now, I happen to know the order of operator precedence in PowerShell well enough to know that the above would work without any doubt, but if you aren’t sure about a particular piece of code, you can always surround a portion of a statement with parentheses in order to ensure that you get the order that you need.

Leave a Reply

Your email address will not be published.