Friday, 22 May 2015

Next-Level Scripting – Remember Get-Member

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.

What You Don’t Know

One of the most important skills to have as a Next-Level Scripter is the ability to find the answers to what you don't know.  What don't you know exactly?  A lot!  I've been writing PowerShell scripts for years and I still regularly use the techniques I will outline in this post to discover information that I don't know.  And I think it's safe to say that I will never stop using these techniques because there is just too much "stuff" to know it all.  And there’s nothing wrong with that so please don’t ever think that your scripting skills are lacking if you regularly have to look things up!

Warning – Mild Developer-Speak Ahead

I have to apologize but it's time for some developer-speak.  Don't worry though, there are only two main terms you need to understand as your foundation - methods and properties.

To explain these terms, let's take a step back from the world of computers and look at a real-world example to which pretty much everyone can relate to - the car.  In developer-speak the car is known as an object.  Every car (object) has, among other things, a colour, weight and model.  These are fixed attributes that you can't change at will and are what as known as properties.  In developer-speak, the colour is a property of the car object.

But, there are also things that you can change and to make these changes you need to actually do something.  For example, the car can accelerate but it doesn't just do that by itself; you need to press the gas pedal to make it accelerate.  In the world of objects this is what is known as a Method.  Once again, to put it in developer speak: you use the accelerate method to make the car object accelerate.

And that's all you really need to know about objects, methods and properties for the purposes of this post!  See, it wasn't too bad was it?

Exploring Objects

Remember the car object from our example above?  When you work with PowerShell you are working with objects as well and all of these objects have methods and properties.  The question though when you're dealing with these objects is, "What can I do with this object via its methods and properties?"  Like I said in the introduction to this post, there is absolutely no way you can know what all the properties and methods are for everything you'll ever come across and you therefore must know how to find this information out.

Enter Get-Member…this cmdlet is what shows you the methods and properties of an object.  To get it to tell you this information you need to tell it what object you're interested in.  Let's look at a simple example to show how this is done.

You've been asked to write a script to find the date created for every subfolder and file of a given folder.  You've never done this before but suspect that there is a property which contains the creation date of a folder/file.  Your mission then is to find this property (I know you could easily look this up on on the internet but if you want to truly learn PowerShell you need to figure out how to do these things yourself - it's the best way to learn).

You know about the Get-ChildItem cmdlet so you decide that's a good place to start. You run it against your folder of interest and examine the output.

image_thumb17

Well that's a little disappointing - no mention of creation date, only modified date.  But does this mean you should throw your monitor at the wall and go home weeping?  No!  In most cases, there is way more information you can retrieve and PowerShell is only showing what it has been programmed to show you by default.

As you've probably figured by now, this is one of those times where you need to run Get-Member.  Remember how I said above that you must tell Get-Member what object you're interested in?  That object will come from Get-ChildItem via this command:

001
Get-ChildItem C:\CorpMiscellaneous | Get-Member

And here’s the output:

image_thumb23

This command will retrieve all the available Methods and Properties of the C:\CorpMiscellaneous folder.  Since we're trying to retrieve a property and not a method, we can ignore all the methods in the list and focus only on the properties, highlighted in red.  And lo and behold, what is the 2nd property we see in the list, highlighted in green?  CreationTime!  Pleased with this discovery, you decide that you want to report on the name of the file and the creation time as well as sort the results by the creation time.  Let's try this out:

001
Get-ChildItem C:\CorpMiscellaneous | Select-Object Name, CreationTime | Sort-Object CreationTime

And here’s the output:

image_thumb26

In this example, we used the Select-Object cmdlet to specify the properties we want to retrieve from each object, in this case Name and CreationTime (you could have specified any valid property in this command).  We then pipe the results to Sort-Object and tell it to sort the results based on CreationTime.  If you wanted to save the results to a file you could also add Export-CSV at the end of the command.

And that's all there is to it.  You can use this approach for any object to discover what you can do with it and in doing so, take the next step on your way to becoming a Next-Level Scripter.

Tuesday, 12 May 2015

Quick and Easy Check For Nested Groups

I sometimes find myself needing to check if a group contains any other nested groups.  I don’t necessarily need to know who the members are of each nested group, I just want to know if it contains other groups and what those groups are named.  You can use this quick one liner to get a list of any groups who belong to the group specified, in this case Citrix_Users

001
Get-ADGroupMember Citrix_Users | where {$_.objectClass -ne "user"} | select -ExpandProperty Name

Each AD object has an objectClass property associated with it.  All this one-liner does is get each member of the specified group, and if its attribute is not user, display the group name.  Depending on how frequently you used this, you could add a parameter for the group name and even add it as a function to your PowerShell profile so that it’s loaded automatically each time you run PowerShell.

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.

Monday, 4 May 2015

Debugging Without The ISE

Sometimes when you’re debugging a PowerShell script you may find that you need to run the script in a separate PowerShell instance instead of in the ISE.  For example, you may want to test something like transcription which doesn’t work in the ISE.  Problem is, when you’re not using the ISE you lose the ability to easily insert a breakpoint into your script and poke around to check whatever it is you want to check.  One solution is to write a bunch of lines echoing what each variable is set to at that point.  But that’s time consuming, especially if you have a lot of variables you’re interested in.  A more efficient solution is to use a very handy method which allows you to temporarily stop a PowerShell script in mid-execution so you can examine those variables dynamically to your heart’s content.

The automatic $host variable has a host (pardon the choice of words) of properties and methods, one of which is EnterNestedPrompt.  What this allows you to do is stop script execution at any given point and enter a prompt inside the script with the all the variables available at that point in the script accessible to you.  Let’s look at the very simplistic example shown below:

001
002
003
004
005
006
007
008

$a = 1
$b = $a + 1

Write-Host "Entering nested prompt..."
$host.EnterNestedPrompt()
Write-Host "Out of nested prompt"
$c = $b + 1
$c

All we’re doing here is setting some very basic variables in lines 1 through 4.  But the magic happens on line 5 where we enter the nested prompt.  Let’s take a look at the script output when this is run:

image

Notice how after the line “Entering nested prompt” the PS prompt changes from PS C:\Temp> to PS C:\Temp>>.  The script is paused at this point and we’re now inside the nested prompt. We can now type anything we want with all the variables active in the script at that time available to us.  Notice how I typed $a and $b and got the correct values echoed back.  Very handy!  Once you’re done, just type exit and the script continues.