Friday, 8 May 2015

Next-Level Scripting - Strongly Type Your Variables

Welcome to the next post in my Next-Level Scripting series where I show you the PowerShell scripting techniques that take your scripts to the next level – a level defined by concise, well-written code that conforms to best practices and uses the built-in tools in PowerShell whenever possible to handle common scripting tasks.

Go Easy On Your Keyboard

By “strongly type”, I don’t mean pound on your keyboard as you type each variable.  What do I mean then?  For us sysadmins who don’t have a development background, it might come as a surprise to find out that each and every variable that we declare in PowerShell has a data type associated with it.  Your variable, $userName, is not just a variable – it’s a string variable.  Admittedly, this sounds like developer stuff and it kind of is, but if you want to take your scripts to the next level it’s a concept you need to understand.  In this post we’ll examine how our variables get assigned a data type and how and why we should control that process.

Automatic For the Variables

If the news that all your variables have a data type comes as a surprise to you, it’s because PowerShell does a very good of job of automatically assigning the correct data type to your variables without you ever knowing about it.  For example, if you create a variable called $fileName and set it to C:\Temp\MyFile.log, PowerShell will figure out the best data type for it and without any fanfare or big announcement, assign it that type.  This doesn’t mean you can’t find out what type your variable is, you just have to know the right command.  Let’s take a look at some examples.

001
002

$fileName = "C:\Temp\MyFile.log"
$fileName.GetType().Name

Perhaps not surprisingly, the command to find out the type of a variable is GetType().  Let’s see the output from running this.

image

There you go – the type of our variable is String.  Notice how we never told PowerShell to do this, it just figured out what type it should be and did it.

Let’s try one more.

001
002

$fileCount = 100
$fileCount.GetType().Name

image

This time, our variable was assigned the type Int32.  Once again, job well done by PowerShell – this is indeed an integer and it figured it out for us.

Seems Like PowerShell Is Really Good At Variable Typing

It is!  But as a next-level scripter you should get into the habit of strongly typing your variables because things don’t always go as planned.  Let’s see how we do this by revisiting our examples from above.

001
002

[string]$fileName = "C:\Temp\MyFile.log"
[int32]$fileCount = 100

As you can see, we simply need to put the type of our variable in square brackets before the variable name.  This way, we are telling PowerShell what type to make the variable and completely eliminating the risk of an unintended data type being assigned.  This is known as variable casting.

User Input

You may know very clearly what type of variable you want but once you hand over your script to someone else you can never count on them getting it right every time the script is run.  Strongly typing your variables is a great way to easily protect your script against unexpected user input.  Let’s look at an example where our hapless user accidentally provides the wrong input.

001
002
003
004
005

param
(
   
[Parameter(Mandatory = $true)]
    [int32]$daysToRetrieve
)

In this very simple example, our script accepts one parameter – the number of days to retrieve Event Log records for (if you’re not familiar with the parameter block shown above, see here).  Obviously, this input needs to be a number.  But you can also write a number as a word and how do you know that someone, at some point, won’t type out the word “six” instead of the number 6?  You don’t, so prepare for it by strongly typing the variable.  Let’s see what happens when we provide the wrong input.

image

Denied!  PowerShell rejects the input with the message “Input string was not in a correct format”.  The word “six” is a number but it’s not an integer and since we told PowerShell that this is the type that the variable must be, it will not accept anything that it can’t convert to an integer. 

Let’s imagine for a second that you didn’t strongly type your variable.  PowerShell would have accepted “six” and then proceeded to run whatever command you had in your script to retrieve the event log records.  The command would very likely have failed anyway at this point so what did we gain by catching the mistake earlier?  It is much, much better to catch this kind of thing before your script starts doing its work.  What if your script was doing something other than just retrieving information like deleting files, or rebooting computers, or deleting user accounts from AD?  The last thing you want is the wrong type of information being fed to those commands because the consequences could be disastrous.  When you catch this kind of thing before the script actually does anything, you are ensuring that you don’t have a crazy runaway script causing havoc because of something you didn’t expect and couldn’t have planned for.

How Do I Know What Type To Use?

When you use data types in PowerShell you’re actually using .Net classes.  PowerShell has a number of shortcuts, known as type accelerators, which allow you to specify a data type with less typing.  For example, in our script above when we used [Int32], we were actually using the [System.Int32] data type.  Thanks to the built-in type accelerators though, you only need to type [Int32] and PowerShell does the rest for you.  Here’s a list of some of the more common data types you would use:

[int] 32-bit signed integer
[string] Fixed-length string of Unicode characters
[bool] True/false value
[double] Double-precision 64-bit floating point number
[decimal] 128-bit decimal value
[array] Array of values
[xml] Xmldocument object
[hashtable] Hashtable object (similar to a Dictionary object)

You can also get a full list of the available type accelerators by typing this command:

001
[psobject].Assembly.GetType("System.Management.Automation.TypeAccelerators")::get

One other method is to not strongly type your variable, let Powershell do it for you, and then check what data type PowerShell automatically assigned.  You would do this using the GetType() method that I showed above.

No comments:

Post a Comment