Working with Certificates in PowerShell
When working with customers, it’s often necessary to look at the certificates in place on their servers and clients. Typically, we have done the following to access these certificates:
- On the designated machine, open an MMC (usually by selecting the Run window and typing MMC)
- Within the MMC, select File\Add/Remove Snap-ins…
- Choose Certificates from the available snap-ins window and select ‘Add’ and ‘OK’
- Select which certificates to work with (either user, service account or computer) and choose ‘Finish’
Doing this, opens a GUI interface for the certificates store on the local computer as shown below.
From within this interface, administrators can work with the certificates related to the selected certificates store.
While this process works fine, it is sometimes helpful to have a method to work with certificates without having to interact with the MMC. The good news is that PowerShell provides a way to work with certificates without having to resort to the MMC. In my experience, working with certificates via PowerShell can be much more efficient and can provide more accessible data than you can get when working in the GUI. In this blog, I’ll be focusing on a few common things that PowerShell enables administrators to do when working with certificates. But first, we need to cover some basics.
Importing the Certificates Module
In PowerShell, capabilities are provided through modules. A module is a set of PowerShell cmdlets, functions, etc. that allow the admin to work with a particular technology such as Active Directory or SQL Server. A core set of these modules is added automatically when PowerShell starts up, but if the desire is to work with some other technology, its module will need to be added. If you attempt to use a cmdlet associated with a module which is not loaded, PowerShell may add it for you (assuming the module is located in the path that PowerShell looks for it). For the Certificates module, we will need to add it since it is not present by default.
There are two ways to add the Certificates module to your PowerShell session (three if you count profiles, but this goes beyond the scope of the present article). These are:
- Add the module explicitly
- Invoke a command contained in the Certificates module (like set-location cert: which is explained below)
For our purposes we will explicitly add the module. We do this with the following cmdlet:
IMPORT-MODULE <MODULE NAME>
The problem is, what is the module name we should use? If you try the word Certificate or Certificates you’ll get an error. So how do we find the module name we should use? The simplest way is to use the following command:
This will return every command PowerShell is aware of that includes CERT somewhere in the name. When we run this, we get the following results:
Notice that there are commands like Export-Certificate and the name of the module they belong to is PKI. This tells us which module we need to import. We do this by simply typing
This ensures that the Certificates module is imported and we’re ready to work with it.
To start working with certificates in PowerShell, it’s important to have an understanding of what a provider is. Essentially this is how PowerShell is able to access a data store. This data store may be the Windows file system, the local registry on a computer, or things like Active Directory and a SQL Server database. Each type of data store has its own provider, including Certificates. Each provider enables PowerShell to access a particular type of data store.
To see which providers are available, open PowerShell (this can be done with either the Console or the ISE) and type the following:
This command will return a complete list of all providers available on the local computer, as shown below:
Note that the last provider listed is the Certificate provider. So what does this tell us? First, let’s understand what is meant by the Capabilities and Drives columns:
- Capabilities – these indicate the ways that you can use each provider. For the Certificate provider, the ‘ShouldProcess’ capability means that the –WhatIf and –Confirm parameters can be used. If you are unfamiliar with these parameters, they do the following:
- -WhatIf: causes PowerShell to evaluate the command entered and will tell what PowerShell would do if it were run without this parameter. Think of it as you asking PowerShell “what would happen if I ran this command?”. If you have added the –WhatIf parameter, PowerShell won’t actually run the command. It will only tell you what would happen if you chose to run it.
- -Confirm: adds an extra layer of protection when running a command. It essentially stops the command and asks “are you sure you want to run this?”. This may be a pop-up window (in the ISE) or simply a line of text (in the console). To continue with the command, it is necessary to confirm that PowerShell should run it. Alternatively, the command can be canceled or suspended as needed.
- Drives – Providers “provide” access to different data stores, and Drives are the way the access is granted. This is easy to see by looking at the command prompt in the screenshot above. The prompt is set to PS C:\> . While it might not be immediately obvious, this is the Drive (or PSDrive, to use PowerShell terminology) granting access to the Windows file system, in this case the C: drive. By looking above under the FileSystem entry, the Drives are listed as (C, A, D). This indicates that for PowerShell to interact with the Windows file system, it needs to do so using one of these drives. Similarly, if PowerShell wants to interact with the local Certificates store, it will need to do so through the Cert drive.
To see a list of the Drives available on the local machine, type the following:
The resulting list is what appears in my test lab, though the list of Providers and Drives may differ depending on what’s been installed on each machine.
What can be seen from this screen is the following:
- Name: This is the PSDrive which can be used to access the data store made available by the Provider
- User (GB): Where appropriate, this indicates the amount of space currently used by the data store
- Free (GB): This indicates the amount of free space remaining in the data store (The C drive on my test machine has 86.31 GB free, for example)
- Provider: This is the name of the PSProvider which grants access to each data store
- Root: Indicates which portion of the data store will be connected to when using the PSDrive.
To change to a particular drive, all that’s needed is to set PowerShell’s location to the desired PSDrive, type the following within PowerShell:
Alternately admins can use CD instead of SET-LOCATION. CD is an alias of the SET-LOCATION cmdlet to make transitioning from the command prompt to PowerShell as easy as possible, but the actual PowerShell cmdlet is SET-LOCATION which we will use for the rest of this blog.
To connect to the certificate store on the local computer, an administrator would type the following in PowerShell:
NOTE: Putting the colon at the end of CERT is not optional. Leaving it off will result in an error.
The following screenshot illustrates both the incorrect and the correct way to connect to the PSDrive for the Certificates store on my lab machine:
Note that the first time I ran the command, I left off the colon and received the error displayed. The second time, I added the colon and was redirected to the Cert:\> PSDrive.
At this point, we have connected to the root of the Certificates store on the local computer.
Common Administrative Tasks
At this point, we have connected successfully to the Certs PSDrive made available by the Certificates PSProvider. But what can we do now that we’re here? Many things, it turns out. Some things we can do go beyond the scope of this blog, but it turns out many basic tasks administrators do every day are easily done with almost no scripting required.
Below, I have outlined several tasks that administrators routinely do when working with certificates in their environment. While this is by no means a complete list, it illustrates various things that we can easily do with PowerShell rather than resorting to the MMC each time.
The tasks we will look at are:
- Specifying which context (user, service account, or computer) within the Certificates store that we want to work with
- Navigating the stores within the Certificates store
- Listing the certificates within the Certificates store
- Looking for certificates with specified expiration dates
- Importing and Exporting certificates
In the remainder of this article, I’ll go through each of the tasks below and show how PowerShell enables us to do each of the tasks listed.
Specifying the Certificate Store Context
What I mean when I say “Certificate Store Context” is the question of whether I want to look at the certificates for my user account, a service account or a computer account. Typically when working with certificates we are concerned with the computer certificates so this will be our focus. But the same principles apply to the other contexts as well.
We’ve already seen that by typing SET-LOCATION CERT: in PowerShell will access the root of the Certificates data store on the local machine. But there are several ways to specify the specific context we’re interested in. Below I’ve listed two ways to set PowerShell to the LocalMachine context.
First, try the following:
The screenshot below shows the results of this in my test lab:
Note that I could also have entered both of these commands on the same line by typing
SET-LOCATION CERT: ; DIR
As we can see, my test lab has two contexts to work with. We are interested in LocalMachine so I can simply change to that context just as I can with the Windows file system by entering the following in the PowerShell interface:
Doing this sets PowerShell to the LocalMachine context as shown below:
I can also get to the LocalMachine context by typing the following
This attaches PowerShell to the Certificate Provider and focuses me immediately on the LocalMachine context as shown below:
From here, if I want to change to the other context, it’s as easy as it was in the command prompt. Just type CD.. which will take us up to the Cert: root again. Then it’s a simple matter of typing SET-LOCATION and selecting the context of interest. NOTE: Already we see that PowerShell is much quicker when shifting between contexts than the GUI would be. With the GUI we’d have to go into Add/Remove Snap-ins in the MMC and add the new context we care about. Here’s it’s just a few keystrokes.
Navigating the Certificate Stores
When we look in the Certificates snap-in, we can see many certificate stores within our selected context as shown below.
It turns out that getting to these stores in PowerShell is as simple as it was to get to the context itself. First, to see the list of stores, you can type the following:
Note that the alias for this cmdlet is DIR to enable people used to the command prompt to use this older command as they ease into using PowerShell. For our purposes, though, we’ll use the PowerShell command going forward. When we do this within the LocalMachine context, we get the following list:
As we can see, the names of the stores differ a bit from the GUI and the PowerShell console, but they are the same stores and with a bit of scrutiny they can be lined up if needed. But then, if we’re working within PowerShell we should not need to worry about how the Certificates snap-in names these stores.
If we need that information for some reason, however, we can get it by comparing the thumbprint of the certificate as it’s listed in PowerShell against the certificates in the snap-in. This brings us to our next task.
Listing the Certificates Within the Certificates Store
There are at least two ways to list the certificates within a certificate store. Depending on what our goal is, we may want to choose one method over the other. In the first method we learn how to enumerate the certificates within a specific store (For instance, the “My” store). Later we’ll learn how to enumerate all certificates from every store in our chosen context.
To get the certificates in a specific store, the following steps are needed:
- Navigate in PowerShell to the store of interest
- Type the GET-CHILDITEM cmdlet
An example of this is below:
Notice that I first set the location to the “My” store and then returned all of the items within that store. By doing this, we are able to see how we can verify which store name in the Certificates snap-in maps to the “My” store in PowerShell. To accomplish this, take the following steps:
- Open the Certificates snap-in and navigate to the store of interest (in this case, we will navigate to the Personal store)
- Within that store, double-click one of the certificates. This brings the certificate details up
- Select the Details tab and scroll down to the thumbprint attribute
If the certificate in the Certificates store has the same thumbprint as we see in PowerShell, we have confirmed that the two stores are the same. The screenshot below shows the certificate thumbprint from my Personal store in the Certificates snap-in. As can be seen, the thumbprint matches that of the certificate listed above. We have confirmed that the “My” store we see in PowerShell is the same as the “Personal” store we see in the Certificates snap-in
But what if we want to get all of the certificates in our selected context? It turns out that this is an even easier task because we don’t need to set our location to any specific store first. Instead, if we want a list of every certificate in our selected context we only need to enter the following at the context root.
Doing this will cause every certificate in each of our stores to be output to the screen as shown below:
Note that when I ran this cmdlet, the results were too long for a single screen of output. But in the screenshot above, we can see that the TrustedPublisher, ClientAuthIssuer and Remote Desktop stores have all been queried. The Remote Desktop store had at least 2 certificates to list while Trusted Publisher and ClientAuthIssuer did not have any.
PowerShell is configured with a default set of properties it will output when we run this command. But what if we want more properties to be displayed? Certainly the 7 listed in the screenshot above is not all that a certificate possesses (and it turns out that the store itself possesses extra properties than just the name attribute). How do we get this information?
To get a complete list of properties, we can type the following:
GET-CHILDITEM –RECURSE | FORMAT-LIST –PROPERTY *
In this case, we had to use two cmdlets and pipe the results of the first one (Get-ChildItem) to the second (Format-List). What’s happening here is fairly straightforward. Get-ChildItem is gathering every certificate store and certificate within our selected context (each of the returned items is technically an object, but that gets beyond the scope of our discussion). It then sends these items to Format-List using the pipeline, which is the vertical bar in the command above. The pipeline allows the cmdlet on the left to send its results to the next cmdlet (sending the results “through the pipeline” so to speak). The second cmdlet receives that information as input and takes additional steps. In this case, formatting the output in list form. By explicitly listing –Property * we are also saying that we want every property (not just the default 7 from above) to be listed. The results look similar to the following:
So what if I don’t want my output going to the screen? PowerShell makes it easy to output the results of any command to either a file or a printer. For our purposes, let’s look at how to export the list above to a file so we can retain it for future use. To do this, we would type:
GET-CHILDITEM –RECURSE | FORMAT-LIST –PROPERTY * | OUT-FILE –PATH “C:\FILE\OUTPUT.TXT”
Obviously the file path I’m exporting to is flexible. I just entered one that works in my lab.
One final point on this topic before moving on. What if we want to enumerate every certificate in every available context on our machine? Can we do the same thing from the root of Cert: and get every certificate from both the CurrentUser and LocalMachine contexts? As it happens, we can.
As the result snippet shows below, the only difference is that we will start by listing which context we’re working on before listing the stores and certificates within that context. Though the screenshot doesn’t show it, when we reach the end of the CurrentUser context, the command moves on to the LocalMachine context and enumerates all of the certificates and stores there as well.
So far we’ve only looked at a couple of things that the Certificate provider allows us to do in PowerShell, and already we see that it’s much more efficient to review the certificates in this way than to click our way through the Certificates snap-in.
Finding Certificate Expiration Dates
Suppose we are interested in finding every certificate that will be expiring in a specified period of time? This turns out to be very simple. Simply type the following from within the Cert provider:
GET-CHILDITEM –RECURSE –EXPIRINGINDAYS <Number of Days>
In my lab, I had to set the # of days to 1000 in order to get any results. When I did, I received 4 certificates that will be expiring within that time period. Looking at the screenshot below, we can see one of those. Notice that it starts by showing me which certificate context and store I could find the certificate. Then it lists the certificate itself.
An excellent article on this, written by the Scripting Guys, is located here. It shows how much easier this task is in PowerShell 3 and 4 than it was in PowerShell 2. If you want to see just how much simpler it is to work with certificates now, take a look at the article and note the hoops you had to jump through to find expiring certificates in the past.
Importing and Exporting Certificates
Finally, we’ll take a quick look at the process of importing and exporting certificates. This is a relatively common administrative task used, for example, when two computers don’t participate in the same CA hierarchy but need to both trust the same root CA.
Importing and Exporting certificates turn out to be slightly more complex than the previous commands, but not significantly. We will be using the Import-Certificate and Export-Certificate cmdlets that come with PowerShell 4. I have linked to the TechNet articles for these cmdlets, or we can choose to read the help files in PowerShell itself to find out more. Either option will work.
We will start with Export-Certificate so that we’ll have something exported that we can later import. Both exporting and importing require two steps:
- Get the certificate that will be imported/exported
- Carry out the operation to import/export the selected certificate
Depending on how we want to write our code, we can do these two steps at the same time (relatively) or as two separate commands. I’ll show you both.
First, if we want to export a certificate we need to find the certificate of interest. We do this using the following command:
$selectedcert = (Get-ChildItem –Path cert:\LocalMachine\My\DE53B1272E43C14545A448FB892F7C07A217A765)
Let’s look at what we’re doing here:
- $selectedcert is a variable that will store the certificate we choose (and there is nothing special about the name I chose…as long as we don’t select a reserved name, we can choose anything). You can recognize a variable because it starts with $ and in this case we’re using it to hold the information we get using Get-ChildItem
- Get-ChildItem is no different than the cmdlet we ran above. The only difference is that it’s within parentheses, which means it will run first (think ‘order of operations’ from high school math). We are explicitly specifying the path to the certificate using the –Path parameter and we’re using its thumbprint to uniquely identify which certificate we want to export
This line on its own doesn’t do much. Once we enter it, we can type $selectedcert at the PowerShell prompt to see what it contains. But we need to take the next step to actually export it. To do this, we need to type the following:
Export-Certificate –Cert $selectedcert –FilePath c:\test\export.cer
This command does the following:
- Export-Certificate is the cmdlet used to export the certificate, which should be self-explanatory
- -Cert $selectedcert is used to specify the cert we are exporting. Because we stored our chosen certificate in the $selectedcert variable in step 1 above, we can simply use the variable here instead of having to type out the entire command listed in step 1 (we can do this if we want…more on that in a moment)
- -File path <path> specifies where we want to send the certificate and specifies the file extension we want to use.
Once we’ve run the second command, we can go to the file location where the certificate was exported and see it. By double-clicking the file, the certificate will open up so that we can review it and make sure we’ve exported the right certificate. The screenshot below shows an example of this from my lab:
At this point, we have successfully exported our certificate. But what if we wanted to do everything in one single line rather than having to use two steps and store information in a variable as I did above? We can do this as well, though the code looks a bit more complicated. Here is what would happen if we combined these two commands into one:
Export-Certificate –Cert (Get-ChildItem –Path Cert:\LocalMachine\My\DE53B1272E43C14545A448FB892F7C07A217A765) –FilePath c:\test\export2.cer
Note that we simply put the entire Get-ChildItem command into the parentheses rather than using the variable. Because we use parentheses around Get-ChildItem, this cmdlet runs first and gets the certificate we want to export. Then Export-Certificate takes over and does the actual exporting. This gives us the whole command on a single line (the multiple lines above are due to formatting on the blog). While this is a bit more complex to read, if we run it we’ll see that we get the same thing as before.
Finally, we’ll take our certificate and import it (perhaps we’re importing it to another machine, in which case we’ll need to either move the cert to the destination machine or set up a network share so the other machine can access the cert. In my case, I will import this certificate to another Windows 2012 R2 server I have in my test lab.
To import the certificate (and we are assuming we have successfully moved the .cer file to our destination machine), do the following:
Get-ChildItem –Path c:\import\export.cer | Import-Certificate –CertStoreLocation cert:\LocalMachine\My
As we can see, the first cmdlet (Get-ChildItem) locates the certificate and sends the result to the second cmdlet (Import-Certificate). Import-Certificate specifies which store the certificate should be imported to and then does the work of importing the certificate. Below, we can see what this looks like in the PowerShell console:
If we want, we can look in the Certificates snap-in to verify the certificate has been successfully imported.
Last of all, since this was merely done for test purposes, let’s look at how to remove the certificate from the store. To do this is as simple as the following:
Remove-Item –Path Cert:\LocalMachine\My\DE53B1272E43C14545A448FB892F7C07A217A765
Notice that by using the –Path parameter I didn’t even have to be in the Cert provider to interact with it. I just needed to specify the parameter at the beginning, much as I would specify a full path for a file (such as C:\Test\Export.cer).
In this article I have attempted to show the basics of working with the Certificates store using PowerShell. PowerShell offers a great deal of flexibility when working with certificates. Here, I have only showed a few basic commands running against a local machine. It is quite possible to do a great deal more than I’ve shown, including interacting with certificate stores on other devices, etc. Hopefully this basic look at the Certificates provider has given you an appetite to learn more and to incorporate this into your administrator’s toolkit as you work with certificates in your environment.