Get-WindowsUpdateLog…If it were only that easy….


Remember the good old days, when there was a simple text file that you could look at to see what was going on with your Windows Update processes?  Yep...the good old days...life was so simple.

Starting with Windows 10 and Windows Server 2016 the process has changed considerably.  If you view the WindowsUpdate.log file that is located in the C:\Windows directory, you are greeted with the following text:

Windows Update logs are now generated using ETW (Event Tracing for Windows).
Please run the Get-WindowsUpdateLog PowerShell command to convert ETW traces into a readable WindowsUpdate.log.

For more information, please visit http://go.microsoft.com/fwlink/?LinkId=518345

The link specified above describes the reasoning for modifying this process and the general guidelines for generating and viewing the WindowsUpdate.log file. While I'm in favor of better performance and using less disk space, this new process takes some getting used to.  To quote the ever-wise REO Speedwagon, "you got to roll with the changes".  And roll we will.

The reason that I am creating this particular blog post is that I recently  had to analyze the WindowsUpdate.log files from multiple machines, which was a challenge when you consider the amount of work that needs to be done to get the correct Symbols in place and the burden of running the command locally on each machine.  I didn't do that too many times before the PowerShell light bulb lit up in my head to automate the process of gathering the logs from remote machines.  In my case, it was much easier to generate them from the remote machine and copy the files locally where I have everything installed that I need to work with the WindowsUpdate.log files.

Prerequisites

As noted in the top portion of the script, there are a few prerequisites that need to be in place on the machine that is running the script:

  • TraceFMT.exe installed locally, which can be downloaded from the following sites:

WDK Download site:  https://developer.microsoft.com/en-us/windows/hardware/windows-driver-kit

SDK Download Site:   https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk

In my experience, TraceFMT.exe has worked more reliably to convert the files than the native Get-WindowsUpdateLog cmdlet.

  • Windows Symbols either locally stored on the computer or on a network file share.  Symbols are Windows Version specific, so make sure that you have the correct version downloaded for the Operating System version to match the system you are pulling the Windows Update logs from.  Symbols can be downloaded from:

Symbols Download Site:  https://developer.microsoft.com/en-us/windows/hardware/download-symbols

  • Create a folder to serve as the base, top-level folder for the collection of the logs.  The script will create a sub-folder with the name of the remote machine to hold all of the collected files.

General Script Flow

  • Connects remotely to a remote machine and executes the Get-WindowsUpdateLog cmdLet against it.
  • Copies the resulting WindowsUpdate.*.etl files to your "Work Directory", which is just a local folder or file share location to use as a base directory.
  • Organizes the ETL files into individual folders by date.
  • Breaks the list of ETL files from each directory into groups of 15.
  • TraceFMT.exe  generates the WindowsUpdate.log file.
  • Generates a separate WindowsUpdateX.log for each group of 15 ETL files until all ETL files are processed.

Expected Output

After the script has completed, there will be a folder for the machine, with sub-folders for each date.  The date folders will have all of the ETL files for that date. When there are more than 15 files, the first 15 files are processed into WindowsUpdate1.log, the second group of 15 into WindowsUpdate2.log, etc.

Script Syntax

WindowsUpdateLogs.ps1 -Computer "PCNameHere" -WorkDir "C:\Work" <-SymbolPath "X:\SymbolPathHere"> <-TraceFMTPath X:\TraceFMTEXEPathHere>

Download

Download the script from the link below:

WindowsUpdateLogs.ps1

NOTE:  If you copy the text below, the ScriptBlock line on line #135 may be incomplete due to the text wrap on that line.  Use the download link above instead or make sure that line is corrected before running the script for the first time.

###############################################################################################
# 
# The sample scripts are not supported under any Microsoft standard support 
# program or service. The sample scripts are provided AS IS without warranty 
# of any kind. Microsoft further disclaims all implied warranties including, without 
# limitation, any implied warranties of merchantability or of fitness for a particular 
# purpose. The entire risk arising out of the use or performance of the sample scripts 
# and documentation remains with you. In no event shall Microsoft, its authors, or 
# anyone else involved in the creation, production, or delivery of the scripts be liable 
# for any damages whatsoever (including, without limitation, damages for loss of business 
# profits, business interruption, loss of business information, or other pecuniary loss) 
# arising out of the use of or inability to use the sample scripts or documentation, 
# even if Microsoft has been advised of the possibility of such damages
#
###############################################################################################
#
# THIS SCRIPT WILL ALLOW A CENTRALIZED ADMIN WORKSTATION TO REMOTELY GENERATE 
# WINDOWSUPDATE.LOG ETL FILES AND WILL COPY THEM TO A LOCAL LOCATION FOR ANALYSIS.
# IT PLACES THE ETL FILES IN SUBDIRECTORIES ORGANIZED BY DATE AND UTILIZES TRACEFMT.EXE
# TO GENERATE THE FINAL WINDOWSUPDATE.LOG FILE. 
#
# PREREQUISITES:
# ============================================================================================
# 1. TRACEFMT.EXE (PART OF THE WINDOWS WDK AND SDK)
# WDK Download site: https://developer.microsoft.com/en-us/windows/hardware/windows-driver-kit
# DK Download Site: https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk
# 2. WINDOWS SYMBOLS
# SYMBOLS (FOR EACH SPECIFIC OS BEING ANALYZED)
# SYMBOLS DOWNLOAD: https://developer.microsoft.com/en-us/windows/hardware/download-symbols
#
# 3. CREATE A FOLDER TO BE USED AS A BASE FOR STORAGE OF ALL FILES PULLED FROM REMOTE MACHINES
#
#
# SYNTAX/USAGE:
# ============================================================================================
# WindowsUpdateLogs.ps1 -Computer "SomePC1" -WorkDir "C:\Work" <-SymbolPath "X:\SymbolPathHere"> 
#                       <-TraceFMTPath X:\TraceFMTEXEPathHere>

Param (
[Parameter(Mandatory=$true)][string]$Computer,
[Parameter(Mandatory=$true)][string]$WorkDir,
[string]$SymbolPath = "C:\Symbols",
[string]$TraceFMTPath = "C:\Program Files (x86)\Windows Kits\10\bin\x86"
)

# VERIFY THAT THE $WorkDir PATH SPECIFIED EXISTS
If (!(Test-Path $WorkDir))
{
    Write-Output "$WorkDir path not found, exiting script"
    Exit 99
}

# VERIFY THAT THE PATH SPECIFIED CONTAINS THE TRACEFMT.EXE TOOL SINCE IT IS REQUIRED
If (!(Test-Path $TraceFMTPath\TraceFmt.exe))
{
    Write-Output "TraceFmt.exe not found at $TraceFMTPath, exiting script"
    Write-Output "TraceFmt.exe is part of the Windows Driver Kit and Software Development Kit"
    Write-Output "WDK Download: https://developer.microsoft.com/en-us/windows/hardware/windows-driver-kit" 
    Write-Output "SDK Download: https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk"
    Exit 99
}

# VERIFY LOCATION OF SYMBOLS DIRECTORY (REQUIRED)
If (!(Test-Path $SymbolPath))
{
    Write-Output "Symbols not found at $SymbolPath, exiting script"
    Write-Output "If you need to download symbols, they are available at:"
    Write-Output "https://developer.microsoft.com/en-us/windows/hardware/download-symbols"
    Write-Output "========================================================================================="
    Write-Output "NOTE: Symbols must match the OS version that you are pulling the Windows Update logs from"
    Write-Output "========================================================================================="
    Exit 99
}

# SET UP SESSION ON REMOTE COMPUTER
$Sess = New-PSSession -ComputerName $Computer
Enter-pssession $Sess

# RUN THE COMMAND TO GENERATE THE WINDOWS UPDATE LOGS ON THE REMOTE COMPUTER
Invoke-Command -Session $Sess -ScriptBlock {Get-WindowsUpdateLog}

# EXIT PSSESSION 
Exit-PSSession

# SET UP A TARGET DIR TO STORE FILES
$strTargetDir = "$WorkDir\$Computer"
[array]$arrWULogs = Get-ChildItem "\\$Computer\c$\WINDOWS\logs\WindowsUpdate\WindowsUpdate*etl"
ForEach ($log in $arrWULogs)
{
    $FileDate = $log.LastWriteTime.date
    $strFileDate = $FileDate.ToString("MMddyyyy")
    $strNewDir = "$strTargetDir\$strFileDate"
    If (Test-Path $strNewDir)
    {
        Copy-Item $log $strNewDir
    }
    Else
    {
        New-Item -ItemType Directory $strNewDir
        Copy-Item $log $strNewDir 
    }
}

# GET ALL OF THE DATE DIRECTORIES CREATED
[array]$arrDateDirs = Get-ChildItem -Directory $strTargetDir

# GO THROUGH EACH DIRECTORY AND GENERATE THE WINDOWSUPDATE.LOG
ForEach ($DateDir in $arrDateDirs)
{
    $strFullName = $DateDir.FullName
    [array]$arrETLs = Get-ChildItem "$strFullName\WindowsUpdate*.etl"
    [int]$ETLCount = $arrETLs.Count
    # SINCE TRACEFMT.EXE CAN ONLY HANDLE 15 FILES AT A TIME, BREAK LIST UP INTO GROUPS OF 15
    # ROUNDING UP TO NEXT WHOLE NUMBER (16 WOULD BE 2 GROUPS, 46 WOULD BE 4 GROUPS, ETC.)
    [single]$NumGroups = [math]::Ceiling($ETLCount/15)
    # INITIALIZE COUNTERS
    [int]$intGrpCtr = 1
    [int]$intETLCtr = 0
    # PROCESS THE GROUPS OF 15 ETL FILES NOW
    While ($intGrpCtr -le $NumGroups)
    {
        New-Variable -Name "tmpVar$intGrpCtr"
        $tmpVar = Get-Variable -name "tmpVar$intGrpCtr"
        # TAKE EACH GROUP OF 15 THROUGH TRACEFMT NOW
        While (($intETLCtr -lt (15 * $intGrpCtr)) -and ($intETLCtr -lt $ETLCount))
        { 
            $curETL = ($arrETLs[$intETLCtr]).Name
            $tmpVar.Value += "$CurETL "
            $intETLCtr++
        }
        # DO THE TRACEFMT COMMAND NOW WITH TMPVAR HOLDING THE 15 ETLS IN THE LIST
        Set-Location $DateDir.FullName
        [string]$strLogList = $tmpvar.Value
        # CREATE SCRIPTBLOCK TO EXECUTE THE COMMAND
        $sbTrace = $ExecutionContext.InvokeCommand.NewScriptBlock("$TraceFMTPath\TraceFmt.exe -o .\windowsupate$intGrpCtr.log $strLogList -r $SymbolPath")
        Invoke-Command -ScriptBlock $sbTrace
        # CLEAR ALL USED VARIABLES HERE SO THEY DON'T CAUSE ISSUES NEXT TIME THROUGH THE LOOP
        Remove-Variable -name "tmpVar$intGrpCtr"
        $intGrpCtr++
        $tmpVar = $null
    }
}


This just in...

And, almost like clockwork, we released an announcement that the process will be changing in version 1709 of Windows 10.  Until then, keep the script in mind if you have a need to check out the WindowsUpdate.log on machines.

https://blogs.technet.microsoft.com/mniehaus/2017/10/10/improved-windows-update-log-formatting-with-windows-10-1709/

Comments (1)

  1. Adam says:

    This script solved a very real problem for me. Thanks. The script only appears to be decoding about 1/4 of the lines but this could be due to my symbols.

Skip to main content