Skip to content

Code Signing

Code signing is the mechanism that lets you prove who authored a PowerShell script and whether it has been modified since it was approved. In an enterprise, this is not a “nice to have”—it is the backbone of trustworthy automation. When combined with execution policies such as AllSigned or RemoteSigned, code signing turns scripts from “just text files” into controlled, auditable assets.


1. What code signing actually guarantees

A signed script carries a digital signature created with a code‑signing certificate. When PowerShell validates that signature, it answers two questions:

  • Authenticity:

    Is this script really from the person or system it claims to be from?

    This is tied to the identity embedded in the certificate (e.g., your organization, team, or service account).

  • Integrity:

    Has the script been changed since it was signed?

    If even a single character is modified, the signature becomes invalid.

PowerShell uses these guarantees to decide whether a script is allowed to run under the current execution policy. For example:

  • Under AllSigned, unsigned or tampered scripts are blocked.
  • Under RemoteSigned, downloaded scripts must be signed to run without prompts.

So code signing is not just a cryptographic trick—it is the enforcement mechanism that makes execution policies meaningful in real environments.


2. The building block: the code‑signing certificate

To sign scripts, you need a code‑signing certificate. This is an X.509 certificate with a specific usage: it is allowed to sign code.

In an enterprise, this usually comes from:

  • An internal PKI (Active Directory Certificate Services or similar), or
  • A public CA (for scripts distributed outside the organization).

The certificate must be installed in the user’s personal certificate store so PowerShell can access it:

Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert

This command lists all certificates in the CurrentUser\My store that are valid for code signing. If this returns nothing, you don’t have a usable code‑signing certificate in that profile.

Key points:

  • The certificate’s private key must be available to sign scripts.
  • The certificate’s trust chain must be valid on the machines where scripts will run.
  • Revocation and expiration are enforced during signature validation.

3. Inspecting script signatures

Before you can trust a script, you need to know whether it is signed and whether that signature is valid.

PowerShell exposes this via Get-AuthenticodeSignature:

Get-AuthenticodeSignature .\Deploy.ps1

Important fields in the output:

  • Status
    • Valid — signature is intact and trusted
    • NotSigned — no signature present
    • UnknownError, HashMismatch, etc. — something is wrong
  • SignerCertificate

    Shows who signed the script and which certificate was used.

  • StatusMessage

    Explains why a signature is invalid or untrusted (e.g., untrusted root, expired certificate).

This is how you programmatically enforce “only signed scripts from our org are allowed” in tooling, CI/CD, or pre‑deployment checks.


4. Signing a script step by step

The basic workflow to sign a script is:

  1. Obtain a code‑signing certificate
  2. Select the certificate in PowerShell
  3. Apply the signature to the script

4.1. Select a code‑signing certificate

$cert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert | Select-Object -First 1

This picks the first available code‑signing certificate in the current user’s store. In a real environment, you might filter by subject, issuer, or thumbprint to ensure you’re using the correct one.

4.2. Sign the script

Set-AuthenticodeSignature -FilePath .\Deploy.ps1 -Certificate $cert

PowerShell calculates a hash of the script, signs that hash with the certificate’s private key, and embeds the signature into the script as a comment block at the end.

From this point on:

  • Any modification to the script will invalidate the signature.
  • Get-AuthenticodeSignature will show HashMismatch if the file is altered.

This is exactly what you want: any change must go through your normal development and signing process.


5. How PowerShell validates signed scripts

When you run a signed script, PowerShell performs several checks:

  1. Is there a signature?
    • If not, and the policy requires signing (AllSigned, RemoteSigned for remote scripts), execution is blocked or prompts the user.
  2. Is the signature intact?
    • PowerShell recomputes the hash of the script and compares it with the signed hash.
    • If they differ, the script has been modified → execution is blocked.
  3. Is the certificate trusted?
    • The certificate chain must lead to a trusted root CA.
    • If the root or intermediate CA is not trusted, the signature is treated as untrusted.
  4. Is the certificate valid and not revoked?
    • Expired or revoked certificates cause validation to fail.
    • CRL/OCSP checks may be performed depending on configuration.

Only if all checks pass does PowerShell treat the script as trusted under the current execution policy.


6. Code signing in an enterprise workflow

In a serious environment, code signing is not something developers do manually once in a while—it is part of the delivery pipeline.

A typical enterprise pattern looks like this:

  1. Developers write scripts
    • Scripts live in version control (Git, Azure Repos, etc.).
    • Code reviews and approvals happen before merging.
  2. Build/CI pipeline validates and signs

    • Linting, tests, and security checks run.
    • On success, the pipeline uses a central code‑signing certificate (often stored in a secure vault or HSM) to sign the scripts:
    $cert = Get-ChildItem Cert:\LocalMachine\My -CodeSigningCert | Where-Object { $_.Subject -like '*CN=Org Code Signing*' } | Select-Object -First 1
    Set-AuthenticodeSignature -FilePath .\Out\Deploy.ps1 -Certificate $cert
    
  3. Deployment only accepts signed artifacts

    • Servers enforce AllSigned or RemoteSigned.
    • Unsigned or tampered scripts simply do not run.
  4. Audit and compliance
    • Every signed script can be traced back to a certificate and a pipeline run.
    • Certificate revocation can instantly invalidate compromised signing identities.

This turns PowerShell scripts into controlled, auditable assets, not random files copied around by hand.


7. Handling expiration and timestamping

Certificates expire. If you do nothing special, a script signed with an expired certificate will fail validation—even if the script itself has not changed.

To avoid this, enterprises use timestamp servers when signing:

Set-AuthenticodeSignature `
    -FilePath .\Deploy.ps1 `
    -Certificate $cert `
    -TimestampServer "http://timestamp.digicert.com"

What this does:

  • The timestamp server attests: “This script was signed at this date and time, when the certificate was still valid.”
  • Even after the certificate expires, the signature remains valid because it was valid at the time of signing.

This is crucial for long‑lived automation and for compliance audits that may review scripts years later.


8. How code signing ties back to execution policies

Execution policies and code signing are designed to work together:

  • AllSigned
    • Every script must be signed, including local ones.
    • Ideal for high‑security or regulated environments.
    • Forces a disciplined signing workflow.
  • RemoteSigned
    • Local scripts can be unsigned.
    • Scripts from remote sources (internet, email, network shares) must be signed.
    • Good balance between security and developer convenience.
  • Bypass (Process scope)
    • Used for automation agents or tools that must run without prompts.
    • Typically combined with internal signing and other controls.

Code signing is what makes these policies enforceable in a meaningful way. Without signing, execution policies can only distinguish “local vs remote” or “script vs no script”—they cannot distinguish trusted from untrusted code.


9. Summary

As an expert‑level PowerShell practitioner, you should treat code signing as a non‑negotiable part of enterprise automation:

  • It proves who authored a script and whether it has been altered.
  • It integrates directly with execution policies to control what can run.
  • It fits naturally into CI/CD pipelines and version control.
  • It supports long‑term auditability through timestamping and certificate management.

Once you internalize this, you stop thinking of scripts as loose files and start treating them as signed, governed artifacts—which is exactly the mindset required for enterprise‑grade PowerShell.