Skip to main content

    PowerShell "Script Is Not Digitally Signed": Causes and Fixes

    Why PowerShell says a script is not digitally signed (a download mark, AllSigned or Group Policy), how to tell which, and when signing is the real fix.

    MS
    My-SSL
    ·
    9 min read
    ·
    Published October 26, 2026
    ·
    Last updated October 26, 2026

    The short answer

    PowerShell says a script "is not digitally signed" when the execution policy demands a signature the file doesn't have. Under RemoteSigned that happens only to files Windows marked as downloaded; under AllSigned it happens to every unsigned script; and Group Policy can impose either one. Run Get-ExecutionPolicy -List to see which applies, then unblock a script you have read and trust with Unblock-File instead of switching the whole machine to Unrestricted.

    What PowerShell does with an unsigned script under each execution policyRestricted: running scripts is disabled. RemoteSigned and a local script: runs. RemoteSigned and a downloaded script: is not digitally signed, in gold. AllSigned: is not digitally signed, in gold. Unrestricted and a downloaded script: runs after a prompt. Group Policy can impose RemoteSigned or AllSigned.An unsigned script meets the policyRestricted · Any script“Running scripts is disabled”RemoteSigned · Written on this PCRunsRemoteSigned · Marked as downloaded“Is not digitally signed”AllSigned · Any script“Is not digitally signed”Unrestricted · Marked as downloadedRuns after a promptGroup Policy can impose RemoteSignedor AllSigned above every local setting
    Only the two gold rows produce this error, so the fix depends on which one you are in: the download mark can be removed, but AllSigned can only be satisfied with a signature.

    What does "is not digitally signed" mean?

    The "is not digitally signed" error means PowerShell's execution policy required an Authenticode signature on this script and found none. It is not a syntax or permission problem, and nothing is wrong with the script's code. Microsoft's documentation shows the message like this:

    File .\Start-ActivityTracker.ps1 cannot be loaded.
    The file .\Start-ActivityTracker.ps1 is not digitally signed.
    The script will not execute on the system.
        + CategoryInfo          : NotSpecified: (:) [], PSSecurityException
        + FullyQualifiedErrorId : UnauthorizedAccess

    Many systems show "You cannot run this script on the current system" in place of the last sentence. The meaning is the same. Two execution policies can produce it, according to Microsoft's policy definitions:

    • RemoteSigned requires a signature from a trusted publisher on scripts "downloaded from the internet which includes email and instant messaging programs", and none on scripts written on the local computer.
    • AllSigned requires a trusted signature on every script and configuration file, including the ones you wrote yourself.

    A third cause sits above both: an administrator can set either policy through Group Policy, and that setting outranks anything you change locally. If you run scripts other people wrote, the fixes below take a minute or two. If you publish scripts that other people run, the lasting fix is on your side: a signature from a code signing certificate their computers already trust, so the error never reaches them.

    How do you find which cause applies?

    Three read-only commands identify the cause of a PowerShell signing error: the policy list, the file's download mark and the file's signature. None of them changes anything, so run all three before you fix anything.

    Get-ExecutionPolicy -List
    Get-Content .\script.ps1 -Stream Zone.Identifier
    Get-AuthenticodeSignature .\script.ps1

    Get-ExecutionPolicy -List shows the five scopes. Microsoft's order of precedence puts the two Group Policy scopes, MachinePolicy and UserPolicy, above everything; without them, Process beats CurrentUser, which beats LocalMachine. The first scope that isn't Undefined is the policy in force. If every scope is Undefined, the effective policy is Restricted on Windows clients; for Windows Server, about_Execution_Policies says RemoteSigned while the Set-ExecutionPolicy page says Restricted, so trust what the command reports on your machine.

    The second command reads the alternate data stream that browsers and mail programs attach to downloaded files. If it prints a [ZoneTransfer] section with ZoneId=3 (the Internet zone), RemoteSigned treats the file as remote. If it fails because the stream doesn't exist, the file is not marked, and under RemoteSigned the error must come from somewhere else. The third command tells you whether the file carries a signature at all and, if it does, whether it is valid.

    What you seeWhat it meansCheck withFix
    “…is not digitally signed”, effective policy RemoteSignedThe file carries the downloaded mark (Zone.Identifier stream)Get-Content .\script.ps1 -Stream Zone.IdentifierRead the script, then Unblock-File
    “…is not digitally signed”, effective policy AllSignedEvery script needs a trusted signature, local ones includedGet-ExecutionPolicy -ListSign it; unblocking does not help
    Set-ExecutionPolicy says the setting “is overridden by the Group Policy”MachinePolicy or UserPolicy is set and outranks every local scopeGet-ExecutionPolicy -ListWork within that policy, or ask whoever owns the GPO
    “…cannot be loaded because running scripts is disabled on this system”Restricted: no scripts run at all, signed or notGet-ExecutionPolicySet-ExecutionPolicy RemoteSigned -Scope CurrentUser
    “The contents of file … might have been changed”The script changed after it was signedGet-AuthenticodeSignature .\script.ps1Re-sign the file you ship; fix what rewrites it
    “Do you want to run software from this untrusted publisher?”Signature verified; publisher not yet in Trusted PublishersGet-AuthenticodeSignature .\script.ps1Run once / Always run, or deploy the publisher certificate

    Microsoft also notes that PowerShell 6 on Windows Server Core or Nano Server can fail with "AuthorizationManager check failed" instead, because the zone check relies on the Windows Desktop Shell, which those editions lack.

    How do you fix a downloaded script you trust?

    A downloaded script that you have read and trust is fixed with Unblock-File, which removes the download mark and leaves the execution policy alone. Microsoft's own example runs into this error under RemoteSigned, unblocks the file and runs it, and adds that the best practice is to read the script's code and verify it's safe before unblocking it.

    Unblock-File -Path .\script.ps1
    .\script.ps1

    Eric Lawrence's reference on the Mark of the Web documents that Windows Explorer carries the mark from a downloaded .zip over to the files it extracts, while 7-Zip made that behaviour optional in version 22.0. So the same module can fail on one machine and run on another depending on the extraction tool.

    Some downloads are never marked. Microsoft notes that curl.exe, Invoke-WebRequest and Invoke-RestMethod may not mark a file as coming from the internet, so a script fetched that way runs under RemoteSigned with no signature check at all. That is a reason to read what you download, not a workaround to rely on.

    Should you change the execution policy instead?

    Changing the execution policy is the right fix only for the Restricted case, and the narrowest change that works is the best one. For a downloaded script, a looser policy hides the warning without making the script any safer. Microsoft states that the execution policy "isn't a security boundary, it's defense in depth": it exists to stop people running scripts by accident.

    • Your own account, if scripts are disabled: Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser. This lets your local scripts run. It does not stop the "not digitally signed" error on a downloaded file, because blocking that file is exactly what RemoteSigned is for.
    • One run only: pwsh.exe -ExecutionPolicy Bypass -File .\script.ps1, or powershell.exe with the same parameter in Windows PowerShell. The policy applies to that session and its child sessions and is kept in $Env:PSExecutionPolicyPreference, which disappears when the session closes. It affects that process, not your user account.
    • Unrestricted: rarely what people expect. Microsoft says it still warns before running scripts that are not from the local intranet zone, so a downloaded unsigned script prompts instead of failing. Bypass is the policy where "nothing is blocked and there are no warnings or prompts", and it is meant for applications with their own security model.

    If Set-ExecutionPolicy answers that it "updated your local preference successfully, but the setting is overridden by the Group Policy applied to your system", your change was saved and ignored. Group Policy outranks every local scope, so you either work within the policy it sets or ask whoever manages it. Under an AllSigned Group Policy, that usually means the script needs a signature.

    Version also matters on the same machine. PowerShell 7 stores the CurrentUser and LocalMachine policies in powershell.config.json files, while Windows PowerShell keeps them in the registry. A policy you set in one is not the policy the other reads, which can explain why a change seems to have no effect.

    When is signing the script the right fix?

    Signing is the right fix for a PowerShell script when AllSigned is enforced, or when you hand the script to people whose machines run RemoteSigned and receive it by download or email. In both cases you can't, or shouldn't, ask every user to unblock files or relax a policy. A valid signature lets the policy do its job and the script still runs.

    Which certificate depends on who runs the script. Microsoft's about_Signing page says the certificate must come from a certification authority the computer trusts. Inside one organization, a code signing certificate from your internal CA works on every machine that trusts that CA. For scripts that leave the organization, use a certificate from a publicly trusted CA. The Certum Standard and EV certificates on our code signing certificate page are delivered through Certum's SimplySign cloud, and Certum's manual documents SignTool and jarsigner for them rather than Set-AuthenticodeSignature, so test your PowerShell signing route before a release depends on it.

    A trusted root alone does not stop the prompt. Jordan Borean's AllSigned tests show that a certificate chaining to a trusted root still produces the untrusted-publisher prompt until the signing certificate is in the Trusted Publishers store. Answering "Always run" adds it there, and IT teams deploy it in advance. Add a timestamp when you sign: Microsoft notes that most signing certificates are valid for one year, and a timestamp keeps the signature usable after that. The commands are in our guide to signing a PowerShell script with Set-AuthenticodeSignature.

    Why does a signed script still fail?

    A signed PowerShell script still fails when the file changed after signing, when the computer doesn't trust the signer, or, before PowerShell 7.2, when the file uses an encoding the signature check doesn't support. The first three cases have their own messages, so "not digitally signed" usually means PowerShell found no signature it could use; an unsupported encoding before 7.2 is the exception to check.

    • Changed after signing: "The contents of file … might have been changed by an unauthorized user or process because the hash of the file does not match the hash stored in the digital signature." Microsoft documents one surprising trigger on Windows 10, Windows 11 and Windows Server: characters such as ö, ä or ü in an ASCII or UTF-8 script signed on an en-US computer and run on a cs-CZ one. Its fixes are to remove those characters or save the script as UTF-16 LE with a byte-order mark.
    • Root not trusted: "A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider." This is typical of self-signed and internal-CA certificates on machines that don't have the root.
    • Publisher not trusted yet: the prompt "Do you want to run software from this untrusted publisher?" with Never run, Do not run, Run once and Always run. Microsoft notes that Never run and Always run stop PowerShell asking again for that publisher.
    • Encoding: Microsoft's about_Signing says that before PowerShell 7.2 a signed script had to be saved as ASCII or UTF-8 without a byte-order mark; 7.2 and later accept any encoding.

    Why does Git break a PowerShell script signature?

    In our test, every change we tried broke a signed .ps1 file's signature, including ones nobody would see in an editor: a trailing space and a switch from CRLF to LF line endings. We signed a two-line script on Ubuntu 24.04 with osslsigncode 2.8 (OpenSSL 3.0.13) and a lab certificate on October 26, 2026, then changed the file in different ways and checked it again with osslsigncode verify. The signature block was appended as comment lines between # SIG # Begin signature block and # SIG # End signature block, which matches what Microsoft describes for Set-AuthenticodeSignature.

    What osslsigncode verify returned after each change to a signed scriptUntouched: ok, in gold. One character edited: mismatch. Trailing space added: mismatch. CRLF to LF through Git: mismatch. Same repository with ps1 files marked as not text: ok, in gold.A signed .ps1 after each changeSigned script, untouchedVerification okOne character editedDigest mismatchOne trailing space addedDigest mismatchLine endings CRLF → LF via GitDigest mismatchSame repo with *.ps1 -textVerification ok
    Two of the three failing changes are invisible in an editor, yet each one changed the script the signature was made over.

    The Git result matters most for teams. We saved and signed the script with CRLF line endings, as Windows editors do, and committed it with core.autocrlf=true. Git stored the file with LF endings. A clone with core.autocrlf=false or input, for example on a build agent that never set it, received an LF file whose digest no longer matched; only a clone with core.autocrlf=true got the original bytes back. Adding *.ps1 -text (or *.ps1 text eol=crlf) to .gitattributes before committing kept the signature valid in every clone.

    # .gitattributes
    *.ps1 -text

    The steadier habit is to sign in the release step, after checkout and after anything else that touches the files, and to ship exactly the bytes you signed. We ran these checks with osslsigncode on Linux, not with PowerShell on Windows. Microsoft's locale case suggests that Windows reads the script as text, so a change that keeps the same characters may behave differently there; the Windows messages quoted above come from Microsoft's documentation.

    Frequently Asked Questions

    Quick answers to related questions.

    Does PowerShell on Linux or macOS show this error?

    No. Microsoft's documentation says that on non-Windows computers the execution policy is Unrestricted and can't be changed, and that enforcement of execution policies only happens on Windows. Set-ExecutionPolicy exists there but reports that it isn't supported, and Get-AuthenticodeSignature is available only on Windows.

    Can I sign my scripts with a self-signed certificate instead?

    On machines you control, yes. Microsoft's about_Signing page says a self-signed certificate must be installed in the Trusted Root store of each computer that runs the script, and Jordan Borean's AllSigned tests show it must also be in Trusted Publishers for the script to run without a prompt. Machines outside your control will not trust it.

    Does a signed script stop working when the certificate expires?

    Not if it was timestamped. Microsoft states that a script's signature is valid until the signing certificate expires, or for as long as a timestamp server can verify that the script was signed while the certificate was valid. Sign with a timestamp server so a one-year certificate does not limit how long the script runs.

    Why does the same script run on one computer and fail on another?

    The two computers differ in at least one of three things: the effective execution policy (check Get-ExecutionPolicy -List, including Group Policy scopes), whether that copy of the file carries the downloaded mark, and for signed scripts whether the computer trusts the signer's root and publisher. A copy made with curl.exe or Invoke-WebRequest may carry no mark, while the same file saved from a browser does.

    Still Have Questions?

    Contact our support team with questions about certificates, installation, or technical issues.

    If your users keep meeting this error on scripts you publish, a trusted signature is the fix that travels with the file. Compare Standard and EV on our code signing certificate page, and see the guide to what code signing certificates cover if you are choosing one for the first time.