Accidental Task: Test SCCM Certificate Expiration.

When System Center Configuration Manager (SCCM) Client connects to upstream management point, it should confirms it identity. In an environment with a single security realm (i.e. in the same Active Directory forest), such identification is implemented seamlessly without any additional configuration.

However, if management points are not in the same trusted environment, then certificates come into game. A client presents pre-installed certificate, which must be trusted and successfully verified by an upstream management point. It’s also true for management point’s certificate, because identification is mutual. So, the pain point here is to ensure that every single certificate-base SCCM Agent has a valid certificate. Skipping the initial enrollment task at the moment, let’s concentrate on certificate life cycle. Sooner or later it will expire, so it’s good to replace it before the expiration to keep manageability on. Again, for the purposes of this post, I’m skipping the way these certificates can be renewed. My task for today is to make a report on expiring certificates.

Unlike Operations Manager Agent, which has an explicit binding to a certificate, Configurations Manager Agent only has some “guidelines” on how to select a certificate. Therefore, the certificate, currently used by SCCM Agent, should be somehow discovered. First and the most obvious way is to search in client’s log. This way is good for a human, but not for a script (or any other automation). Log parsing, even very specific and narrow, is not the easiest task. Luckily, there is a way how to get current certificate’s thumbprint. It’s stored in the CCM_ClientIdentificationInformation WMI class.

Well, the official Configuration Manager Client SDK doesn’t mention this class, neither its properties aren’t self-documenting at all.

According to Gabe Brown’s post on 14 of December 2009, the information we’re looking for is in ReservedString1 property. Now it’s possible to find the current certificate, thus check its validity. Next part is to report on certificate expiration.

Configurations Manager offers a feature called “Compliance Settings“. It was significantly improved in SCCM 2016. First, one or more Configuration Items have to be created. Each CI represent a single configuration aspect, such as (in this case) “SCCM Bound Certificate Expiration”. Then, one or more CIs can be assigned to a Configuration Baseline. Finally, a Baseline object is deployed to a collection of devices, which must be compliant with this baseline. This is the mechanism, I’m going to use, to report on certificate expiration. CIs support PowerShell scripts to determine compliance state, so let’s craft a script to find and check current SCCM certificate.

$DaysThreshold = 5

$ComplianceResult = "NotCompliant"
$sccmCert = $null
$sccmCert = Get-WmiObject -Query "SELECT * FROM CCM_ClientIdentificationInformation" -Namespace "root\ccm" -ErrorAction SilentlyContinue
if ($sccmCert -eq $null)
{
  $ComplianceResult = "Compliant" # because no certificate required
}
else
{
  # try SMS store
  $certThumb = $sccmCert.ReservedString1
  $certStore = [System.Security.Cryptography.X509Certificates.X509Store]::new("SMS", [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine)
  $certStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly) # doesn't error if stroe not exists
  $myCert = $certStore.Certificates | Where-Object { $_.Thumbprint -eq $certThumb }
  $certStore.Close()
  if ($myCert -eq $null)
  {
    # now try Personal (My) store
    $certStore = [System.Security.Cryptography.X509Certificates.X509Store]::new([System.Security.Cryptography.X509Certificates.StoreName]::My, [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine)
    $certStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly) # doesn't error if stroe not exists
    $myCert = $certStore.Certificates | Where-Object { $_.Thumbprint -eq $certThumb }
    $certStore.Close()
  }
  if ($myCert -ne $null)
  {
    $daysLeft = [System.Math]::Round($myCert.NotAfter.Subtract([datetime]::UtcNow).TotalDays, 2)
    if ($daysLeft -ge $DaysThreshold)
    {
      $ComplianceResult = "Compliant" # Compliant, because certificate is still valid for $daysLeft days.
    }
    else
    {
      $ComplianceResult = "NotCompliant" # Not complient, expiring soon
    }
  }
}

$ComplianceResult

At the final step a CI is created:

Use the highlighted button to add the test script.

After successful CI creation, a Baseline can be created:

When the new Configuration Baseline is deployed to a collection, the script runs and report on certificate validity. To view baseline valuation status, SCCM has a number of predefined reports in the “Compliance and Settings Management” section under the “Reports” node.

That’s it!

P.S. As usual, there is always room for improvements. For instance, the scrip may return number of days left, rather than compliant/non-compliant status. This will allow change the threshold in CI properties, rather then editing the script.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s