In our previous series on learning Powershell, the very first topic of discussion was the storage of data. In my opinion, the 2 most important things in computing are interface and data structure. As developers, our interface to the data is using varible naming conventions that make sense. Each of us have a personal style, but using descriptive names for our variables serves us when we inevitably have to revisit our own code a year from now. Comments, variable names, and formatting make it easier to get back to work rather than spending time figuring out what we were thinking a year ago.

Our most basic element of storage for a datum is a variable containing a single piece of information. Remember when I said data was plural? You thought I was just being glib. We're going to work up from this starting point:

[INT]$performanceCounterPercentage = 0.5523

Our format here is to identify the class of the datum, the variable name, the assignment operator, and the value. This lets Powershell know exactly what we're looking for, but also gives us the most information for we the programmer to be able to tell exactly what we're using that assignment for.

In terms of which class to use for a datum, it is correct to use the smallest version of the class type you need for any possible value. There is a speed cost for overprovisioning your dataset. It is easiest to use the largest one we can. That way, we can stuff anything inside it we need without worrying about exceeding those minimum and maximum ranges I illustrated for each of the different classes in the previous discussion on data types.

[byte]$inBounds = 255

[byte]$outOfBounds = 256

This will succeed for the first line as [byte] is 8 bits long, 0-255. Since 256 is larger than the memory we're telling powershell to set aside for it, the second line will produce an error. You'll note that I didn't capitalize the data type in this example. It's not case-sensitive, so we can choose how we like it to read. I'm lazy, so I tend to keep things lowercase as much as possible. You'll also note the variable names have a specific capitalization scheme. It's called "Camel Case" due to the humps in the middle. Each word is capitalized. We could also use underscores to separate the words. However it looks best to you works just fine:

$outOfBounds

$Out_Of_Bounds

$out_of_bounds

Remember that generally, your code will be written while you're conscious and alert. You'll be fixing it at 3am while mainlining coffee because your business is at risk due to your code breaking due to an update that changes the environment in which your code is supposed to be running... that feels really specific, doesn't it?

This structure works well for singular pieces of information, but often we're dealing with more than just the one piece of data. Yes, that's correct pluralization - a datum is a piece of data - stop interrupting, we've got quite a bit of work to do yet.

To deal with more than one datum (happy?), we'll use an array:

$volumeCapacities = @(

53687091200,

53687091200,

107374182400

)

This makes less sense if we're storing different pieces of information.

$drive_info = @(

"Server System Volume",

53687091200,

0.24

)

For this type of use, we really want some type of structure that let's us access the data in a meaningful way. If we look back to our previous set of lessons, we're looking for a hashtable:

$drive_info = @{

volumeName = "Server System Volume",

capacity = 53687091200,

percentUsed = 0.24

}

This will let us access the information more easily:

$drive_info.volumeName

You'll note here that Powershell chose what to use for the individual data type:

$drive_info.percentUsed.GetType().name

This shows us that it's chosen a fairly large [DOUBLE] for the percentUsed. If we don't need that many digits to the right of the decimal for these values, we should choose something smaller. If we're working on a thousand servers, that extra storage will add up and start to force more memory swapping to disk at some point. To allow for defining the data type in this type of structure, we can prepopulate our variables before adding them to the hashtable:

[string]$volumeName = "Server System Volume"

[int64]$capacity = 53687091200

[single]$percentUsed = 0.24

$drive_info = @{

volumeName = $volumeName;

capacity = $capacity;

percentUsed = $percentUsed

}

Now that same command shows us that we're storing it as a single:

$drive_info.percentUsed.GetType().name

This saves 4 bytes per instance of that hashtable. It doesn't sound like much, but we're coding for scale and speed. Every little bit (PUN!) counts. CAVEAT: The whole Y2K thing was due to variables containing only enough for 2 digits of the year to be stored. Make sure to plan for expansion while still keeping the memory footprint as light as possible.

Wouldn't it be nice if we could take our capacity and display it automatically as MB or GB without having to do any math to it? We can build our own METHOD for these types of tasks, but it'll require that we make our own class.

In the last series, when we covered loops, we used the class [PSObject]. It came with an open set of PROPERTIES that we could define with our hashtable and a set of METHODS built in. One of them that we used was .add() This was brought up as a way to speed up our script by using the built in method rather than the slower += operator that added our object to the end of our array.

In the same way we make a function:

function functionName {}

we can pre-define our own class:

class className {}

So let's make our driveInfo class:

class driveInfo {

[string]$volumeName

[int64]$capacity

[single]$percentUsed

}

So now we can ask for a new instance of that class:

$drive_1 = New-Object driveInfo

So we can set the properties directly thusly:

$drive_1.volumeName = "Server System Volume"

If you have predefined information that you want to have access to, but not something that will ever be altered, you can define it as STATIC and set a value right within the declaration:

class driveInfo {

[string]$volumeName

[int64]$capacity

[single]$percentUsed

static[string]$objectType = "Hard Drive"

}

This allos us to access it as a static property using a pair of colons. That looks like [type]::property :

$drive_2 = New-Object driveInfo

$drive_2::objectType

This will now return the value assigned in the class declaration. If you try to alter it, it'll yell at you. This is how the [system.math]::pi works. it holds the value for pi. It's predefined and changing it makes no sense at all as it's a constantly defined value. It's also the way we got to the [INT]::minValue last time.

Let's make our method using this structure within the class declaraion:

[returnType]methodName ($argument1, $argument2, ...){}

If you're bringing in arguments, you'll want to make sure to verify that the values coming in are the expected type and range of value. Malicious hackers will find a way to exploit this type of thing if they're given any access at all. Bacially, it's:

[INT32]methodName ([ExpectedType]$argument1) {

if(($argument1 -ge $minVal) -and ($argument1 -le $maxVal)){

#do stuff here

}

}

I'm going to speed up the testing for these examples by adding default values directly. Let's do it!

class driveInfo {

# Property definitions here

[string]$volumeName = "Volume Name"

[int64]$capacity = 53687091200

[single]$percentUsed = 0.0

# Static values go here

static[string]$objectType = "Hard Drive"

# Methods are here

[INT32]capacityGB () {

return $this.capacity / 1GB

}

}

$drive_3 = New-Object driveInfo

Write-Output $drive_3.capacity

Write-Output $drive_3.capacityGB()

The only other thing today is to wrap your head around the fact that the properties within the class can be hashtables. You can also put these into other arrays and objects. My senior thesis in college was doing complex geospatial data visualization. The dataset for that project contained a massive amount of hashtables within hashtables within hashtables. I felt like I was losing it, but eventually, my little metal top wobbled and tipped over, so I came out of that just fine.

Next time, we'll address how to take these pieces of data and moving them around in your project.