How to Sign PowerShell Scripts
Follow a practical PowerShell signing process for a .ps1 script: prepare the final script, find a code-signing certificate, sign it with Set-AuthenticodeSignature, add a trusted timestamp, verify the result, and test execution under the target PowerShell policy.
Core workflow: Write → test → finalize → find certificate → sign → timestamp → verify → execute → distribute. If the script changes after signing, sign the changed file again.
What You Need to Sign a PowerShell Script
Start with the actual signing inputs. You do not sign a PowerShell script by installing a separate “script signing” application. PowerShell provides the signing and verification cmdlets, while the certificate provider supplies the certificate and its private key.
.ps1 scriptThe script must be final before signing. Editing the file after signing can make the signature invalid. Finish development, testing, formatting, packaging, and other content changes first.
Check That the Certificate Can Sign Scripts
Before working with the .ps1 file, confirm that the certificate you plan to use is the intended signing identity and that PowerShell can access its private key.
Importing a certificate into Windows is not enough if the matching private key is missing or inaccessible. The signing operation needs the private-key capability associated with the certificate.
Find the Code-Signing Certificate in PowerShell
PowerShell exposes certificate stores through the Cert: provider. The two stores most commonly relevant to an interactive user's certificate lookup are the current user's personal store and the local machine personal store.
Get-ChildItem Cert:\CurrentUser\My
Get-ChildItem Cert:\LocalMachine\My
Start by looking at the certificate subject and other identifying properties. Do not assume the first certificate returned by the store is the correct production identity.
Confirm the Certificate Has a Private Key
Use the certificate object returned by the provider and check its private-key availability. This catches one of the most common setup failures before you attempt to sign the script.
Get-ChildItem Cert:\CurrentUser\My |
Where-Object {$_.HasPrivateKey} |
Select-Object Subject, Thumbprint, NotAfter, HasPrivateKeyIf the intended certificate is absent from this result, investigate the certificate import, key association, certificate store, account permissions, or hardware/remote provider configuration before continuing.
Important: the certificate is the public identity. The private key protection approach determines how the signing operation can be performed without exposing key material.
Select the Exact Signing Certificate
When multiple certificates are present, select the intended certificate explicitly. You can inspect the store and assign the chosen certificate to a variable for the signing operation.
$cert = Get-ChildItem Cert:\CurrentUser\My\CERTIFICATE_THUMBPRINT
Cert:\CurrentUser\MyPersonal certificate store for the current user.CERTIFICATE_THUMBPRINTReplace with the exact certificate thumbprint.Use an explicit identity in production automation. This prevents a build or signing machine with several certificates from selecting an unintended signer.
Sign the PowerShell Script
Set-AuthenticodeSignature is the PowerShell cmdlet used to add an Authenticode signature to a PowerShell script. Give it the script path and the certificate object selected from the certificate provider.
Set-AuthenticodeSignature ` -FilePath "C:\Release\Deploy-Application.ps1" ` -Certificate $cert
-FilePathIdentifies the script file to sign.-CertificateSupplies the certificate object used for signing.The cmdlet returns signature information. Inspect that result instead of assuming the command succeeded simply because no obvious error appeared.
Add a Trusted Timestamp
For a production signing workflow that requires timestamp evidence, provide a timestamp server through -TimestampServer during the signing operation.
Set-AuthenticodeSignature ` -FilePath "C:\Release\Deploy-Application.ps1" ` -Certificate $cert ` -TimestampServer "TIMESTAMP_URL"
Replace TIMESTAMP_URL with the timestamp service required by your signing setup. If the timestamp service is unavailable and timestamping is mandatory for your release, treat that as a signing failure rather than silently publishing without the required evidence.
Verify the Signed Script
Use Get-AuthenticodeSignature to inspect the signature status on the exact script you intend to distribute.
Get-AuthenticodeSignature "C:\Release\Deploy-Application.ps1" | Format-List *
| Status | Meaning for the workflow |
|---|---|
| Valid | The signature is valid under the verification conditions used. |
| NotSigned | No usable Authenticode signature is present on the script. |
| HashMismatch | The signed content no longer matches the content being verified. |
| NotTrusted | The signature or signer does not satisfy the trust evaluation on that machine. |
Also inspect StatusMessage and SignerCertificate. A valid status is the starting point for release acceptance, not a substitute for checking the certificate chain and confirming that the certificate identifies the expected publisher.
Check the Signer Certificate and Timestamp
The verification object exposes the signer certificate so you can inspect the identity associated with the script signature. Check the publisher identity, certificate status, chain, and timestamp information where available.
$sig = Get-AuthenticodeSignature "C:\Release\Deploy-Application.ps1" $sig.SignerCertificate | Format-List Subject, Issuer, NotBefore, NotAfter, Thumbprint
Identity check
Confirm the subject and certificate are the production signing identity you intended to use.
Time evidence
Confirm the signature carries the timestamp information required by your signing policy.
If the certificate is correct but the target machine does not trust the issuer, the script can have a valid cryptographic signature while still failing the trust requirement of the environment.
Test the Script Under the Intended Execution Policy
Do not stop after cryptographic verification. PowerShell's execution policy affects how scripts are allowed to run, so test the signed script in the environment where it will actually be used.
Get-ExecutionPolicy
| Policy | Signing relevance |
|---|---|
| Restricted | Script execution is restricted; signing alone does not change the policy. |
| AllSigned | Scripts generally need valid signatures to run. |
| RemoteSigned | Downloaded or remote-origin scripts have additional signature requirements. |
| Unrestricted | Less restrictive execution behavior, but signatures can still provide publisher and integrity information. |
| Bypass | Execution policy does not block execution based on the policy setting. |
| Undefined | No policy is set at that particular scope; effective behavior can come from another scope. |
Important: execution policy controls PowerShell script-execution behavior. It is not a complete security boundary and should not be treated as one.
Understand What Happens If the Script Says “Not Digitally Signed”
When PowerShell reports that a file is not digitally signed, work through the signing state first and the execution policy second. Do not assume that the certificate itself is the problem.
Possible causes include an unsigned script, a signature invalidated by editing, an untrusted certificate chain, an execution policy requiring a signature, or a signing identity that is not usable in the current environment.
Distinguish Valid Signature From Trusted Publisher
These are related but different checks. A script can carry a cryptographically valid signature while the certificate is not trusted by the target machine or does not identify the publisher you expected.
Valid signature
The signed script content matches the signature and the signature can be verified.
Trusted publisher
The certificate and its chain are trusted according to the target machine's trust configuration and policy.
Keep these checks separate when troubleshooting. This makes it much easier to identify whether the problem is the file signature, certificate identity, trust chain, or PowerShell environment.
Do Not Modify a Signed PowerShell Script
PowerShell scripts are text files, which makes accidental post-signing edits particularly easy. Adding or removing a line, changing a variable, editing comments, changing whitespace, or simply saving a modified version can change the content covered by the signature.
The correct sequence is modify → save → test → sign again. Treat the signed script as a release artifact, not as a working source file.
Sign Multiple PowerShell Scripts
For a release directory containing several scripts, enumerate the files and sign only the scripts that belong to the release. Keep test, development, and generated temporary files outside the signing set when they are not intended for distribution.
$cert = Get-ChildItem Cert:\CurrentUser\My\CERTIFICATE_THUMBPRINT
Get-ChildItem "C:\Release\Scripts" -Filter *.ps1 -File |
ForEach-Object {
Set-AuthenticodeSignature `
-FilePath $_.FullName `
-Certificate $cert
}Get-ChildItem "C:\Release\Scripts" -Filter *.ps1 -File -Recurse |
ForEach-Object {
Set-AuthenticodeSignature `
-FilePath $_.FullName `
-Certificate $cert
}For production automation, capture the returned status for every file and fail the process if a required script does not receive the expected signature.
Sign PowerShell Scripts in CI/CD
Automated signing belongs after the script has been generated, tested, and finalized. The pipeline should never modify a signed script as part of a later packaging step.
Use an HSM or Cloud Signing Service
If the private key should not be exported to a developer workstation or build agent, move the private-key operation into a protected HSM-backed signing setup or an approved remote signing service. The PowerShell script remains the artifact being signed.
HSM-backed signing
The private key remains inside protected hardware. The signing environment requests a cryptographic operation through the configured provider.
- Private-key non-exportability
- Access controls
- Authentication
- Audit records
Cloud / remote signing
The build infrastructure sends an authenticated signing request to a service that controls the private key.
- Authentication
- Authorization
- Signing policy
- Audit logs
The exact integration depends on the provider. Do not assume that a remote signing service uses the same certificate-store workflow as a local certificate.
Common PowerShell Signing Problems
| Problem | First thing to check |
|---|---|
| Certificate not found | Check the certificate store and the path or thumbprint used to select the certificate. |
| Certificate has no private key | Confirm the correct certificate/key pair was imported or that the configured provider exposes the private key. |
Set-AuthenticodeSignature fails | Check certificate selection, private-key access, provider configuration, permissions, and the target file. |
Status is NotSigned | Confirm that the signing command completed and that you are checking the same script file. |
Status is HashMismatch | Look for edits, line changes, post-build processing, or replacement of the script after signing. |
Status is NotTrusted | Inspect the certificate chain and trust configuration on the target machine. |
| Script will not run | Check execution policy, signature status, certificate trust, file origin, and security controls. |
| Timestamp missing | Check the timestamp server setting and whether the timestamp operation completed. |
| Works on one machine only | Compare certificate trust, stores, execution policy, Group Policy, PowerShell version, and security controls. |
| Signature breaks after deployment | Compare the signed source artifact with the deployed script and identify any modifying deployment step. |
Choose a Certificate for PowerShell Script Signing
For scripts distributed across teams or customer environments, choose a signing certificate that fits the publisher identity and trust requirements. Explore software distribution certificates
Why Is My PowerShell Signature HashMismatch?
HashMismatch means the content being checked no longer matches what was signed. For scripts, the most useful investigation is to identify what changed between signing and verification.
Check source control exports, post-build substitutions, line-ending transformations, packaging systems, configuration replacement, deployment scripts, and manual edits. Once the final content is established, sign the resulting file again.
Why Does a Signed Script Still Not Run?
A valid signature and successful script execution are not the same test. Separate the cryptographic result from PowerShell's execution rules and the security configuration of the target environment.
Also compare PowerShell versions and machine configuration when a script behaves differently across systems. A signature proves something about the signed content and signer; it does not override every execution or application-control rule.
Why Does a Signed Script Work on One Computer but Not Another?
Use an environment comparison instead of repeatedly re-signing the script. The same signed file can encounter different trust and execution settings on different machines.
| Check | Computer A | Computer B |
|---|---|---|
| Signature status | ||
| Signer certificate | ||
| Root trust | ||
| Intermediate certificates | ||
| Trusted Publisher configuration | ||
| Execution Policy | ||
| Group Policy | ||
| PowerShell version | ||
| Application Control / security controls |
Complete PowerShell Signing Example
Use Deploy-Application.ps1 as the final release script. The recipe below keeps all script changes before signing and verifies the distributed copy at the end.
$cert = Get-ChildItem Cert:\CurrentUser\My\CERTIFICATE_THUMBPRINT
Set-AuthenticodeSignature ` -FilePath "C:\Release\Deploy-Application.ps1" ` -Certificate $cert ` -TimestampServer "TIMESTAMP_URL"
$sig = Get-AuthenticodeSignature "C:\Release\Deploy-Application.ps1" $sig | Format-List Status, StatusMessage, SignerCertificate
Release rule: the script that users or administrators receive should be the exact script that passed final signature verification. If deployment modifies it, the deployed file needs to be treated as a different artifact.
PowerShell Script Signing Checklist
.ps1 completedSet-AuthenticodeSignature completedRelated Signing Workflows
PowerShell script signing has its own signing and execution model. Use these related workflows for the other Windows artifacts in the release chain.
Protect Signing Keys in Automated Workflows
Keep signing authority separate from routine development and build access by selecting a key protection model suited to your release process. Explore enterprise code signing
Frequently Asked Questions
What command signs a PowerShell script?
Use Set-AuthenticodeSignature with the script path and a certificate object that has access to the matching private key.
How do I find my code-signing certificate in PowerShell?
Use the certificate provider, such as Get-ChildItem Cert:\CurrentUser\My, then inspect the returned certificates and select the intended signing identity.
How do I check whether the certificate has a private key?
Certificate objects expose the HasPrivateKey property. Check it before attempting to sign.
How do I verify a signed PowerShell script?
Use Get-AuthenticodeSignature and inspect Status, StatusMessage, SignerCertificate, and timestamp information where available.
What does HashMismatch mean?
It means the current script content no longer matches the content covered by the signature. Check for edits, rebuilding, post-build processing, or deployment changes.
Does a valid signature guarantee that PowerShell will execute the script?
No. Execution policy, certificate trust, file origin, Group Policy, application-control systems, and other environment settings can affect execution separately from signature validity.
Should I sign scripts before or after testing?
Test the final script first. Once the script is stable and no further content changes are expected, sign it, timestamp it where required, verify it, and distribute that exact file.
Prepare Your Code Signing Setup for Release
For production signing, choose a certificate that fits your software distribution needs, protect the private key, timestamp where required, and verify the exact script that will be released. View code signing certificates
