Skip to content

Add Authenticode signing support for PowerShell modules#92

Merged
HeyItsGilbert merged 10 commits intomainfrom
claude/add-module-signing-XKMbq
Feb 20, 2026
Merged

Add Authenticode signing support for PowerShell modules#92
HeyItsGilbert merged 10 commits intomainfrom
claude/add-module-signing-XKMbq

Conversation

@HeyItsGilbert
Copy link
Member

Summary

This PR adds comprehensive Authenticode code-signing capabilities to PowerShellBuild, enabling modules to be signed with digital certificates from multiple sources. It includes three new public functions and corresponding build tasks for signing module files and creating/signing Windows catalog files.

Key Changes

  • New Function: Get-PSBuildCertificate - Resolves code-signing X509Certificate2 objects from five different sources:

    • Auto (environment variable or certificate store, configurable)
    • Windows certificate store (with optional thumbprint filtering)
    • Base64-encoded PFX from environment variables (CI/CD pipelines)
    • PFX files on disk with optional password protection
    • Pre-resolved certificate objects (for custom providers like Azure Key Vault)
  • New Function: Invoke-PSBuildModuleSigning - Signs PowerShell module files (*.psd1, *.psm1, *.ps1) with Authenticode signatures, supporting configurable timestamp servers and hash algorithms (SHA256, SHA384, SHA512, SHA1)

  • New Function: New-PSBuildFileCatalog - Creates Windows catalog (.cat) files that record cryptographic hashes of module contents for tamper detection

  • New Build Tasks - Added to both psakeFile.ps1 and IB.tasks.ps1:

    • SignModule - Signs module files with Authenticode
    • BuildCatalog - Creates a Windows catalog file
    • SignCatalog - Signs the catalog file
    • Sign - Meta-task that orchestrates the full signing pipeline
  • Configuration - Extended build.properties.ps1 with comprehensive Sign configuration section supporting:

    • Certificate source selection and parameters
    • Timestamp server configuration
    • Hash algorithm selection
    • File inclusion patterns
    • Catalog generation settings (version, filename)
  • Localization - Added localized messages for certificate resolution, file signing, and catalog creation

Implementation Details

  • All signing operations include platform checks (Windows-only) with appropriate warnings
  • Pre-condition checks ensure signing is only attempted when enabled and dependencies are available
  • Certificate resolution supports both explicit configuration and environment-based auto-detection
  • Task dependencies ensure proper execution order: Build → SignModule → BuildCatalog → SignCatalog
  • Verbose logging throughout for troubleshooting certificate resolution and signing operations

https://claude.ai/code/session_01Bt5Xb9HLoSppQ22PQUTyGP

Introduces a flexible, opt-in code-signing pipeline covering:
- Authenticode signing of module files (*.psd1, *.psm1, *.ps1)
- Windows catalog (.cat) file creation via New-FileCatalog
- Authenticode signing of the catalog file

**New public functions:**
- Get-PSBuildCertificate   – resolves a code-signing cert from five
  interchangeable sources: Auto, Store, Thumbprint, EnvVar (Base64 PFX
  in a CI/CD secret), or PfxFile. Auto mode checks the env var first
  and falls back to the certificate store, enabling the same psakeFile
  to work on developer machines and in GitHub Actions / Azure DevOps.
- Invoke-PSBuildModuleSigning – signs files matching configurable glob
  patterns under the module output directory using Set-AuthenticodeSignature.
- New-PSBuildFileCatalog      – wraps New-FileCatalog to create a
  SHA2 (version 2) or SHA1 (version 1) catalog file.

**New psake/Invoke-Build tasks:**
- SignModule    – signs module files; skips gracefully when disabled or
                 on non-Windows platforms.
- BuildCatalog  – creates the .cat file; independent enable flag.
- SignCatalog   – signs the catalog file.
- Sign          – meta task (depends on SignCatalog).

All tasks respect overridable $PSB*Dependency variables so consumers
can splice them into any position in their build graph, e.g.:
  $PSBPublishDependency = @('Sign')

**New $PSBPreference.Sign properties:**
  Enabled, CertificateSource, CertStoreLocation, Thumbprint,
  CertificateEnvVar, CertificatePasswordEnvVar, PfxFilePath,
  PfxFilePassword, Certificate (direct X509Certificate2 object),
  TimestampServer, HashAlgorithm, FilesToSign,
  Catalog.{Enabled, Version, FileName}

All features are disabled by default (Sign.Enabled = $false).

https://claude.ai/code/session_01Bt5Xb9HLoSppQ22PQUTyGP
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Authenticode signing support to the PowerShellBuild pipeline by introducing certificate resolution + module/catalog signing functions and wiring them into both psake and Invoke-Build task graphs.

Changes:

  • Adds new public functions for certificate resolution, module file signing, and Windows catalog generation.
  • Extends build configuration and localized messages to support signing options.
  • Introduces new build tasks (SignModule/BuildCatalog/SignCatalog/Sign) in both psake and Invoke-Build task files.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 16 comments.

Show a summary per file
File Description
PowerShellBuild/psakeFile.ps1 Adds signing/catalog tasks and dependency defaults to the psake build pipeline.
PowerShellBuild/IB.tasks.ps1 Adds equivalent signing/catalog tasks for Invoke-Build.
PowerShellBuild/build.properties.ps1 Introduces Sign configuration (cert sources, timestamp, algorithms, catalog settings).
PowerShellBuild/en-US/Messages.psd1 Adds localized strings for certificate resolution, signing, and catalog creation.
PowerShellBuild/Public/Get-PSBuildCertificate.ps1 New public function to resolve certificates from Store/Thumbprint/EnvVar/PfxFile.
PowerShellBuild/Public/Invoke-PSBuildModuleSigning.ps1 New public function to sign module files using Set-AuthenticodeSignature.
PowerShellBuild/Public/New-PSBuildFileCatalog.ps1 New public function to generate a Windows catalog file via New-FileCatalog.
PowerShellBuild/PowerShellBuild.psd1 Exports the newly added public functions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 245 to 267
Task SignModule -Depends $PSBSignModuleDependency -PreCondition $signModulePreReqs {
$certParams = @{
CertificateSource = $PSBPreference.Sign.CertificateSource
CertStoreLocation = $PSBPreference.Sign.CertStoreLocation
CertificateEnvVar = $PSBPreference.Sign.CertificateEnvVar
CertificatePasswordEnvVar = $PSBPreference.Sign.CertificatePasswordEnvVar
}
if ($PSBPreference.Sign.Thumbprint) {
$certParams.Thumbprint = $PSBPreference.Sign.Thumbprint
}
if ($PSBPreference.Sign.PfxFilePath) {
$certParams.PfxFilePath = $PSBPreference.Sign.PfxFilePath
}
if ($PSBPreference.Sign.PfxFilePassword) {
$certParams.PfxFilePassword = $PSBPreference.Sign.PfxFilePassword
}

$certificate = if ($PSBPreference.Sign.Certificate) {
$PSBPreference.Sign.Certificate
} else {
Get-PSBuildCertificate @certParams
}

Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The certificate-resolution block is duplicated across SignModule and SignCatalog tasks (and also repeated in IB.tasks.ps1). This increases the risk of future drift (e.g., adding a new cert source/parameter in one place but not the other). Consider factoring this into a shared helper (e.g., Resolve-PSBuildSigningCertificate) used by both tasks.

Suggested change
Task SignModule -Depends $PSBSignModuleDependency -PreCondition $signModulePreReqs {
$certParams = @{
CertificateSource = $PSBPreference.Sign.CertificateSource
CertStoreLocation = $PSBPreference.Sign.CertStoreLocation
CertificateEnvVar = $PSBPreference.Sign.CertificateEnvVar
CertificatePasswordEnvVar = $PSBPreference.Sign.CertificatePasswordEnvVar
}
if ($PSBPreference.Sign.Thumbprint) {
$certParams.Thumbprint = $PSBPreference.Sign.Thumbprint
}
if ($PSBPreference.Sign.PfxFilePath) {
$certParams.PfxFilePath = $PSBPreference.Sign.PfxFilePath
}
if ($PSBPreference.Sign.PfxFilePassword) {
$certParams.PfxFilePassword = $PSBPreference.Sign.PfxFilePassword
}
$certificate = if ($PSBPreference.Sign.Certificate) {
$PSBPreference.Sign.Certificate
} else {
Get-PSBuildCertificate @certParams
}
function Resolve-PSBuildSigningCertificate {
param(
[Parameter(Mandatory = $true)]
$SignPreference
)
$certParams = @{
CertificateSource = $SignPreference.CertificateSource
CertStoreLocation = $SignPreference.CertStoreLocation
CertificateEnvVar = $SignPreference.CertificateEnvVar
CertificatePasswordEnvVar = $SignPreference.CertificatePasswordEnvVar
}
if ($SignPreference.Thumbprint) {
$certParams.Thumbprint = $SignPreference.Thumbprint
}
if ($SignPreference.PfxFilePath) {
$certParams.PfxFilePath = $SignPreference.PfxFilePath
}
if ($SignPreference.PfxFilePassword) {
$certParams.PfxFilePassword = $SignPreference.PfxFilePassword
}
if ($SignPreference.Certificate) {
return $SignPreference.Certificate
}
return Get-PSBuildCertificate @certParams
}
Task SignModule -Depends $PSBSignModuleDependency -PreCondition $signModulePreReqs {
$certificate = Resolve-PSBuildSigningCertificate -SignPreference $PSBPreference.Sign

Copilot uses AI. Check for mistakes.
* Introduced tests for `Get-PSBuildCertificate`, `Invoke-PSBuildModuleSigning`, and `New-PSBuildFileCatalog`.
* Validated various certificate sourcing methods including `Auto`, `Store`, `Thumbprint`, `EnvVar`, and `PfxFile`.
* Ensured proper parameter validation and help documentation for each function.
* Established integration tests to verify the workflow of signing modules and creating catalogs.
- Introduced `-SkipValidation` parameter to bypass validation checks for certificates from `EnvVar` or `PfxFile` sources.
- Enhanced error handling for missing or invalid certificates.
- Updated localization strings for better clarity on certificate validation messages.
…Get-PSBuildCertificate`

* Implemented a check to throw an error if the 'Store' certificate source is used on non-Windows platforms.
* Added localized message for unsupported certificate source on non-Windows.
* Updated tests to handle the new error action for better verbosity control.
* Removed tests that check for the existence and help documentation of `Get-PSBuildCertificate`.
* Updated context descriptions to clarify platform-specific behavior.
* Ensured tests are appropriately skipped on non-Windows platforms.
* Updated tests to skip execution on non-Windows systems.
* Ensures that Store mode tests are only run in a compatible environment.
- Introduced new test files `Invoke-PSBuildModuleSigning.tests.ps1` and `New-PSBuildFileCatalog.tests.ps1` to cover the functionality of code signing.
- Removed the old `Signing.tests.ps1` file to streamline test organization.
- Ensured tests validate the existence, parameters, and expected behaviors of the `Invoke-PSBuildModuleSigning` and `New-PSBuildFileCatalog` functions.
- Added checks for help documentation and parameter validation, including mandatory parameters and default values.
…uild tasks

* Added verbose output for certificate resolution in `Get-PSBuildCertificate`.
* Introduced `SkipCertificateValidation` option in `build.properties.ps1` for CI environments.
* Enhanced `psakeFile.ps1` to support verbose logging across various build tasks.
* Improved readability and maintainability of the build script.
…t-PSBuildCertificate`

* Simplified test cases by removing the default Auto mode test.
* Adjusted the remaining tests for clarity and consistency.
…n `Get-PSBuildCertificate`

* Introduced a test to verify that the function defaults to Auto mode when no `CertificateSource` is specified.
* Enhanced the existing Store mode test to check for verbose output indicating the resolution to 'Store'.
@HeyItsGilbert HeyItsGilbert merged commit a8a877d into main Feb 20, 2026
5 checks passed
@HeyItsGilbert HeyItsGilbert deleted the claude/add-module-signing-XKMbq branch February 20, 2026 07:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments