DEV Community

Cover image for Powershell: Keeping it Classy and Functional - Part 1of3
TheInfraDev
TheInfraDev

Posted on • Updated on • Originally published at theinfradev.pro

Powershell: Keeping it Classy and Functional - Part 1of3

Let’s keep it Classy

"48 hrs to... Oh, Shutup!"

In PowerShell 5.0 Microsoft introduced proper Classes giving us a very powerful capability that to the average system admin probably went unused.
In fact, the greatness of classes is in how we can combine their data and functional capabilities with our standard PowerShell functions to create much cleaner code, with well-defined configurations and output.

If you have never created a Class in PowerShell or... ever, I suggest trying it out seperately. I won’t go into all the specifics as this is less about how to create a class, and more about what we can do with them, once we are comfortable. I suggest this guy’s blog as a good primer, or at least to bookmark for later.

Powershell Classes and Concepts

In this first post I will create our first data classes by emulating a physical process. In this case we are going to build a configuration for a little Gaming PC to hook up to the TV. The main build is not particularly powerful using only an AMD integrated GPU however it should be able to get decent performance for couch games such as Rocket League on medium settings at 1080p. Because of our use of classes we can later extend this configuration to make it (our PC configuration) more powerful.

Let’s create our first class to store our configuration

Can’t we just buy an Xbox or PS4? Seems easier.

Ok, so let’s define the parts we want in our little TV gaming box. We will need the following as our base config:

  • Mainboard: MSI B450M Mortar Titanium ($105)
  • RAM: Team Delta RGB - 16 GB DDR3000 ($135)
  • Storage: Samsung 970 EVO 500 GB ($128)
  • Case: Thermaltake Level 20 VT Chassis ($105)
  • PSU: SeaSonic FocusPlus Gold 650W ($90)

We are going to standardize around this platform allowing use to make slight changes to CPU, GPU if we want to beef up the processing power.

I don’t particularly want to go into the reasons why I have chosen AMD over INTEL or 16GB over 8GB but suffice to say If you build this computer it should all work.

So how do we do this as a class in Powershell. Well the first thing to know is PowerShell makes this super easy and we can essentially just list our items and PowerShell will worry about how to construct the object. This is the simplest way we can create our configuration.

Class PCSystem
{
   [string]$CPU
   [string]$Mainboard   = 'MSI B450M Mortar Titanium'
   [string]$Case        = 'Thermaltake Level 20 VT Chassis'
   [string]$PowerSupply = 'SeaSonic FocusPlus Gold 650W'
   [string]$RAM         = 'Team Delta RGB - 16 GB DDR3000'
   [string]$Storage     = 'Samsung 970 EVO 500 GB'
   [string]$GPU
}

In the above example we can see that we have declared the variable type and then the variable name. For some that is all we have declared, however for others we have declared a default value. This means when this object is instantiated later, we already have these values keyed in. Because I want to keep our options open I have kept our CPU/GPU options as undeclared values.

Now we have this base class we can instantiate it.

$TVPC =  [PCSystem]::new()
Now I am going to add a little gem of a Processer, an APU (CPU + GPU) from AMD that will give our box some great processing capacity and due to the integrated GPU gives us a low end gaming system for very little money. (USD$158)
$TVPC.CPU = 'AMD Ryzen 5 2400G'
Now our CPU is added, we have our base system. If we print this to console we get:
CPU         : AMD Ryzen 5 2400G
Mainboard   : MSI B450M Mortar Titanium
Case        : Thermaltake Level 20 VT Chassis
PowerSupply : SeaSonic FocusPlus Gold 650W 
RAM         : Team Delta RGB - 16 GB DDR3000 
Storage     : Samsung 970 EVO 500 GB 
GPU         :
Perfect, now we have a simple object which describes our configuration which we can pass around between functions as we like. Later if we decide we need extra storage or any other attributes we can just add them to our class, and they will automatically be instantiated, so we have a very extensible config.

Let’s make our own Class constructors!

I thought we were going to let PowerShell do that for us?

You are probably thinking, that’s kind of cool, Job done! Well, yes it is. But we can do it better.

The great thing about classes is that unlike say a hash table - we can add behaviour(methods) to a class via a constructor.

Quick primer: A constructor tells the language engine how to build the Class object.

We can give the class logic so that if the class is not provided input on creation - it will be the default class, or alternatively create a more custom version of the class if provided a configuration option.

Let’s have a look at what the constructor looks like. (I am going to compress the variable declarations at the top because it looks neater).

Class PCSystem
{
   $CPU; $Mainboard; $Case; $PowerSupply; $RAM; $Storage; $GPU
   PCSystem([string]$CPU)
   {
       [string]$this.CPU           = $CPU 
       [string]$this.Mainboard     = 'MSI B450M Mortar Titanium' 
       [string]$this.Case          = 'Thermaltake Level 20 VT Chassis'
       [string]$this.PowerSupply   = 'SeaSonic FocusPlus Gold 650W'
       [string]$this.RAM           = 'Team Delta RGB - 16 GB DDR3000'
       [string]$this.Storage       = 'Samsung 970 EVO 500 GB'
    }
}
So what is the difference here? Well, now we have made a constructor method that has the same name as the class (this is important). PowerShell will now treat this method as its constructor and knows that when an instance of this object is created it must follow the rules provided in this method:
  • First, instead of declaring the defaults when we create the variables we are doing it in the constructor, this allows us to make this decision depending on the constructor rather than just by default.
  • Second, we are requiring that on creation, a value be given for the CPU. This way we are requiring the decision early, but ultimately saying - without this we cannot proceed.
  • Third, in this particular case you will note the GPU is ignored. This particular configuration doesn’t care about GPU so we don’t need to specify it. However the GPU attribute will still be created because it is referenced at the top of the class.
    Note: Like many other languages when you refer to a variable of the class, (like CPU for example) we need to call it "$This.CPU". The ‘this’ part of the parameter is the way .NET handles talking about the actual instances of the object rather than the abstract class we are working from here. In our example earlier where we created the $TVPC object, "$this.CPU" is an analogy for writing "$TVPC.CPU".

    Due to this constructor, we can now define our CPU on creating the variable, shrinking our code from 2 lines to 1 and making sure the object is at a minimum spec from the beginning.

    We can now create it, like so:

    $TVPC =  [PCSystem]::new('AMD Ryzen 5 2400G')
    

    What’s Next?

    Wait, you were saying stuff? Sorry I was busy downloading Red Dead Redemption 2 on my PS4 that I just bought.

    I hope that while I am using a real world physical object here, you could see how this could be used to describe configuration of any complex function. And by using a simple constructor we could create minor variations of our configuration which we can call depending on circumstance. Say for example, you want to create a fleet of VMs and you have 2 different memory configurations. By having these constructors you can save a lot of time and duplicated code by just changing the input to the constructor from one config to the other, and PowerShell will take care of the rest.

    OK, that is it for this post, In my next post I will look at how we can use constructors further as well as general class methods to build a script into a more complex yet maintainable bit of software.

    Cheers,


    TheInfraDev


    To get updates on my new articles, follow me on Twitter @theinfradev

    Photo by Heng Films on Unsplash

Top comments (0)