Classes are a common term in object-oriented programming, which you’d see normally in a language such as C# or Python. By using classes of data, you can create your own data types with properties, methods, inheritance, and more. PowerShell supports classes and this can be used to make your scripts cleaner and easier to read.
When you declare classes, you’re creating a blueprint that can make instances of objects at runtime. For instance, if you declare the class Device and initialize the variable $thinkpad using it, $thinkpad becomes a logical instance of a Device type. If you initialized the $macbook variable using the Device, it would have the same blueprint as $thinkpad, but could hold different data.
Class Definition in PowerShell
First, we’ll define the Device class.
class Device {
[string]$Brand # No commas!
# If every device has to have a brand, we could use:
# [ValidateNotNullOrEmpty()] [string]$Brand
[string]$Color
}
Now, we can initialize a variable.
$thinkpad = [Device]::new()
$thinkpad.Brand = "Lenovo"
$thinkpad.Color = "Black"
$thinkpadNotice that when calling the variable, we get a table with it’s data. If we do the same using a different variable, the data is separate but uses the same method.
$macbook = [Device]::new()
$macbook.Brand = "Apple"
$macbook.Color = "Silver"
$macbookYou can also make Constructors that require input to initialize a variable using a class. This allows you to add parameters during variable initialization to avoid method-based assignment.
class Device {
[string]$Brand
[string]$Model
[string]$VendorSku
Device(
[string]$b,
[string]$m,
[string]$vsk
){
$this.Brand = $b
$this.Model = $m
$this.VendorSku = $vsk
}
}
[Device]$device = [Device]::new(
"Fabrikam, Inc.",
"Fbk5040",
"5072641000"
)
$deviceIt’s possible to initialize classes using other classes as well. In this example from Microsoft, there is a class Rack which can hold a set number of Devices, and the Fabrikam device is added to the third slot. This shows you can use methods to put a Device on the Rack and keep track of everything.
class Device {
[string]$Brand
[string]$Model
[string]$VendorSku
[string]ToString(){
return ('{0}|{1}|{2}' -f $this.Brand, $this.Model, $this.VendorSku)
}
}
class Rack {
[int]$Slots = 8
[string]$Brand
[string]$Model
[string]$VendorSku
[string]$AssetId
[Device[]]$Devices = [Device[]]::new($this.Slots)
[void] AddDevice([Device]$dev, [int]$slot){
## Add argument validation logic here
$this.Devices[$slot] = $dev
}
[void]RemoveDevice([int]$slot){
## Add argument validation logic here
$this.Devices[$slot] = $null
}
[int[]] GetAvailableSlots(){
[int]$i = 0
return @($this.Devices.foreach{ if($_ -eq $null){$i}; $i++})
}
}
$rack = [Rack]::new()
$device = [Device]::new()
$device.Brand = "Fabrikam, Inc."
$device.Model = "Fbk5040"
$device.VendorSku = "5072641000"
$rack.AddDevice($device, 2)
$rack
$rack.GetAvailableSlots()If there’s a value we don’t want to change, we can set it as hidden. This will still be visible if checked but helps prevent accidental changes.
class Device {
[string]$Brand
[string]$Model
}
class Rack {
[int] hidden $Slots = 8
[string]$Brand
[string]$Model
[Device[]]$Devices = [Device[]]::new($this.Slots)
Rack ([string]$b, [string]$m, [int]$capacity){
## argument validation here
$this.Brand = $b
$this.Model = $m
$this.Slots = $capacity
## reset rack size to new capacity
$this.Devices = [Device[]]::new($this.Slots)
}
}
[Rack]$r1 = [Rack]::new("Fabrikam, Inc.", "Fbk5040", 16)
$r1
$r1.Devices.Length
$r1.SlotsIf you want to have all instances of a class rely on the same data, you can use a static attribute.
class Device {
[string]$Brand
[string]$Model
}
class Rack {
hidden [int] $Slots = 8
static [Rack[]]$InstalledRacks = @()
[string]$Brand
[string]$Model
[string]$AssetId
[Device[]]$Devices = [Device[]]::new($this.Slots)
Rack ([string]$b, [string]$m, [string]$id, [int]$capacity){
## argument validation here
$this.Brand = $b
$this.Model = $m
$this.AssetId = $id
$this.Slots = $capacity
## reset rack size to new capacity
$this.Devices = [Device[]]::new($this.Slots)
## add rack to installed racks
[Rack]::InstalledRacks += $this
}
static [void]PowerOffRacks(){
foreach ($rack in [Rack]::InstalledRacks) {
Write-Warning ("Turning off rack: " + ($rack.AssetId))
}
}
}
# Test the racks
[Rack]::InstalledRacks.Length
[Rack]::PowerOffRacks()
(1..10) | ForEach-Object {
[Rack]::new("Adatum Corporation", "Standard-16",
$_.ToString("Std0000"), 16)
} > $null
[Rack]::InstalledRacks.Length
[Rack]::InstalledRacks[3]
[Rack]::PowerOffRacks()This is just the surface of PowerShell classes. We haven’t touched on inheritance, pulling classes from PowerShell modules, and more. For the scripting enthusiast, this allows for some tight code and makes life much easier.