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"
$thinkpad
Notice 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"
$macbook
You 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"
)
$device
It’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.Slots
If 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.