Archive for the ‘PowerShell’ Category

Certificate :- Test Website SSL Protocols

April 22, 2017 1 comment

Certificate :- Test Website SSL Protocols

NOTE: This turned out to be a long post.

The “S” in HTTPS:\\ means that the site has security and is supposed to be secure.  There is a lot more to the “S” though and that’s where the SSL part of the title comes from.  Hypertext Transfer Protocol Secure (HTTPS) is a combination of the Hypertext Transfer Protocol (HTTP) with the Secure Socket Layer (SSL)/Transport Layer Security (TLS) protocol. TLS is an authentication and security protocol widely implemented in browsers and Web servers. So, as you surf around the web, you’ll encounter HTTPS:\\ and HTTP:\\ sites. I would never enter personal information into an HTTP:\\ site as this isn’t secure.  And, supposedly with HTTPS:\\ sites like banks and online shopping there is some expectation that the site is secure enough for us to do our transactions. All these things you should be aware of.

So, as we take precautions for doing business online I’ve recently noticed that so many certificates are expiring on web sites I’m visiting and it’s occurring on an assortment of sites. I use a tool that popups when I encounter issues with certificates. You’d think that a company’s overall strategy for ensuring that security certificates remain up to date and monitored for expiration dates should be in the daily SOP.

After looking into the issue I decided I’d start by searching for something along the lines of this code by  Chris Duck  I already had some simple code that I’ve been using but wanted something where I could modify to suite my needs. In fact found many similar ideas while searching the web and this is how I’ve put my research to use.

Loading and running Test-WebSiteSslProtocols

I made some simple modifications to the script as this location:
$ProtocolNames = [System.Security.Authentication.SslProtocols] | gm -static -MemberType Property | ?{ $_.Name -notin @(“Default”, “None”) } | %{ $_.Name }
$global:certinfo = @() #- ADD THIS
And at this place in the code.

$global:certinfo = $ProtocolStatus #- ADD THIS

This is the modified function without all the comments.
function Test-WebSiteSslProtocols
Check validity of SSL on web sites visited populate collection to be used for updating into database
Ideas found searching the web

param (
[Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
[Parameter(ValueFromPipelineByPropertyName = $true)]
[int]$Port = 443
$ProtocolNames = [System.Security.Authentication.SslProtocols] | gm -static -MemberType Property | ?{ $_.Name -notin @(“Default”, “None”) } | %{ $_.Name }
$global:certinfo = @()
$ProtocolStatus = [Ordered]@{ }
$ProtocolStatus.Add(“URLChecked”, $URLChecked)
$ProtocolStatus.Add(“Port”, $Port)
$ProtocolStatus.Add(“KeyLength”, $null)
$ProtocolStatus.Add(“SignatureAlgorithm”, $null)

$ProtocolNames | %{
$ProtocolName = $_
$Socket = New-Object System.Net.Sockets.Socket([System.Net.Sockets.SocketType]::Stream, [System.Net.Sockets.ProtocolType]::Tcp)
$Socket.Connect($URLChecked, $Port)
$NetStream = New-Object System.Net.Sockets.NetworkStream($Socket, $true)
$SslStream = New-Object System.Net.Security.SslStream($NetStream, $true)
$SslStream.AuthenticateAsClient($URLChecked, $null, $ProtocolName, $false)
$RemoteCertificate = [System.Security.Cryptography.X509Certificates.X509Certificate2]$SslStream.RemoteCertificate
$ProtocolStatus[“KeyLength”] = $RemoteCertificate.PublicKey.Key.KeySize
$ProtocolStatus[“SignatureAlgorithm”] = $RemoteCertificate.SignatureAlgorithm.FriendlyName
$ProtocolStatus[“Certificate”] = $RemoteCertificate
$ProtocolStatus.Add($ProtocolName, $true)
$ProtocolStatus.Add($ProtocolName, $false)
$global:certinfo = $ProtocolStatus

The [PSCustomObject]$ProtocolStatus is local to the function. As I wanted to use this for input into a function to update
a database I’ve added $global:certinfo = $ProtocolStatus
$global:certinfo can now be used as input to other functions.

I’ll be investigating running this same process in the cloud where it would be cool cataloging information contained in certificates or to look
for anomalies that might be found in a certificate.

Running the function (Test-WebSiteSslProtocols above will return as a data set:

As I’ve also added the global collection which will allow you to drill down into the results.

Peering into the global collection

I’ve used 3 different methods to display information from the collection.

  1. $global:certinfo
  2. $global:certinfo.Certificate
  3. $global:certinfo.Certificate | format-list

As you see each returns varying views that can be used for further analysis or updating into a database.  With method 1($global:certinfo) we have a collection of values. ComputerName,
Port, KeyLength, SignatureAlgorithm, Ssl2, Ssl3,  Tls, Tls11,  and Tls12 are single values that don’t require a drill down to collect populate values for updating into a database.

Certificates returns a collection.  Item 2 ($global:certinfo.Certificate), dose not return enough visual information I use Item 3. $global:certinfo.Certificate | format-list

as returned by or function to get web records.

Extensions is returned as an object. As of yet I’ve not found a sql datatype at least in 2012 to use for import directly. So, I’ve created this snippet that can extract and serialize into xml.

To see all the elements available I’m using method 3( $global:certinfo.certificate | Format-List) As this returns the Extensions information.

The area that contains Subject, Issuer, Thumbprint, FriendlyName, NotBefore, NotAfter and Extensions.  Extensions contains another collection for the Oid values.

$global:certinfo.certificate.Extensions and $global:certinfo.certificate.Extensions.Oid (not very friendly)

The following code snippet I’ve wrapped into a function just for simplicity. Just strait code without try/catch blocks at least for now. 🙂

This function, takes the the $global:certinfo.certificate.Extensions data and generates a more friendly way to view the data.

function CompactExtensions() # Also, in Insert-SslWebSite function (slightly different format)
$global:MyExt = @()
$global:ext = @()
$Extensions = & {
for ($i = 0; $i -le $global:certinfo.Certificate.Extensions.count – 1; $i++)
Write-Output ([pscustomobject]@{

Extensions = & {
Write-Output ([pscustomobject]@{
ValueU = $global:certinfo.Certificate.Extensions.EnhancedKeyUsages[$i].Value
FriendlyNameU = $global:certinfo.Certificate.Extensions.EnhancedKeyUsages[$i].FriendlyName
Value = $global:certinfo.Certificate.Extensions.oid[$i].Value
FriendlyName = $global:certinfo.Certificate.Extensions.oid[$i].FriendlyName
RawData = ([string]::Join(“,”, $global:certinfo.Certificate.Extensions[$i].RawData))
# #End foreach objectcollection
} #End Object Collection$
$global:ext = $extensions
$global:MyExt = $global:ext.extensions

$EnhancedKeyUsageList = & {
Write-Output ([pscustomobject]@{
EnhancedKeyUsageList = {
foreach ($itm in ($global:certinfo.Certificate.Extensions.EnhancedKeyUsages))
Write-Output ([pscustomobject]@{
Value = $itm.Value
FriendlyName = $itm.FriendlyName
} #End foreach objectcollection
} #End Object Collection

When you run CompactExtensions the Oid collection is extracted and merged into $global:extensions and when presented in this way is more readable.

A version of CompactExtensions is also in the Insert-SslWebSite

As, I want to insert or update records into a database  I want to convert the $global:extensions.extensions into xml for insertion into the database field extensions.

So, using a small piece of .NET $ExtensionsXml = [System.Management.Automation.PSSerializer]::Serialize($global:extensions.extensions, 3)

has created an in memory variable that will be used to input into the extensions field in the table.

Database Fields.

Adding this information into a database will allow for further analysis.  I’ve created (WIP) an API for backend calls to assist with parsing and validating information in the Certificate Subject field and OID values from the extensions xml field.  My API tracks OIDs, serial numbers and generally information contained in the certificate. Still WIP but soon to be published.

CertificateSubject, OU=GTI GNS, O=JPMorgan Chase and Co., STREET=270 Park Ave, L=New York, S=New York, PostalCode=10017, C=US, SERIALNUMBER=0691011, OID. Organization, OID., OID., OU=Umpqua Bank, O=Umpqua Bank, STREET=445 SE Main St., L=Roseburg, S=Oregon, PostalCode=97470, C=US, SERIALNUMBER=143662, OID. Organization, OID.

My first pass at setting up the table that I needed used varchar and nvarchar on fields.

CREATE TABLE [dbo].[SSLByWebSite](
 [URLChecked] [varchar](255) NULL,
 [Port] INT NULL,
 [KeyLength] [varchar](255) NULL,
 [SignatureAlgorithm] [varchar](255) NULL,
 [Ssl2] BIT NULL,
 [Ssl3] BIT NULL,
 [CertificateSubject] [nvarchar](max) NULL,
 [Extensions] [nvarchar](max) NULL,
 [CertificateIssuer] [varchar](255) NULL,
 [CertificateSerialNumber] [varchar](255) NULL,
 [CertificateNotBefore] [varchar](255) NULL,
 [CertificateNotAfter] [varchar](255) NULL,
 [CertificateThumbprint] [varchar](255) NULL,
 [Tls] BIT NULL,
 [Tls11] BIT NULL,
 [Tls12] BIT NULL


Function to upload into database.

The fields that are in the table are represented in the Insert-SslWebSite function.

This function builds up the parameter list that is passed into SQL to be inserted into the table.

Currently no data is checked for dups, nor indexed. In this release. I will add at least one to URLChecked as I do want to make delta

comparison checks on future calls to the web sites.

function Insert-SslWebSite ()
{ # Currently not manditory
param (
[string]$URLChecked = $global:certinfo.URLChecked
[string]$ServerName = $env:servername
[System.Reflection.Assembly]::LoadWithPartialName(‘Microsoft.SqlServer.Smo’) | out-null

$conn = New-Object System.Data.SqlClient.SqlConnection
$conn.ConnectionString = “Server=$ServerName; Database=<DATABASENAME>; Integrated Security=true”

$cmd = New-Object System.Data.SqlClient.SqlCommand
$cmd.CommandText = “INSERT INTO SSLByWebSite(URLChecked,Port `
,CertificateNotBefore, CertificateNotAfter, CertificateThumbprint, Tls,Tls11, Tls12) `
VALUES (@URLChecked, @Port, @KeyLength, @SignatureAlgorithm, @Ssl2, @Ssl3
,@CertificateSubject, @Extensions, @CertificateIssuer `
,@CertificateSerialNumber `
,@CertificateNotBefore `
,@CertificateNotAfter `
,@CertificateThumbprint `
,@Tls `
,@Tls11 `

if ($global:certinfo.KeyLength) { “has VALUE” }
else { $global:certinfo.KeyLength = 0 }

$cmd.Connection = $conn
$CompInfoEntryDate = Get-Date

# Split Extensions prior to insertion into database
$Extensions = & {
for ($i = 0; $i -le $global:certinfo.Certificate.Extensions.count – 1; $i++)
Write-Output ([pscustomobject]@{
Extensions = & {

Write-Output ([pscustomobject]@{
EnhancedKeyUsageList = & {
foreach ($itm in ($global:certinfo.Certificate.Extensions[$i].EnhancedKeyUsages))
Write-Output ([pscustomobject]@{
Value = $itm.Value
FriendlyName = $itm.FriendlyName
} #End foreach objectcollection
} #End Object Collection
catch [System.IO.IOException] {
# Catch all other exceptions thrown by one of those commands
# not really doing anything with these but here in case
# Execute these commands even if there is an exception thrown from the try block
# not really doing anything with these but here in case
Critical = $global:certinfo.Certificate.Extensions[$i].Critical
Value = $global:certinfo.Certificate.Extensions.oid[$i].Value
FriendlyName = $global:certinfo.Certificate.Extensions.oid[$i].FriendlyName
RawData = ([string]::Join(“,”, $global:certinfo.Certificate.Extensions[$i].RawData))
# #End foreach objectcollection
} #End Object Collection$
# Quick export and import as string to for db insert
# Used as example. In practice can use different methods to generate. Both methods work great
# $extensions.extensions | export-clixml c:\temp\temp.txt
# [string]$mdxml = get-Content c:\temp\temp.txt # Value is inserted as a string
# $extensions.extensions | export-clixml c:\temp\temp.txt
$Depth = 3
[string]$mdxml = [System.Management.Automation.PSSerializer]::Serialize($extensions.extensions, $Depth)

# $mdxml = “”
$cmd.Parameters.AddWithValue(“@URLChecked”, $global:certinfo.URLChecked) | Out-Null
$cmd.Parameters.AddWithValue(“@Port”, $global:certinfo.Port) | Out-Null
$cmd.Parameters.AddWithValue(“@KeyLength”, $global:certinfo.KeyLength) | Out-Null
$cmd.Parameters.AddWithValue(“@SignatureAlgorithm”, $global:certinfo.SignatureAlgorithm) | Out-Null
$cmd.Parameters.AddWithValue(“@Ssl2”, $global:certinfo.Ssl2) | Out-Null
$cmd.Parameters.AddWithValue(“@Ssl3”, $global:certinfo.Ssl3) | Out-Null
$cmd.Parameters.AddWithValue(“@CertificateSubject”, $global:certinfo.Certificate.Subject) | Out-Null
$cmd.Parameters.AddWithValue(“@Extensions”, $mdxml) | Out-Null
$cmd.Parameters.AddWithValue(“@CertificateIssuer”, $global:certinfo.Certificate.Issuer) | Out-Null
$cmd.Parameters.AddWithValue(“@CertificateSerialNumber”, $global:certinfo.Certificate.SerialNumber) | Out-Null
$cmd.Parameters.AddWithValue(“@CertificateNotBefore”, $global:certinfo.Certificate.NotBefore) | Out-Null
$cmd.Parameters.AddWithValue(“@CertificateNotAfter”, $global:certinfo.Certificate.NotAfter) | Out-Null
$cmd.Parameters.AddWithValue(“@CertificateThumbprint”, $global:certinfo.Certificate.Thumbprint) | Out-Null
$cmd.Parameters.AddWithValue(“@Tls”, $global:certinfo.Tls) | Out-Null
$cmd.Parameters.AddWithValue(“@Tls11”, $global:certinfo.Tls11) | Out-Null
$cmd.Parameters.AddWithValue(“@Tls12”, $global:certinfo.Tls12) | Out-Null

$cmd.ExecuteNonQuery() | Out-Null


Once you have both functions implemented it’s easy to run as follows:

Test-WebSiteSslProtocols | Insert-SslWebSite.

It’s nice to be able to pipe this into the Insert-SslWebSite function. And the results of these simple methods show xml for extensions and an inserted

record for URLChecked. I will add a key to URLChecked. 🙂

I intend on placing all code with comments on gethub after I complete my testing and some minor updates to functions to allow for parallel workflows.

So, in a future post I’ll layout how I use PowerShell with Workflows and running parallel while reading in list of websites I want to monitor.

In larger shops this can be beneficial.

Let me know what you think.


PowerShell – Out-GridView Headers (Using Get-Process)

March 13, 2017 1 comment

PowerShell – Out-GridView Headers with Get-Process

I thought I would do a follow-up on a previous post about using out-GridView with Headers and use Get-Process this time.  Although this post is
similar to prior post, I’ve shortened a few things in regards to my collection of header information.

($A = Get-Process) | Out-GridView or OGV

Essentially the $A is creating a variable/collection of items and displays without column names.
Use $A.GetType() and as you see the object type is a collection(System.Array).

At this point $A returns the items that are in the collection including the headers that are automatically created and are the names that will be
used for our header object.

the $A and Get-Process with Out-Gridview/OGV both return the same results with out our extra header information. 

Discovering what makes up the header can be found by using the following commands.

The first item displays all the available Member Types available for $A.
$A | Get-member -MemberType *
Here is a mini sub-set of items that are displayed. I find it a tad strange that all items were not created as an AliasProperty.

The first 7 items identified as AliasProperty are used as the column headings from $A.
$A | Get-member -MemberType ‘AliasProperty’

$A | Get-member -MemberType ‘ScriptProperty’

Headings to be used from Get-member functions are:
Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id  SI ProcessName

Data to populate the tables arrives in the following form:
Handles AliasProperty Handles = Handlecount
Name AliasProperty Name = ProcessName
NPM AliasProperty NPM = NonpagedSystemMemorySize64
PM AliasProperty PM = PagedMemorySize64
SI AliasProperty SI = SessionId
VM AliasProperty VM = VirtualMemorySize64
WS AliasProperty WS = WorkingSet64
Id                         Property       int Id {get;}
CPU                   ScriptProperty System.Object CPU {get=$this.TotalProcessorTime.TotalSeconds;}

Running get-help get-process -full returns information about Get-Process.

This shows what the values are for column headings.  SessionID does not appear in the help file. ( there is a lot more information then what I’ve displayed)

Now that I’ve shown where the fields are for the headers that will be used for reporting lets build the Report-Header object.
Create an object to hold header values

$TestProperties = new-Object Object

By itself $TestProperties only returns the System.Object

Now lets setup the Properties we want in our object.
add-Member -inputobject $TestProperties -MemberType NoteProperty -Name “Handles” -Value “Handles
add-Member -inputobject $TestProperties -MemberType NoteProperty -Name “NPM” -Value “NPM(K)
add-Member -inputobject $TestProperties -MemberType NoteProperty -Name “PM” -Value “PM(K)
add-Member -InputObject $TestProperties -MemberType NoteProperty -Name “WS” -Value “WS(K)
add-Member -InputObject $TestProperties -MemberType NoteProperty -Name “VM” -Value “VM(K)
add-Member -InputObject $TestProperties -MemberType NoteProperty -Name “CPU” -Value “CPU(s)
add-Member -InputObject $TestProperties -MemberType NoteProperty -Name “Id” -Value “Id
add-Member -InputObject $TestProperties -MemberType NoteProperty -Name “SI” -Value “SI
add-Member -InputObject $TestProperties -MemberType NoteProperty -Name “ProcessName” -Value “ProcessName

I kept the values in the same order that Get-Process returns.

What I’m doing is creating an initialized array with values that will be used as the header of a report. For this example of working with Report-Headers I’ve used Get-Process to show that it’s pretty darn easy to quickly add report headers for use in OGV or Out-GridView functions.

At this point I have an collection/array that contains 1 initialized record of straight text that will be used for Out-GridView.

Without placing the statements into a function here is what you see when running $TestProperties | Out-GridView

There 1 initialized record showing headings. 🙂

Now that there is an initialized collection of header information we can combine $A and $TestProperties and generate our Out-GridView.

Ooops. 🙂 $TestProperties is not the type that allows for joining objects of data together.  You’ll need to create your collection/array to something

like this  $local:TestCollection = @() But it’s just as easy to create a simple function

function Report-Headers() {
# There are a few methods for createing objects. Keeping this simple
# to show the possibilities

# Sets up an empty object with header names for use in out-gridview
$local:TestCollection = @()

# Create a new object with the same values as in ($A = Get-Process) to hold header names
$TestProperties = new-Object Object
add-Member -inputobject $TestProperties -membertype NoteProperty -Name “Handles” -Value “Handles”
add-Member -inputobject $TestProperties -membertype NoteProperty -Name “NPM” -Value “NPM(K)”
add-Member -inputobject $TestProperties -membertype NoteProperty -Name “PM” -Value “PM(K)”
add-Member -InputObject $TestProperties -MemberType NoteProperty -Name “WS” -Value “WS(K)”
add-Member -InputObject $TestProperties -MemberType NoteProperty -Name “VM” -Value “VM(K)”
add-Member -InputObject $TestProperties -MemberType NoteProperty -Name “CPU” -Value “CPU(s)”
add-Member -InputObject $TestProperties -MemberType NoteProperty -Name “Id” -Value “Id”
add-Member -InputObject $TestProperties -MemberType NoteProperty -Name “SI” -Value “SI”
add-Member -InputObject $TestProperties -MemberType NoteProperty -Name “ProcessName” -Value “ProcessName”

$local:TestCollection += $TestProperties
$global:ReportHeaders = $local:TestCollection

I realize that there are similar ways to solve this issue.  However this is simple and works.

$Global:ReportHeaders | out-gridview

Please keep in mind that I’m not doing any calculations on the field values.  This is just a simple method for creating report headers if they are needed.

Well that’s it for this post.  Comments and suggestions are always welcome.




PowerShell :- Certificate Object Model Creation Part 1

March 4, 2017 2 comments

Certificate Object Model Creation Part 1

An object model is a logical interface, software or system that is modeled through the use of object-oriented techniques. It enables the creation of an architectural software or system model prior to development or programming. An object model is part of the object-oriented programming (OOP) lifecycle.
What is an Object Model? – Definition from Techopedia

What I’ve created is a representation, (work in progress) that represents an object model for a certificate using PowerShell and by creating XML that can be loaded into a database further analysis can be performed on results. Using a DTD assists with well-formed XML. I’ve been considering adding MongoDB into the mix and will do so shortly. Currently all certs on my machine(s) can be read and placed into an SQLServer Database.

I’ll start by going over steps and functions to load the certs on a machine into a collection. I’ll begin by loading a function to create to create $global:CertCollection. I used simple method to compress RawData to include a comma separator for that field. I will go over using TypeLib’s to assist with other field values in another post.
Let’s do some pre-setup so the command will run
Import-Module PKI
Set-Location Cert:\LocalMachine

function Build-GlobalCert()
### Builds collection from under root
## Ran with out ` line continuation all should be on one line.
$global:CertCollection = GET-CHILDITEM –RECURSE | Select-Object -Property “PSPath”,
“PSParentPath”, “PSChildName”, “PSDrive”, “PSProvider”, “PSIsContainer”, “EnhancedKeyUsageList”, “DnsNameList”,
“SendAsTrustedIssuer”, “EnrollmentPolicyEndPoint”, “EnrollmentServerEndPoint”, “PolicyId”, “Archived”, “Extensions”, “FriendlyName”, “IssuerName”,
“NotAfter”, “NotBefore”, “HasPrivateKey”, “PrivateKey”, “PublicKey”, “SerialNumber”, “SubjectName”, “SignatureAlgorithm”, “Thumbprint”, “Version”, “Handle”, “Issuer”,
“Subject”, @{ Name = “RawData”; Expression = { ([string]::Join(“,”, $_.RawData)) } }
##Call or run your function:

And that generates the collection we can work with running $global:certcollection returns all items in the collection.
$global:certcollection[$global:certcollection.count-1] entering this command displays the last record contained in the collection.

If you’re working from command line and don’t want to type full item each time you can shorten into a single variable.
$lastCert = $global:certcollection[$global:certcollection.count-1]

By looking at the output I decided to create the XML following the pattern the output revealed.
Using a simple tool like XMLNotePad from Microsoft will allow you to quickly work on xml for an object model representation using XML.
It’s a free tool and you don’t require much to understand the concept. Search and you should find it.


In my next post I’ll start the drill down process into the collection to build the XML/Object Model
Next post is Certificate Object Model Creation Part 2

PowerShell :- Certificate Store Navigation



For starters there are several methods for working and viewing certificates. These are the steps I took while digging into certs. I’ll try and go over things in a top down fashion so it will be easy to follow along.

When starting in PowerShell and running as administrator the default location is c:\windows\system32. I like changing or creating a temp directory where commands and output can be traced quickly by having all items in one central location. Your PowerShell Profile should be setup to use a different folder as your starting folder.  Standard output items could end up in c:\windows\system32 and I’d rather not clutter up that folder with extra output that really should not be in that folder.

(Get-Help about_profiles) is a good starting point when reading about profiles.
Example set your location to use the home path: Set-Location $env:homepath.
Setting your to something different all should be ok.  For running my examples I’m going to Set-Location to the PSPATH “CERT”.

Import the PKI Module if you have the IMPORT-MODULE PKI in your PowerShell profile you won’t have to import the module as it will already be there.importmodulepki

You can and should set your default directory in your $Profile


I’m setting location to “CERT” as this is where I want to start out. With out the semicolon at the end of CERT:
you’ll receive an error so just re-enter and add :.


Clear the host that’s more for me at this point so I could have a clean capture. 🙂


Running GET-CHILDITEM shows us the certificate stores located on the machine for the current user and
local machine. You can SET-LOCATION  to either CurrentUser or LocalMachine.  For now I’ll go with LocalMachine


And, once in CERT:/ SET-LOCATION CurrentUser then run GET-CHILDITEM.

The certmgr screen can be assessed via command line with this command Invoke-item CERT:


There are some differences in appearance but essentially can do the same with both interfaces. In another post I’ll go over the GUI screen.

Switch to the LocalMachine area. SET-LOCATION CERT:\LocalMachine




So, far we’ve setup to begin working with certificates. The following is a listing of commands that can be used
while working with certificates.
For Source you can see that PKI is listed. That’s because we IMPORTED-MODULE PKI


You can also run this command to get a sorted list. (I’m using Source)
GET-COMMAND *CERT* | Sort-Object -Descending Source


Next post is Certificate Object Model Creation Part 1