How to Quickly Retrieve Errors from OS Deployment Logs with PowerShell

I saw an interesting post yesterday by Keith Garner on using PowerShell’s “Select-string” to search OS deployment logs and find errors by searching on the entry type instead of the usual way that the CMTrace utility does it – by highlighting keywords.  I decided to take the idea further and create a script that will search my deployment logging shares, allow me to choose which deployed computer to search on, which log file to search, and then return the errors into CMTrace for easy viewing.

Here’a a quick demo:

I run the script and it searches my OSD logging directory (“SLShare” variable in the customsettings.ini) and returns all deployments in the last 10 days:

osd1

I select the deployment I want and click OK.  Then it searches recursively for all the log files in that directory, and asks me to choose one:

osd2

I want to search the smsts.log for errors, so I select it and click OK.  It then puts all the errors into a temporary log file and invokes it.  As .log files open with CMTrace by default, I can read through the errors more easily.

osd3

The script will then watch the CMTrace process, wait until you exit it, and delete the temporary log.

Configure the Script

The script has comment-based help, and there are some parameters you can use.  You will need to set the defaults in the script as the parameters are not mandatory.

$NumberOfDays – The script will search the logging share for deployments in the last x number of days

$LogDirectory – The location of your OSD logging share, eg your MDT “SLShare” directory

$Dynamic – Use this switch to search the dynamic logging share instead, eg your MDT “SLShareDynamicLogging” directory

$DynamicLogDirectory – the location of the  “SLShareDynamicLogging” directory

The Script


<#

.SYNOPSIS
    Retrieves error entries from logs used by MDT and ConfigMgr during OSD

.DESCRIPTION
    This script searches the OSD logs for error entries. You can:
    - specify the location of the log files, for example your deployment logging share
    - choose which computer's log files to search
    - choose which log file to search
    The errors will be added to a temporary log file, which will open with CMTrace (you need to set that as the default viewer for log files).
    This script is based on an idea from Keith Garner: https://keithga.wordpress.com/2015/05/04/find-errors-quickly-in-a-sccm-or-mdt-log-file/

.PARAMETER LogDirectory
    The location of your OSD logging directory, for example your "SLShare"

.PARAMETER DynamicLogDirectory
    The location of your OSD dynamic logging directory, for example your "SLShareDynamicLogging".  Use with the -Dynamic switch.

.PARAMETER NumberOfDays
    The number of days of deployment logs to retrieve, for example all deployments in the last 5 days

.PARAMETER Dynamic
    Use this switch to search the dynamic logging directory

.EXAMPLE
    .\Get-OSDLogErrors.ps1
    Searches the default logging directory for all deployments in the default number of days past, prompts you to choose the computer, then the log, then displays the error entries
    in a temporary log file

.EXAMPLE
    .\Get-OSDLogErrors.ps1 -LogDirectory \\mymdtserver\MDT_Logs$ -NumberOfDays 10
    Searches the logging directory specified for all deployments in the last 10 days, prompts you to choose the computer, then the log, then displays the error entries in a temporary
    log file

.EXAMPLE
    .\Get-OSDLogErrors.ps1 -Dynamic
    Searches the default dynamic logging directory for all deployments in the default number of days past, prompts you to choose the computer, then the log, then displays the error
    entries in a temporary log file

.NOTES
    Script name: Get-OSDLogErrors.ps1
    Author:      Trevor Jones
    Contact:     @trevor_smsagent
    DateCreated: 2015-05-11
    Link:        https://smsagent.wordpress.com

#>

[CmdletBinding()]
    param
        (
        [parameter(Mandatory=$False, HelpMessage="The number of days of deployment log files to check")]
            [string]$NumberOfDays = 5,
        [Parameter(Mandatory=$False, HelpMessage="The location of the OSD logging directory")]
            [string]$LogDirectory = "\\sccmserver01\MDT_Logs$",
        [parameter(Mandatory=$False)]
            [switch]$Dynamic,
        [Parameter(Mandatory=$False, HelpMessage="The location of the OSD dynamic logging directory")]
            [string]$DynamicLogDirectory = "\\sccmserver01\MDT_Logs$\Dynamic"
        )

if ($Dynamic)
    {$LogDirectory = $DynamicLogDirectory}

# Get the directory listing of deployed computers, and prompt to choose
if (test-path $LogDirectory)
    {
        $Computer = Get-ChildItem $LogDirectory |
            Where-Object {$_.LastWriteTime -ge (Get-Date).AddDays(-$NumberOfDays) -and $_.Name -notin ('Dynamic','Variables')} |
            Sort LastWriteTime -Descending |
            Select Name, LastWriteTime |
            Out-GridView -Title "Choose a deployed computer" -OutputMode Single |
            Select -ExpandProperty Name
    }
Else {Write-Warning "Could not access the log directory"; break}

# Get the list of log files for that computer, and prompt to choose
if ($Computer -ne $null)
    {
        $LogFile = Get-ChildItem "$LogDirectory\$Computer" -Recurse |
            Where-Object {$_.Mode -ne "d----"} |
            Select Name, @{N='Size (KB)'; E={[math]::Round(($_.Length / 1KB), 2)}}, LastWriteTime, @{N='Location'; E={$_.FullName}} |
            Sort Name |
            Out-GridView -Title "Choose a log file" -OutputMode Single |
            Select -ExpandProperty Location

        # Search the log file for entries of type 2 or 3
        $Entries = Select-String -Path "$LogFile" -Pattern "type=""(2|3)"""
    }
Else {break}

if ($Entries -eq $Null)
    {Write-Warning "No error entries found."; break}
Else
    {
        # Output each log entry to a temporary log file and invoke it
        $x = -1
        foreach ($Line in $Entries)
            {
                $x ++
                $Entries[$x].Line | Out-File "$env:TEMP\OSDerrors.log" -Append
            }
        Invoke-Item "$env:TEMP\OSDerrors.log"

        # Wait for CMTrace to start
        do
            {Start-Sleep -Seconds 2}
        until ((Get-Process CMTrace -ErrorAction Ignore) -ne $null)

        # wait until CMTrace is closed, then delete the temporary log file
        do
            {
                start-sleep -Seconds 2
                $process = Get-process -name CMTrace -ErrorAction Ignore
            }
        until ($process -eq $null)
        Remove-Item "$env:TEMP\OSDerrors.log"
    }

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 )

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.