かつて山市良と呼ばれたおじさんのブログ

セイテクエンジニアのブログ  かつて山市良と呼ばれたおじさんのブログ  メモ. 再起動が完了するまでがWindows Updateのインストール

 

 

メモ. 再起動が完了するまでがWindows Updateのインストール

2024年05月08日配信
2024年06月18日更新
執筆者:山内 和朗

 “家に帰るまでが遠足です。”とは、誰もが幼少の頃からよく耳にしたフレーズです。気を抜いていしまうと、帰宅途中に思わぬ事故に巻き込まれるかもしれません。Windows Updateも同じです。毎月のセキュリティ更新プログラムのインストールが終わり、「今すぐ再起動する」をクリックして終わった気にならないでください。最悪の場合、二度と戻ってこないことだってあります。再起動を要求する更新プログラムのインストールが終わったことを知るのは、再起動が完了したときです。

 

これまでの私の更新自動化スクリプトで気付いたこと

 

 私はこれまで、Windows Updateを如何に自動化できるのか、サンプルスクリプトや自作スクリプトを活用して、さまざまな場所(Webメディアや記事)で紹介してきました。山市某として書いた、弊社の以下の記事もその1つです。

Windowsの運用管理を快適にする10の裏ワザ/表ワザ|テクニック.2 - Windows Updateはお任せではなく、戦略的に

 この記事では、Microsoft提供のWSH(Windows Script Host)スクリプトのサンプル「WUA_SearchDownloadInstall.vbs」や、Windows Update Agent(WUA) APIを利用した更新履歴を取得する自作スクリプト「Get-WUHistory.vbs」を紹介しています。前者のスクリプトを活用すれば、更新プログラムのチェックから、ダウンロード、インストール、そして再起動までを自動化することができます。後者のスクリプトを利用すれば、Windows Updateの更新の履歴と同等(注:まったく同じではありません)の情報を出力画面やテキストファイル(標準出力をファイルにリダイレクト)として取得することができます。

 両者をうまく組み合わせれば、更新プログラムのチェックから、ダウンロード、インストール、再起動、そしてインストールの結果までを取得できるのではないかとハタと気付きました。その組み合わせや、スケジュール実行、実行結果の確認には、弊社製品「Job Director」が使えそうだと考えました。

新たなサンプル「get-wustatus.ps1」、まだ試作段階だけど...

 

 更新プログラムのチェックから、ダウンロード、インストール、再起動、そしてインストールの結果までの一連の情報取得については、まだまだ研究中ですが、更新プログラムの直近のインストール結果の取得について先に検討してみました。

 次に示すPowerShellスクリプトのサンプル「get-wustatus.ps1」は、更新履歴を取得する自作スクリプト「Get-WUHistory.vbs」をカスタマイズして作成したものです。

 

[get-wustatus.ps1]

$localtime = 9 # UTF+$localtime
$objSession = new-object -com "Microsoft.Update.Session"
$objSearcher = $objSession.CreateupdateSearcher()
$intCount = $objSearcher.GetTotalHistoryCount()
$colHistory = $objSearcher.QueryHistory(0, $intCount)
$wustatus = 0
$additionalmsg = ""
$lastwudate = Get-Date    #localtime
if ($intCount -gt 0) {
  $lastwudate = ($colHistory[0].Date).AddHours($localtime)
  if (((Get-Date) - $lastwudate).Days -gt 30) {
    $wustatus = 1
    $additionalmsg = "[Warning: this system has not been updated for more than 30 days.]"
  }
}

foreach ($objHistory in $colHistory)
{
  $wudate = ($objHistory.Date).AddHours($localtime)
  #if (($lastwudate - $wudate).Days -eq 0) {
  if ((get-date($wudate) -Format "yyyyMMdd") -eq (get-date($lastwudate) -Format "yyyyMMdd")) {
    if ($objHistory.HResult -eq 0) {
      Write-Host ($wudate.ToString("yyyy/MM/dd hh:mm,")) $objHistory.Title ", Success"
    } elseif ($objHistory.HResult -eq -2145116140) {
      Write-Host ($wudate.ToString("yyyy/MM/dd hh:mm,")) $objHistory.Title ", Success (pending reboot)"
      $additionalmsg = "[Warning: this system is pending reboot.]"
    } else {
      Write-Host ($wudate.ToString("yyyy/MM/dd hh:mm,")) $objHistory.Title ", Failed (error:"$objHistory.HResult.ToString("X8")")"
      $wustatus = 1
    }
  }
}

Write-Host ""
Write-Host $additionalmsg
exit $wustatus

 

 この新しいサンプルスクリプトでは、更新プログラムが最後にインストールされたその日に、インストールされたすべての更新プログラムのインストールの結果を取得することをまず、考えました。

 その上で、更新プログラムのインストールがすべて成功していた場合は終了(EXIT)コード「0」を返し、1つでも失敗した場合は「1」を返すようにしています。また、最後のインストールの履歴から1か月(30日)以上経過している場合、その旨の警告メッセージを表示し、終了コード「1」で終了します。また、すべて成功している状態でも、インストール後の再起動が保留されている者がある場合は、そのむめの警告メッセージを表示します。このとき、失敗した更新が無い限り、終了コードは「0」を返します。

 

 なお、このサンプルスクリプトは日本時間(UTC+9:00)を基準にしていますが、Windowsのタイムゾーンに合わせて、$localtimeを調整してください。

 

WSHがお好きな方へ、「get-wustatus.vbs」

 

 PowerShellスクリプトより、WSH(Windows Script Host)スクリプトの方が慣れている、扱いやすいという人もいるでしょう。例えば、「WUA_SearchDownloadInstall.vbs」と組み合わせたい場合は、WSHに揃えたいと思うかもしれません。というわけで、「get-wustatus.ps1」をWSHで動作するVBScriptで記述してみました。

[get-wustatus.vbs]

localtime = 9 ' UTF+$localtime
Set objSession = CreateObject("Microsoft.Update.Session")
Set objSearcher = objSession.CreateUpdateSearcher
intCount = objSearcher.GetTotalHistoryCount

Set colHistory = objSearcher.QueryHistory(0, intCount)

wustatus = 0
additionalmsg = ""
lastwudate = date() ' localtime
If intCount > 0 then
  lastwudate = DateAdd("h",localtime,colHistory(0).Date)
  If DateDiff("d",lastwudate,date()) > 30 then
    wustatus = 1
    additionalmsg = "[Warning: this system has not been updated for more than 30 days.]"
  End If
End If
For Each objHistory In colHistory
  wudate = DateAdd("h",9,objHistory.Date)
  If DateDiff("d",wudate,lastwudate) = 0 then
    If objHistory.HResult = 0 then
      WScript.Echo wudate & ", " & objHistory.Title & ", Success"
    ElseIf Hex(objHistory.HResult) = 80242014 then
      WScript.Echo wudate & ", " & objHistory.Title & ", Success (pending reboot)"
      additionalmsg = "[Warning: this system is pending reboot.]"
    Else
      WScript.Echo wudate & ", " & objHistory.Title & ", Failed (error:" & Hex(objHistory.HResult) & ")"
      wustatus = 1
    End If
  End If
Next
WScript.Echo vbNewLine & additionalmsg
Wscript.Quit(wustatus)

 

うまく動くかな?

 

 画面1~画面3は、Windows 11バージョン23H2におけるWindows Updateの複数のタイミングで「get-wustatus.ps1」と「get-wustatus.vbs」を実行してみた結果です。どうやら、期待通りに動いているようです。

画面1
画面1 前回のWindows Updateのインストールの結果

画面2
画面2 更新プログラムのインストールで再起動待ちの状態の場合の結果

画面3
画面3 前回のWindows Updateの後の再起動直後の結果

 画面4は、しばらく更新していないWindows Server 2012 R2で実行してみた結果です。こちらも期待通りに動いているようです。

画面4
画面4 前回のWindows Updateからしばらく時間が経っている(30日を超えている)場合の結果

 このサンプルスクリプト、うまく動作しているようですが、課題がないわけではありません。1つは、このブログで繰り返し扱ってきた、2024年1月以降のWinREのセキュリティ更新プログラムのインストールのエラーに関しては、今回のサンプルスクリプトでは取得できないということです(画面5)。もう1つは、日付(ローカル時間)をまたがって複数の更新プログラムのインストールが行われた場合、取得できるインストールの結果は日付を超えてからのものに限られる点です。

画面5
画面5 2024年1月以降のWinREのセキュリティ更新プログラムのインストールエラーは、今回のサンプルスクリプトでは取得できなかった

 

 Windows 10以降の「更新の履歴」は、「機能更新プログラム」「品質更新プログラム」「ドライバーの更新プログラム」「定義更新プログラム」「その他の更新プログラム」のように、更新プログラムのカテゴリ別(各カテゴリの履歴は過去最大50のようです)に表示してくれますが、カテゴリ別の履歴の取得をしたいとも思っています(現状、その方法さえ思いつきませんが...)。


 今回のサンプルスクリプト、Windows Updateの自動化プロセスの一部として考えたものですが、単体でも定期的に動かすことで、Windows Updateの状況の把握に活用できるのではないでしょうか。Windows Updateの自動化については、今回のサンプルスクリプトを含め、継続して研究(トライ&エラー)を続けます。

blog_subscribe

blog_comment

最新記事