top of page
Search

Your PowerShell Code Deserves to Be Treated with Class: Using Classes in PowerShell

  • Writer: Gabriel Delaney
    Gabriel Delaney
  • Apr 4
  • 4 min read

Until recently, I misunderstood PowerShell classes.


Not entirely, of course—I’ve been using them for a while. But like a lot of folks who come up through the admin route rather than the traditional dev path, I don't think I fully understood them. I treated classes as little more than fancy containers for static helper methods. It wasn’t until I started dabbling in Python over the last couple of weeks that something clicked. I realized I’d been underutilizing one of the most powerful tools in PowerShell's toolbox: stateful, object-oriented design.


And I don’t think I’m alone.


A lot of PowerShell users—especially admins, engineers, and IT pros—aren’t software developers. We weren’t taught OOP principles in a CS101 course. We picked up PowerShell because it got the job done, and we built functions and scripts that were practical, direct, and procedural. Classes? That was dev stuff.


But the deeper I go with building out tools like ConditionalAccessIQ and refining internal modules, the more I realize just how clean, more maintainable, and scalable my code becomes when I lean into what classes actually do.

Let me explain.


State: It’s Not Just a Variable With a Fancy Hat

“Why do I need a class? I can just use a variable.”

Yeah, you can. And that’s fine—for simple use cases. But as soon as your script grows beyond a single operation, you start passing around a dozen parameters to each function, tracking context with global variables, or duct-taping return objects together from multiple sources. That’s when you realize: you're managing state manually, and it’s not efficient.

Classes are designed to do that for you. They encapsulate data and behavior. And when you actually instantiate them—use them like real objects—you’re creating a clean, reusable unit that holds both what it knows (its properties) and what it can do (its methods).


A Simple Example: ProgressManager

Let’s use progress reporting to illustrate how classes can encapsulate both state and behavior.


The Typical Way

Here’s how most people write progress updates in a loop:

$total = 5
$iteration = 0

foreach ($item in 'One','Two','Three','Four','Five') {
    $iteration++
    $percentComplete = ($iteration / $total) * 100

    Write-Progress -Activity "Processing $item" `
                   -Status "Processing $iteration of $total" `
                   -PercentComplete $percentComplete

    Start-Sleep -Milliseconds 500
}

It works. But it’s repetitive, tightly coupled to the loop, and not reusable. You end up copying this logic into every script that needs progress tracking.


The Class-Based Way

Now here’s the same logic, abstracted into a class:

class ProgressManager {
    <#
        .SYNOPSIS
        A class to manage the writing of progress to the console.

        .PARAMETER Total
        The total number of items to process.

        .PARAMETER Iteration
        The current iteration number.
    #>
    [int]$Total
    [int]$Iteration = 0
    [int]$PercentComplete = 0

    ProgressManager([int]$total, [int]$iteration = 0) {
        $this.Total = $total
        $this.Iteration = $iteration
    }

    [void]WriteProgress($item) {
        $this.Iteration++
        $this.PercentComplete = ($this.Iteration / $this.Total) * 100
        $write_progress_params = @{}
        $write_progress_params["Activity"] = "Processing $item"
        $write_progress_params["Status"] = "Processing $($this.Iteration) of $($this.Total)"
        $write_progress_params["PercentComplete"] = $this.PercentComplete
        Try {
            Write-Progress @write_progress_params
        } Catch {
            Throw "An error occurred while writing progress to the console.  The error message was: $_"
        }
    }

    [void]ResetProgress() {
        $this.Iteration = 0
        $this.PercentComplete = 0
    }
}

Using the Class

$progress = [ProgressManager]::new(5)

foreach ($item in 'One','Two','Three','Four','Five') {
    Start-Sleep -Milliseconds 500
    $progress.WriteProgress($item)
}

What Makes This Better?

  • State: The class tracks Iteration, Total, and PercentComplete internally—no external tracking or calculations needed.

  • Abstraction: You no longer care how progress is calculated or displayed. You just tell it what item is being processed.

  • Reusability: You can drop this class into any script with a loop and immediately improve readability and maintainability.

It’s not just about writing “fancy code”—it’s about making your logic easier to read, reuse, and debug.


When You Should Reach for a Class

You don’t need classes for everything. I still write quick one-off scripts as plain procedural code. But here’s when I reach for classes:


✅ You need to track state across multiple operations (access tokens, file paths, config settings)

✅ You want to encapsulate behavior alongside that state

✅ You're building a reusable component that other scripts might consume

✅ You're managing multiple instances of “things” (users, policies, alerts, etc.)


Personally, I've started designing classes to encapsulate actions, using functions primarily as orchestrators.


When to Keep Them on the Sidelines

Not every script needs a class. Here’s when I don’t bother:


❌ One-liners or short utilities that do one thing

❌ Anything where the overhead of defining a class outweighs the complexity

❌ Situations where you're dealing with truly stateless processing


There's no prize for forcing OOP into every corner of your script. But there is a lot of power in knowing when and how to use it.


Objections from the (PS)Gallery

Let me get ahead of the usual feedback:

PowerShell isn’t a real OOP language.”

True. And that’s okay. But it’s object-oriented enough to get 90% of the benefits if you understand the mechanics.


“Why make it more complicated than it has to be?”

That’s a fair question—and sometimes the answer is: you shouldn’t. But if you’re building something meant to last, or meant to grow, complexity is going to show up anyway. Using classes just gives you a better way to manage it when it does.


“This is overkill for most admins.”

Maybe. But I’ll bet most admins building modules, automating infrastructure, or working with APIs are already dealing with state—they just don’t realize they’re reinventing object management with makeshift code patterns.


Final Thoughts

I didn’t grow up with C++ or Java. I didn’t have a professor hand me UML diagrams. I came up through the admin world, scripting out of necessity, googling errors, and learning by breaking things. So no, OOP didn’t come naturally to me in PowerShell. But once it did, it has started to change how I build tools.


Classes in PowerShell aren’t just syntactic sugar—they’re an architectural tool. They give structure to your logic, encapsulate as well as abstract complexity, and make your code more readable, testable, and maintainable.

 
 
Post: Blog2_Post

©2022 by thetolkienblackguy. Proudly created with Wix.com

bottom of page