InvokeSystems.Common: the boring foundation
InvokeSystems.Common: Two Functions You’ll Never Think About
(unless they’re missing)
Every module suite needs shared utilities. Logging setup. Privilege checks. Path resolution. The mundane infrastructure that every module depends on but nobody wants to write twice.
InvokeSystems.Common is that infrastructure. Two functions, both boring, both necessary.
Initialize-InvEventLogSource ensures a Windows Event Log source exists before you try to write to it. Test-InvAdministratorContext tells you whether the current session is running with administrator privileges.
That’s it. No clever abstractions, no ambitious scope creep. Just the minimal shared functionality required by the rest of the suite.
Why Event Log Setup Needs a Function
Windows Event Log sources can’t be created on the fly. You have to register them in advance, and you need administrator privileges to do it.
Attempting to write to a nonexistent source fails silently or throws unhelpful errors depending on how you invoke Write-EventLog. The error messages don’t clearly explain that the source doesn’t exist or that you need admin rights to create it.
The typical workaround is to run a setup script that creates the source before deploying the main module. That works, but it splits installation across multiple steps and assumes users remember to run the setup script.
Initialize-InvEventLogSource makes this deterministic. Call it during module initialization or before your first event log write. If the source exists, it does nothing. If the source is missing and you have admin privileges, it creates it. If you lack privileges, it warns you clearly and skips creation.
The function supports -WhatIf and -DryRun for testing in environments where you can’t or won’t modify event log configuration. It fails loudly when creation is required but impossible, rather than silently allowing later event writes to fail mysteriously.
The Admin Context Check
The second function, Test-InvAdministratorContext, does one thing: return $true if the current PowerShell session is running with administrator privileges, $false otherwise.
This is trivial to implement but tedious to replicate. You query the current Windows principal, check if it has the Administrator role, and return the result. Every script that requires elevation needs this check. Copy-pasting the implementation across modules leads to inconsistencies and makes updates painful.
Centralizing it in Common means one implementation, one place to test, one place to fix bugs. Every other module that requires elevation calls this function instead of reimplementing it.
The function doesn’t attempt to elevate or re-launch with elevated privileges. It just reports the current state. What you do with that information is your problem. Some scripts throw an error and exit. Some degrade gracefully. Some log a warning and continue. The check itself stays simple and single-purpose.
Why This is a Separate Module
InvokeSystems.Common could have been embedded directly into each module that needs it. RestrictedTask and UpdateRepair both require admin checks. Several modules benefit from event log initialization.
But embedding means duplication. Duplication means drift. Eventually one module gets a bugfix that another doesn’t. Or someone improves the error messages in one place and forgets to propagate the change.
Extracting the shared logic into a separate module enforces consistency. All modules import Common as a dependency. Updates to Common propagate automatically. There’s one canonical implementation instead of N slightly different versions.
This does add a dependency. Modules that use Common can’t function without it. That’s acceptable because Common is small, stable, and changes infrequently. The tradeoff favors maintainability over independence.
What This Doesn’t Do
InvokeSystems.Common doesn’t provide a full logging framework. It initializes event log sources. Writing actual log entries is left to Write-EventLog or whatever logging approach your scripts prefer.
It doesn’t handle log rotation, filtering, structured logging, or log shipping. Those are real concerns, but they’re outside the scope of this module. If you need comprehensive logging, use a dedicated framework. Common just handles the setup prerequisite.
The admin check doesn’t validate specific privileges beyond “is this session elevated.” It doesn’t check for SeDebugPrivilege, SeBackupPrivilege, or other granular rights. If you need fine-grained privilege validation, query those explicitly. This function answers one question: elevated or not?
It doesn’t provide cross-platform privilege checking. This is Windows-specific infrastructure for Windows-specific modules. PowerShell Core on Linux has different privilege models and different logging mechanisms. Supporting those would complicate the implementation without benefiting the core use case.
Why These Two Functions Together
Event log setup and admin checking solve adjacent problems: initializing infrastructure that requires elevated privileges.
Most modules that need event logging also need admin rights for their core functionality. Checking both conditions before attempting privileged operations prevents cryptic failures midway through execution.
Bundling them together also establishes a pattern for future additions. If another shared utility emerges—maybe path resolution or module-wide configuration handling—it belongs in Common. The module becomes the home for unglamorous shared infrastructure.
That said, Common stays minimal. Every addition needs strong justification. The goal isn’t to build a framework. It’s to avoid duplicating simple utilities across multiple modules.
Dependency Management
RestrictedTask and UpdateRepair both declare InvokeSystems.Common as a required module in their manifests. PowerShell’s module loader handles this automatically: when you import RestrictedTask, Common loads first.
For manual deployments where manifest-based dependencies don’t work, both modules attempt to import Common from a sibling directory before falling back to the standard module path. This supports development workflows where modules aren’t formally installed.
If Common is missing entirely, the import fails early with a clear error. No cryptic function-not-found errors halfway through execution.
The meta-module InvokeSystems imports Common explicitly as part of loading the full suite. Since Common has no dependencies of its own, it’s always safe to load first.
Testing Common
Testing these functions is straightforward but requires some setup. Testing event log creation requires a disposable event source name and cleanup afterward. Testing admin context requires running tests in both elevated and non-elevated sessions.
The Pester tests cover both success and failure paths. Creating sources that already exist. Creating sources without admin privileges. Checking admin context in various security contexts.
Because the functions are simple, the tests are simple. That’s by design. Complex utilities require complex tests. Simple utilities require simple tests. Common stays firmly in the latter category.
Why This Matters
InvokeSystems.Common is the least interesting module in the suite. It doesn’t solve user-facing problems. It doesn’t automate complex workflows. It doesn’t demonstrate clever techniques.
It exists so other modules don’t have to reimplement the same boring utilities. That’s valuable, but it’s infrastructure value. Users don’t install Common directly. They install RestrictedTask or UpdateRepair, and Common comes along as a dependency.
Good shared infrastructure is invisible. You don’t think about it until it’s missing. Then everything breaks in annoying ways.
That’s the entire justification for this module. Making two simple things consistent, testable, and reliable across the suite.
The Real Point
Most modules start as monoliths. Everything lives in one file. As the codebase grows, duplicated logic accumulates. Eventually someone extracts the common bits into shared utilities.
InvokeSystems.Common exists because that pattern played out predictably. RestrictedTask needed event logging and admin checks. UpdateRepair needed the same. Generator might need them later.
Rather than wait for duplication to become painful, Common was extracted early. Two functions, minimal scope, clear purpose.
It’s not exciting. That’s fine. Infrastructure doesn’t need to be exciting. It needs to work reliably and stay out of the way.
Common does both.