Reading SCCM Logs with PowerShell

Reading SCCM Logs with PowerShell

How to write SCCM logs to the Event Log

Can’t we just use a logarithm?

Objective

SCCM log files follow a standard schema that we can use to parse these log files for the data we’re interested in (timestamp, log data, etc.). Once we’ve separated the important bits, we can do whatever we’d like with that data. In this case, let’s dive in on how we can write those logs to the Event Log for later forensic investigation.

This post covers how this can be done for an OS upgrade task sequence file (smsts.log), but this script can be adapted to take any log file and write the contents to the Event Log.

Background

If you’ve used SCCM, you know that all logs are written to log files on disk. You’ve likely found yourself questioning which of the dozens of log files you need to open, to troubleshoot Jim’s failed installation of Chrome (again!). You probably also have been longing for the ability to instantly, and remotely view any SCCM logs your heart desires.

Prerequisites

You’re going to learn a lot but you’re also expected to come to the table with a few things. If you plan to follow along, be sure you have the following:

  1. A functioning SCCM environment, capable of deploying to clients
  2. A deployed task sequence/package/software/etc.
  3. Windows PowerShell 5.1 or newer, run as administrator

The Script

I’ll start by sharing the entire script, then walking through each section below. I’m always open to suggestions, so feel free to open a pull request or comment below!

The Break Down

We Kind of Need These

We want to ensure two things:

  1. Rightfully so, the Event Log Source exists, before we write to it, preventing any possible errors.
  2. The log file we’re reading from exists because you know, we need that.
if (-not [System.Diagnostics.EventLog]::SourceExists($Source)) {
  try {
    New-EventLog -LogName Application -Source $Source
  }
  catch {
    throw "Failed to create log source: $_"
  }
}

$log = "$LogPath\$LogFile"

if (-not (Test-Path $log)) {
  throw "Failed to locate SCCM log: $log"
}

It’s Strippin’ Time

We only care about what’s between <!\[LOG\[ and \]LOG\]!> in the SCCM log files. We are concerned with specifically the content of the log message, not any of the metadata behind it.

try {
  $formatted_log = Get-Content $log -Raw |
    ForEach-Object { $_ -replace '<!\[LOG\[', '' } |
      ForEach-Object { $_ -replace '\]LOG\]!>(.*)', '' }
}
catch {
  throw "Failed to read log file '$log' with error $_"
}

Trim the Fat

Event Log entries are limited to 32KB per message. Since SCCM log files append by default, your log file may have grown well beyond that limit. Let’s only grab the latest ~32KB from the log file and write that to the Event Log. I’ve found subtracting 31000 to be the sweet spot, as to not overwhelm the Event Log.

Here’s how the math works:

  1. Take the length of the log, for example 300,000 characters
  2. Subtract just under 32KB from that length
  3. Take all characters from the end of the log until you hit that subtracted number above, leaving you with only the latest log entries.
$length = $formatted_log.Length
if ($length -gt 32766) {
  $formatted_log = $formatted_log[($length - 31000)..$length] -join ''
}

Errors Will Be Errors

We’ll want to make use of the various Event Log entry types exposed via the Write-EventLog cmdlet. If there’s an error, let’s make it so in the Event Log. In this case, an error is written only when the string, defined as the parameter $FailureString is detected anywhere in the log file.

We’ll also want to prepend a specific string to each message to make querying these in bulk easier later on.

if ($formatted_log -match $FailureString) {
  $formatted_log = "IPU failed:`n$formatted_log"
  $EntryType = 'Error'
}
else {
  $formatted_log = "IPU succeeded:`n$formatted_log"
}

Grab the Glue

We’re (finally!) ready to write the Event Log message, we’ve gathered all the info we need, now all that’s left is to write the message. This specific instance leverages a neat PowerShell feature called splatting to make the args easier to read. For good measure, let’s make sure we capture any possible errors by appending the ErrorAction parameter.

try {
  $params = @{
    LogName   = 'Application'
    Source    = $Source
    EventId   = 1337
    EntryType = $EntryType
    Message   = $formatted_log
  }
  Write-EventLog @params -ErrorAction Stop
}
catch {
  throw "Failed to export SCCM Event Log: $_"
}

Conclusion

This PowerShell script enables us to grab a log file output by an SCCM OS upgrade task sequence and write it to the Event Log.

This is particularly useful if you can somehow forward these Event Logs to a centralized logging system to query in aggregate, or you want to use remote PowerShell to query SCCM logs on systems throughout your network. Either way, I hope this post was helpful!

Now get loggin’ 📝


Improve this page: 

Share on:      

Comments 💬