Some advice on creating Powershell scripts

Creating a Powershell script CAN be quite difficult. How to get the output you want? Where to start? Let me try to get you started by providing a structure you can follow. I’m not saying this is THE way, but it’s MY way. Hope it helps you and please do comment if you have additions or another way!

First: Scripts for gathering information.

1. What should my OUTPUT be?
It’s easier to start with the end. Start by determining what you would like your output to look like.

1.1 OBJECTS representing what?
It’s good practise generating objects as output. It allows you to use the full power of Powershell to further manipulate your output. So don’t use any Format-… cmdlets in your scripts! That being said, what should the output objects represent?
Let’s take a simple example, like generating an overview of server OS versions or free disk space or something similar. The output objects should represent the servers.

1.2 What PROPERTIES?
You all know objects have properties. If not, read Get-Help About_Object. What properties of the server objects are we interested in? In the example it could be Server Name (obviously), OS Version, Service Pack, Free Disk Space on C-partition, and so on.

2. WHERE to get the information?
Now that we know what objects to create and what properties to fill, let’s examine where to get all this info.

2.1 The EASY ones.
When the properties can all be found in one place, the script is simple. For example, the OS Version and Service Pack of a server, can all be found in Active Directory. The Quest AD cmdlet Get-QADComputer returns objects representing servers, which have properties including the ones we seek. All we have to do is FILTER the properties, like so:
Get-QADComputer | Select-Object Name, OSName, OSServicePack

And we’re all done!

2.2 Getting HARDER:
What if most properties can be found in this way, but some require additional calculations or “get-“ting? Then we can use CALCULATED PROPERTIES. The syntax is as follows:
Get-Something | Select-Object Name, @{N=”CalculatedPropertyName”;E={EXPRESSION THAT RETURNS THE DESIRED VALUE}}
For example, you want to get several properties of a harddisk, but one value should be converted from bytes to GB:
Get-WmiObject Win32_LogicalDisk | Select-Object DeviceID, @{N=”GBFreeSpace”;E={$_.FreeSpace/1GB}}

2.3 The CHALLENGE:
The most difficult scripts gather information from all over the place. But you’d still like to present it in a simple and convenient manner. This is when you should build your own output objects and create the properties yourself. In order to get this done for multiple objects in a collection, it is vital you loop through a collection of objects representing the same thing as your output objects. Consider the following example:
Let’s say you want to get some exotic characteristics for all your vm’s. For instance which server it is running on, the last 5 events that occurred and the average memory usage. These aren’t properties of any vm object you can get. So start by looping through all the vm objects you can get, but not before you create an empty collection to old your output objects:
$myCol = @()
$VC = Connect-VIServer MYVCSERVER
$vms = Get-VM
ForEach ($vm in $vms)
{
Then, inside the loop, create a custom output object along with the properties you’d like to see:
$myObj = “” | Select Name, Host, LatestEvents, AvgMemUsage
Then you can start calculating or “get-“ting the proper values for each of these properties. For example:
$myObj.Name = $vm.Name
$myObj.Host = ($vm | Get-VMHost).Name
$myObj.LatestEvents = ($vm | Get-Event | Select-Object -Last 5)
$myObj.AvgMemUsage = ($vm | Get-Stat -Memory -MaxSamples 1).Value
(These examples are off the top of my head and untested.)
Finally, add your output object to the collection and close the loop:
$myCol += $myObj
}

Next time: Scripts that actually change values or perform some other action. Stay tuned.
Hugo

»crosslinked«

Pimp Your Output: Use Objects

 

Did you ever create a script, and when it was all done, rewrite a large part of it to change the way the output is shown, saved or printed? If you use the power of Powershell in a smart way, you will never have to again.

 

One of the great powers of Windows Powershell, is the way everything you use is an object. The output of every single Powershell cmdlet is an object or a collection of objects. This fact allows you to easily filter, sort or format the output of a command by piping is to Where-Object, Sort-Object or one of the Format cmdlets.

 

Now why don’t you write your scripts in the same way? Think for a minute about what kind of objects your output should be and what properties they should have. Then build these objects in your script. When you run your script, dot-source is (preceed the path to your script by a DOT and a SPACE). This way, all variables set in the script become global variables, which means you can access them after running your script. Either interactively, or by running another script.

 

This is the way to build a collection of objects in a script:

 

$myCol = @()

$collection = …

ForEach ($item in $collection)

{

$myObj = “” | Select Name, Property1, Property2

$myObj.Name = …

$myObj.Property1 = …

$myObj.Property2 = …

$myCol += $myObj

}

$myCol

 

First you create an empty array to hold your collection:

$myCol = @()

 

You grab the collection of object you need to manipulate to get your output values and loop through them (e.g.: Get-QADServer to get the servers in your domain):

$collection = …

ForEach ($item in $collection)

{

}

 

Then you build an empty object to represent your output item and define which properties it should have:

$myObj = “” | Select Name, Property1, Property2

 

You then proceed with your script to get the output values you need (e.g.: do a WMI query to a remote server to get disk space information) and set the values to the appropriate properties of your output object:

$myObj.Name = …

$myObj.Property1 = …

$myObj.Property2 = …

 

When your output object is done, you add it to the output collection and the loop will restart:

$myCol += $myObj

 

After running the script (don’t forget to dot-source it: PS D:>. D:scriptsmyscript.ps1), you can manipulate the output collection to save or print it:

E.g.:

PS D:>$myCol | Where {$_.Property1 -gt 100} | Out-Printer

or

PS D:>$myCol | Sort-Object Name | Out-File D:output.txt