Hey, Scripting Guy! How Can I Sign Windows PowerShell Scripts with an Enterprise Windows PKI? (Part 2 of 2)

Bookmark and Share


Hey, Scripting Guy! Question

Hey Scripting Guy! Will you give us the final steps in the step-by-step guide about how to use an existing Windows PKI installation to sign Windows PowerShell scripts, or were you just kidding yesterday?



Hey, Scripting Guy! Answer


Hello HR,

Microsoft Scripting Guy Ed Wilson here. Just as we promised yesterday, we continue today with the final steps involved in Windows PKI to sign scripts. We go back to Ragnar Harper.


Step 3: Sign my Windows PowerShell script and run it

In this step we will be inside Windows PowerShell, and we will sign our script. For this purpose I have a simple script named demoscript.ps1.

$yourName=Read-Host “What is your name?”

Write-Host “Hello $yourName”

Just a quick reminder that your requirements for signed scripts are set using the Set-ExecutionPolicy cmdlet (or by Group Policy).




No requirements; all scripts allowed


All local scripts allowed; only signed remote scripts


All scripts need to be signed


No scripts allowed

For this demonstration, my executionpolicy is set to AllSigned. If I just try to run my script, it will fail, as shown in the following image.

Image of failed script message

We will use the cmdlet Set-AuthenticodeSignature to sign the script. I will start storing the code signing certificate in a variable named $cert.

$cert=(dir cert:currentuser\my\ -CodeSigningCert)

Then I am ready to sign my script with the Set-AuthenticodeSignature cmdlet. This is shown in the following image.

Image of signing script with Set-AuthenticodeSignature cmdlet

As you see, the status is valid, so the signing was successfully done. Please note that I recommend that you supply the TimeStampServer parameter. This will make sure the script works even though the certificate that signed it is expired. It will tell the system that the code signing certificate was valid at the time of signing. (Okay, I can imagine there are some situations where this might not be correct, but I also guess it will be good enough for most of us.) If you do not use the TimeStampServer parameter, the script will stop to work when the certificate used for signing expires. There are multiple sources for timestamping out there. Use one that suits you.

Let us try to run the scripts again, and see what happens. The results are shown in the following image.

Image of results of running script again

We get a question if we want to run the script or not. The question says that this is a script from an untrusted publisher. In Step 4, I will show you how to make the publisher (code signing certificate) trusted for your domain.

As for this computer, you can now make this publisher trusted by choosing A for Always run. If you choose V for Never run, you will explicitly make this publisher untrusted, and scripts signed by this certificate will not run.

Let’s stop and see what exactly is happening here. If you make any choice persistent (such as Always run or Never run), the code signing certificate is stored as a trusted or untrusted publisher on your computer. You can see this through the GUI if you open mmc.exe and load the Certificates snap-in, as shown in the following image.

Image of trusted and untrusted publisher nodes

Or, you could also do this from Windows PowerShell:

dir cert:\CurrentUser\TrustedPublisher

dir cert:\CurrentUser\Disallowed

As you will see in Step 4, you can also control this setting through Group Policy. For now, you can just click Run Once, and the script is allowed to execute. If you open the script, you will see that the signature is attached at the bottom.

Image of signature at bottom of script

You can also use validate the signature using the Get-Authenticode cmdlet.

Image of validating signature with Get-Authenticode cmdlet

In this step, I showed you how to sign a Windows PowerShell script, and also how to make it trusted or untrusted on your computer. In the next step, we will make the code signing certificate trusted in our domain using group policy.

Step 4: Make the code signing certificate trusted in my domain

If you were to deploy this in your domain, you would probably use Group Policy to make sure the code signing certificate in use is a trusted publisher. To do this there a two steps:

1. Export the code signing certificate.

2. Create a policy and import the code signing certificate into trusted publishers.

Export the code signing certificate

Let’s start with exporting the code signing certificate from the client computer where we requested the certificate.

Start the Certificates snap-in as shown in Step 2 yesterday. Open the Personal node, and then Certificates. In the content pane, you will now see your certificate. (The one with Intended Purpose set to Code Signing). Right-click the certificate, click All Tasks, and then click Export. You can see this in the following image.

Image of steps for exporting code signing certificate

Click Next in each of the three dialog boxes you see. Make sure that you save the certificate somewhere you can access it from the computer on which you are going to run Group Policy Management. There is no security risk making the public part of this certificate available, so you can store it wherever you want.

Image of Certificate Export Wizard

This finishes the export part from the client. Now we need to open up the Group Policy Management Console. This is a part of the Server Administration tools and is usually found if you have installed RSAT (Remote Server Administration Tools) on your client or on your domain controller. For this demonstration, I will run this from one of my domain controllers.

Create a policy and import the code signing certificate into trusted publishers

When I open the Group Policy Management Console, I start by creating a new policy. I open my domain (harper.labs), right-click it, and click Choose Create a GPO in this domain, and link it here.

Image of creating GPO

Make sure that you create this Group Policy object (GPO) where you want it in your own domain. For this demonstration, I create it at the domain level. I give the policy the name Certificates Policy, and I click OK.

Image of creating new GPO

Select the policy (Certificates Policy) in the navigation pane, right-click it, and click Edit, as shown in the following image.

Image of starting editing certificate process

Wait for the Group Policy Editor to start, and then click Computer Configuration, click Policies, click Windows Settings, and then click Public Key Policies. You are now ready to start the import. Right-click Trusted Publishers, and then click Import.

Image of starting certificate import

In the dialog box that asks you for the certificate to import, select the certificate you exported earlier. Then click Next.

Image of selecting certificate to import

As shown in the following image, make sure the certificate is placed in the Trusted Publishers store, and click Next.

Image of placing certificate in Trusted Publishers store

Now finish the wizard by clicking Finish. You have imported the certificate as a trusted publisher, which is shown in the following image.

Image of finishing Certificate Import Wizard

You can confirm this by looking inside the Trusted Publishers node in the Group Policy Editor as shown in the following image.

Image of certificate in Trusted Publishers node

So, the next time the policy is updated on computers in your domain, they will add this certificate as a trusted publisher. You can now run scripts signed by this certificate without being asked if the certificate is trusted or not. You can also do the same with untrusted certificates if you want.

I will test this from my client computer. I will first make sure that the certificate is not in my trusted publishers list. This should be done through the Certificates snap-in on my client.

Image of ensuring certificate isn't already in trusted publishers list

Then I run gpupdate /force from my Windows PowerShell window. The results are shown in the following image.

Image of results of running gpudate /force in Windows PowerShell

When the update is finished successfully, I refresh the Trusted Publishers list in my Certificates snap-in. My certificate should now be listed as trusted, as shown in the following image.

Image of certificate being trusted

I hope this helps, and have fun with Windows PowerShell!


HR, that is all there is to using Windows PKI to sign Windows PowerShell scripts. This brings Guest Blogger Week to a close. Check with us tomorrow as we delve into the virtual mail bag for Quick-Hits Friday.

If you want to know exactly what we will be looking at tomorrow, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at scripter@microsoft.com, or post them on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson and Craig Liebendorfer, Scripting Guys

Comments (16)

  1. Anonymous says:

    Boom! This article (and its preceding twin, Part 1) are dynamite. Helped immensely. I can now confidently roll out scripts without setting every client in the enterprise to unrestricted.

  2. Thomas Lee says:

    Another great article – and if I may, I have a small addition to make.

    The steps noted in the article do indeed work just fine, so long as you are editing your scripts in notepad and signing them from the console. If you use the ISE and save your scripts, the signing noted above does not work correctly (by default). By default, the ISE saves scripts in Unicode Big Endian – which Set-Authenticode does not like signing. Here is a short transcript of what I see:

    Psh[Cookham8]>Set-AuthenticodeSignature .helloworld.ps1 -cert $cert

       Directory: C:foo

    SignerCertificate                         Status                                       Path                                        

    —————–                         ——                                       —-                                        

                                             UnknownError                                 helloworld.ps1      

    If I then use notepad to save the file as ansii, the results are what you want.

    Psh[Cookham8]>Set-AuthenticodeSignature .helloworld.ps1 -cert $cert

       Directory: C:foo

    SignerCertificate                         Status                                       Path                                        

    —————–                         ——                                       —-                                        

    D42B4A6B4DBB8C697E5CA2CDD51A7F1F9325B632  Valid                                        helloworld.ps1                              

    I have written a short blog article about this as it has caught a number of folks out. See: tfl09.blogspot.com/…/signing-powershell-scripts.html


  3. tony says:

    Couldn't read the text in the PS images.

  4. Al says:

    What do yo do if you have to edit the script after it has been signed?

  5. William Knox says:

    Thanks Ed! Great post.  

    A very special thanks to Thomas Lee for supplying the final critical piece of the puzzle, it was a sanity saver to say-the-least.


  6. bntest says:

    It seems like a ridiculously complex process to me…

  7. _pisees_ says:

    Thanks this is great information. If I want to sign files uses by PowerShell, for example say I have external text files like .SQL files, how could I sign as well? Or should I put them in .ps1 file as well? Thanks,

  8. PANoone says:

    If you already have your ExecutionPolicy set to RempteSigned, this error will only occur occasionally if you download scripts from other locations.

    The simplest thing to do is just create a new script and copy the contents. This whole signing process and certificates for an uncompiled text file is just ludicrous.

  9. digital signature FAQ says:

    Excellent post. I must say that you have provided a great amount of information about digital signatures in this article. You have also posted the steps to create digital signature which helped me to successfully design my own signature. Thanks a lot.

    [url="http://www.arx.com"]digital signature FAQ[/url]

  10. thomas771 says:


    Top comment and addition to the walk-through here.

    While I appreciate learning something as important as signing scripts, the contrast in workload between signing a script to run it on a non-Unrestricted compared to just copy+pasting it into a new .ps1 file seems oddly diverging.

    From a naive, practical point of view I’d wonder why I would want to go through the extra hassle if I could just "new file->copy+paste" any script on the machine in question, heck, I could even write a script that would do that for me if I have a constant coming
    and going of different PS scripts that need to be executed.

  11. Travis Hubbard says:

    I had some trouble with the Trusted Publishers GPO and wanted to post my correction to the steps mentioned above. In addition to adding the Trusted Publisher Certificate to the GPO you need to do this:

    In the console tree under Computer ConfigurationWindows SettingsSecurity Settings, click Public Key Policies.
    Double-click Certificate Path Validation Settings, and then click the Trusted Publishers tab.
    Select the Define these policy settings check box, select the policy settings that you want to apply, and then click OK to apply the new settings.

    Reference: https://technet.microsoft.com/en-us/library/cc733026.aspx

  12. Ross says:

    Awesome article. Very helpful.

  13. Duncan says:

    I can get this to work as described by trusting the direct code signing certificate. But in most cases, clients will trust the enterprise CA. If I import the enterprise CA cert into Trusted Publishers I still get a prompt from PowerShell. How can I get
    PowerShell to use a proper PKI chain of trust? Otherwise when I leave the company and they disable my account, my scripts will fail!

  14. Dunc the Punk says:

    / Opps, should have signed in to be notified /


  15. BetaBoy says:

    Nice article but your screen captures are too small and hard to read the words…