Mastodon

Importing Functions, Classes, and Methods From Other PowerShell Files

I've recently been working on a project that requires me to use PowerShell. I actually feel relatively fluent in PowerShell since it was my main scripting language for a little over a decade while I worked as a sysadmin in highly Windows-centric environments that involved me automating as much of my job as possible in order to be able to do things like sleep on occasion. However, my PowerShell work rarely (read: never) went beyond single file scripts.

With this project being a decent bit more complicated and with the potential for some of the code to be useful in future projects, I wanted to figure out how to actually break things up into useful chunks, just like I would do when writing something in Python. Fortunately, it wasn't terribly difficult to figure out, though, as is typically the case with PowerShell, the documentation was a bit wanting. I had to put information together from a few different resource and go through a little trial-and-error to actually figure it out since Microsoft can never just seem to give clear, concise examples of anything PowerShell-related.

The first thing I needed to realize is that I wanted to create my files not with a normal .ps1 extension but with a .psm1 extension to indicate that they were PowerShell modules. Only the file that would normally be executed directly has a .ps1 extension. I kind of hate this since it makes things more difficult to test individually; in Python, for example, I could just create a main function that executes when:

if __name__ == "__main__":

Then I can add things in main while building it out that are later ignored when the code is called from elsewhere. PowerShell doesn't offer anything like this, though it's not a huge ordeal. After creating a .psm1 file, it can contain functions, classes, and/or methods. For example, here's a sample file called helloFunc.psm1 with just a function:

function Write-Hello {
    param(
        [Parameter(Mandatory=$true)][string]$Name
    )

    Write-Output "Hello, $Name."
}

And here's a file called personClass.psm1 with both a class and a couple of methods:

class Person {
    [String]$Name
    [int]$Age

    # Constructor.
    Person([String]$Name, [int]$Age) {
        $this.Name = $Name
        $this.Age = $Age
    }

    [String]GreetPerson([String]$PreferredGreeting) {
        if($PreferredGreeting -eq "" -or $null -eq $PreferredGreeting) {
            $PreferredGreeting = "Hello"
        }
        return "$PreferredGreeting, $($this.Name). I can't believe you're $($this.Age) years old."
    }

    [void]HaveBirthday() {
        $this.Age++
    }
}

Neither file has an entrypoint, though that's expected since they're designed to be called from somewhere else. Here's the main.ps1 file which ties them all together:

#!/usr/bin/env pwsh
using module ./personClass.psm1
using module ./helloFunc.psm1

Write-Hello -Name "Garrett"

$me = [Person]::new("Garrett", 9000)
Write-Output $me.GreetPerson("Salutations")
$me.HaveBirthday()
Write-Output $me.GreetPerson("Salutations")

The most important thing here are the two using statements, which specify that I'm going to import the two aforementioned files. Once I do this, I can then call classes, methods, and functions in those files directly.