PowerShell / Intermediate: binding, errors, modules, data, and tests / Lesson 10
Package modules with explicit public interfaces
Separate module discovery, import effects, exported members, manifests, dependency versions, and platform compatibility.
On this page
Lesson goals
- Explain script modules, manifests, and explicit exports.
- Treat imports and dot-sourcing as code-execution boundaries.
- Pin and test dependencies independently from command names.
A module is a versioned reusable contract
A PowerShell module can package functions, cmdlets, providers, types, and other resources. Script modules commonly use .psm1, and a manifest can declare metadata, requirements, and exported content. A module name alone does not establish which version or path resolves in a session. Automatic loading and PSModulePath affect discovery, while compatibility depends on edition, operating system, .NET, and external libraries. Record the resolved artifact and test its public commands rather than assuming an importable name proves every operation works. Do not install content just to make a teaching example executable.
Export deliberately and keep private helpers private
Use clear exported commands and a documented input/output/error contract. Private helper names should not become accidental public dependencies. A module update can preserve command spelling while changing emitted types, defaults, validation, or stream behavior, so version review must include behavior. Avoid mutating a caller’s preference variables, location, or global aliases at import time unless explicitly part of a reviewed contract. Dot-sourcing differs from importing a module and runs code in the current scope; it is not a safe shortcut for reading untrusted configuration as inert data. Keep data parsing and trusted code loading separate.
Read an illustrative module without importing it
The following source describes one harmless data function and an explicit export. It is not a complete installed package or a claim that its manifest and platform requirements were checked. No module is imported in this environment. A real package should declare supported PowerShell versions, edition/platform limitations, dependency policy, and tests for the exported interface. A restrictive export list improves reviewability but does not make hidden module initialization harmless. Review import-time code and dependency imports as well as the public function body before approving an automation dependency.
function Get-LabLabel {
[CmdletBinding()]
param([ValidateSet('Review', 'Archive')][string]$Mode = 'Review')
[pscustomobject]@{ Label = $Mode; SchemaVersion = 1 }
}
Export-ModuleMember -Function Get-LabLabel
This is illustrative .psm1-style source only. It was not imported or executed, and no dependency, manifest, profile, or package installation was evaluated.
Compatibility layers can change object and process boundaries
PowerShell 7 can support selected Windows PowerShell compatibility scenarios on Windows, but that is not universal native compatibility or cross-platform availability. A compatibility mechanism can involve another process and serialized objects, which changes method availability and state semantics. Windows-only APIs remain platform constraints. Read the module’s official support statement and the exact compatibility path, then test the consumer’s actual required operations. Do not infer that a successful import means remote methods, provider actions, credentials, or administrative features are supported unchanged under every edition. Preserve these limitations in project documentation.
Check declared dependency identity without resolving installed content
An offline manifest-like dictionary can compare the intended module identity and version with a synthetic resolved record. It is not a semantic-version solver, PowerShell manifest parser, module resolver, or import test. A real release workflow should preserve source provenance, exact resolved dependencies where required, supported runtime, and a rollback-compatible prior artifact. Test changes in command behavior separately from version labels. The course performs no gallery lookup, installation, discovery, import, or uninstallation; only declared data is compared, leaving the execution boundary explicit.
required = {'Name': 'Lab.Reporting', 'Version': '1.0.0', 'Edition': 'Core'}
resolved_fixture = required.copy()
print('declared_lock_matches=' + str(required == resolved_fixture))
print('module_import=not-performed')
declared_lock_matches=True
module_import=not-performed
Lab.Reporting is a fictional example identity. No package is claimed to exist, downloaded, or loaded; this is a dictionary-equality check only.
Common errors and fixes
Module name is treated as an exact version lock
- Why it happens
- Identity, path, and resolved version were conflated.
- How to fix it
- Record and validate the actual dependency resolution.
- Prevent it next time
- Keep a reviewed dependency manifest.
Dot-sourced configuration executes unexpected code
- Why it happens
- Code loading was mistaken for data parsing.
- How to fix it
- Use a defined data parser for untrusted input.
- Prevent it next time
- Review every import/source boundary.
Compatibility import is called native cross-platform support
- Why it happens
- Process, serialization, and OS limitations were omitted.
- How to fix it
- Test the actual supported consumer operations.
- Prevent it next time
- Document compatibility mechanisms and their limits.
Check your understanding
1. Why is a restricted export list insufficient to prove safe import?
Show worked solution
Module initialization and dependencies can execute before a public function is called. Review that code and environmental effects separately, then test the public interface under the declared runtime and package version.
2. What can a synthetic dependency dictionary establish?
Show worked solution
It establishes consistency between declared fixture values only. It cannot prove that a package exists, is authentic, is installed, resolves from the expected path, imports successfully, or supports the target platform.
Key takeaways
- Version and review modules as executable dependencies.
- Separate public API, import effects, and compatibility mechanisms.