[小ネタ] Windows Update の実行結果(success/fail)をPowerShellでシンプルに取得する

  • 8
    Like
  • 0
    Comment

Windows Serverの月例パッチ当て問題

  • ご担当者の皆様、いつも本当におつかれさまです。
  • パッチ当ての自動化をやってる所は多いと思いますが、Windows Updateの実行結果をPowerShellでシンプルに拾って色々捗らせたい (例) 失敗したホストをSlackに通知

そんな時に役立ちそうな方法を見つけたのでメモ。

"win32_reliabilityRecords" vs "Windows Update Agent COM object"

普通に調べると、win32_reliabilityRecordsを使ってWindowsUpdateの結果(success/fail)を取得する参考がたくさん出てきます。

  • win32_reliabilityRecordsを使って失敗したWindowsUpdate一覧を取得するPowerShellの例
Get-WmiObject -Class win32_reliabilityRecords -filter "sourcename = 'Microsoft-Windows-WindowsUpdateClient'" `
| where {$_.message -match 'failure'} `
| select @{LABEL = "date"; EXPRESSION = {$_.ConvertToDateTime($_.timegenerated)}},@{LABEL = "failed updates"; EXPRESSION = {$_.productname}} `
| Format-Table -AutoSize Wrap

ただこの方法には少々問題がありまして、対象のホストで"reliability WMI provider"が有効になっていないといけません。
全ホストにそんなことやってられない…

そこで、COM objectを使ってWindowsUpdateの結果を取得します。
この方法であれば古いWindows Server2008とかでも特に何か下準備をすることなく使えると思います。

CheckWindowsUpdateResult.ps1
$Session = New-Object -ComObject Microsoft.Update.Session
$Searcher = $Session.CreateUpdateSearcher()
$HistoryCount = $Searcher.GetTotalHistoryCount()
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa386532%28v=vs.85%29.aspx
$Searcher.QueryHistory(0,$HistoryCount) | ForEach-Object -Process {
    $Title = $null
    if($_.Title -match "\(KB\d{6,7}\)"){
        # Split returns an array of strings
        $Title = ($_.Title -split '.*\((KB\d{6,7})\)')[1]
    }else{
        $Title = $_.Title
    }
    # http://msdn.microsoft.com/en-us/library/windows/desktop/aa387095%28v=vs.85%29.aspx
    $Result = $null
    Switch ($_.ResultCode)
    {
        0 { $Result = 'NotStarted' }
        1 { $Result = 'InProgress' }
        2 { $Result = 'Succeeded' }
        3 { $Result = 'SucceededWithErrors' }
        4 { $Result = 'Failed' }
        5 { $Result = 'Aborted' }
        default { $Result = $_ }
    }
    New-Object -TypeName PSObject -Property @{
        InstalledOn = Get-Date -Date $_.Date;
        Title = $Title;
        Name = $_.Title;
        Status = $Result
    }

} | Sort-Object -Descending:$true -Property InstalledOn |
Select-Object -Property * -ExcludeProperty Name | Format-Table -AutoSize -Wrap

尚、このままだと遡って過去の物も一気に取得してしまうので、
必要に応じて当月分だけを抜き出してあげるようにすれば便利になると思います。

これをベースにすれば簡単に失敗を検知できますね。

出典