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

セイテクエンジニアのブログ  かつて山市良と呼ばれたおじさんのブログ  メモ. Windows Update補完計画、フェーズ2

 

 

メモ. Windows Update補完計画、フェーズ2

2024年05月14日配信
2025年04月25日更新
執筆者:山内 和朗

 “Windows Updateを制する者はシステム管理を制す”とは、ついさっき私が適当に考えた言葉です。ですが、あながち間違いとは言えません。Windows UpdateでOSを最新状態に保つことは、セキュリティの基本のキです。一方で、トラブルも付きもの。Windows Updateを如何にしてコントロール下に置くか、それはシステム管理者の大きな課題の1つです。

 このブログでは以前、「メモ. 再起動が完了するまでがWindows Updateのインストール」と題して、Windows Updateの更新の履歴をスクリプト(PowerShellおよびWSH)で取得するサンプルを紹介しました。今回は、その第2弾として、更新プログラムの確認、ダウンロード、インストールまでを行うサンプルスクリプトを紹介します。サンプルスクリプトには更新プログラムのインストールに必要な再起動までは含めていません。起動の自動化を含めるのは簡単ですが、サーバーでの利用を想定しているからです。

 

よく変わるWindows Updateの機能と仕様、変わらないWUA API

 

 Windows 98向けのWebサイトのサービスとして始まったWindows Update、その後、Windowsの標準機能として組み込まれ、Windows 10からは毎月1回のセキュリティおよびセキュリティ以外の修正を累積した「品質更新プログラム(累積更新プログラム、Cumulative Update)」と、年に複数回(現在は1回)のOSアップグレード「機能更新プログラム(Feature Update)」が提供されるようになりました。最近では、「Windows構成の更新(Windows Configuration Update)」としてWindows 10/11に新機能を追加するのにも利用されています。


 Windows Updateのサービスやアーキテクチャにも継続的に変更が加えられています。Windows従来からの「Wuauclt.exe」クライアントと「Windows Update(wuauserv)」サービスに加えて、「UsoClient.exe」クライアントと「Update Orchestrator Service(UsoSvc)」サービスが関与するようになりましたし、更新プログラムのダウンロード技術として、「Unified Update Platform(UUP)」が採用されるようになりました。

 あまり知られていないかもしれませんが、Windows 10およびWindows Serverでは一時期(バージョン1709~1909)、Windows PowerShellに「WindowsUpdateProvider」モジュールが標準提供され、簡単なコマンドレットでWindows Updateを制御できたことがありました。現在、サポートされているWindowsバージョンでは、唯一Windows Server 2019でこのモジュールが見つかり、そして機能します(画面1)。

画面1
画面1 Windows 10ではかつて、PowerShellのWindowsUpdateProviderでWindows Updateを完全に制御できた。現在でもWindows Server 2019で利用できる

 このように時代とともに変化を遂げてきたWindows Updateですが、古くから変わらないものがあります。それは、「Windows Updateエージェント(WUA) API」です。WUA APIの使用は、古くはWindows 2000 SP3、Windows XP、Windows Server 2003から、最新のWindowsバージョンまで幅広くサポートされています。

Windows UpdateエージェントAPIの使用|更新プログラムを検索、ダウンロード、インストールする(Microsoft)

スクリプト化にはWUA_SearchDownloadInstall.vbsが便利

 

 上記のサイトでMicrosoftが公開しているWSH(Windows Script Host)のサンプルスクリプト「WUA_SearchDownloadInstall.vbs」は、WUA APIを使用して更新プログラムをスキャン、ダウンロード、インストールする方法を示すものです。このスクリプトは運用環境向けのコードとしては意図されておらず、Microsoftによってサポートされるものではありません(WUA APIはサポートされます)。


 このサンプルスクリプトについては、山市某氏が以下の記事で取り上げ、詳しく説明しています。サポートされているものではありませんが、ちゃんと動きます。そもそもMicrosoftがサポートするOSの機能としてのWindows Updateにトラブルが付きものなのですから、Windows Updateの手段が1つ増えることは嬉しいことです。

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

 例えば、このスクリプトを管理者として開いたコマンドプロンプトで次のように実行すれば、対話することなく、更新プログラムのスキャン、ダウンロード、インストールまでを実行できます(画面2)。

 

cscript WUA_SearchDownloadInstall.vbs /Automate

 

 このスクリプトはインストールが完了すると、インストールの結果(Installation Result: Succeeded|Succeeded with errors|Failed|Cancelled|Unexpected)と再起動の要否(Reboot Required: True|False)を出力します(画面2)。また、実行時に/RebootToCompleteパラメーターを付ければ、必要な場合に再起動までを行ってくれます。

画面2

画面2 サンプルスクリプト「WUA_SearchDownloadInstall.vbs」は、そのままでも更新プログラムのさまざまな処理に利用できる

 

都合よくカスタマイズしたWUA_SearchDownloadInstallv2.vbs

 

 スクリプトの内容を調べてみると、更新プログラムがインストールされなかった場合(利用可能な更新プログラムがない、/NoDownloadや/NoInstallパラメーターが使用された場合など)、終了(EXIT)コード「0」で終了します。一方、更新プログラムがインストールされた場合、その結果に関係なく終了コード「1」で終了します。終了コードは「%ERRORLEVEL%」で取得することが可能です。

 手動でスクリプトを実行する場合、その結果は画面に表示されるので良いのですが、終了コード「0」と「1」では、あまり自動化に役に立ちません。例えば、Windowsの「タスクスケジューラー」やジョブスケジューラー製品(弊社製品「Job Director」など)で制御するには情報が不足しています。せっかく、詳細な結果が画面に出力されるわけですから、少なくともインストールの結果と再起動の要否を取得できるようにしたいです。デスクトップPCなら再起動まで自動化してもよいでしょうが、常時稼働前提のサーバーをすぐに再起動してしまうわけにはいきません。再起動の必要があれば、次のメンテナンス日時(週末や夜間など)に延期したいはずです。

 というわけで、「WUA_SearchDownloadInstall.vbs」の終了コード以外の動作には影響しないように、都合よくカスタマイズして「WUA_SearchDownloadInstallv2.vbs」を作ってみました。赤色のコードが追加した部分になります。

 更新プログラムのインストールの結果は「2(Succeeded)」、「3(Succeeded with errors)」、「4(Failed)」、「5(Cancelled)」、「それ以外(Unexpected)」を人にやさしい表現にしたものです。再起動の要否は「True」または「False」で判断できます。そして、インストールされなかったときは、元々の終了コード「0」で判断できます。

 カスタマイズした内容はこうです。終了コード「0」はそのままにします。終了コード「1」の場合について、結果や再起動の要否に応じ、最終的にスクリプトの終了コードになる変数returnValueの値を書き換えます。更新プログラムがインストールされた場合は「2~5」が返ってくるはずなので、再起動が必要な場合はさらに100をプラスすることにし、「2~5」(再起動不要)または「102~105」(再起動必要)を終了コードにしています。そして、それ以外の予期せぬエラーは、そのエラーコードを終了コードにしています。

Option Explicit

WScript.Echo "This script is provided by Microsoft Corporation to demonstrate techniques that can be used to search, download,"
WScript.Echo "and install updates through the Windows Update Agent API."
WScript.Echo ""
WScript.Echo "This script is not intended as production code."
WScript.Echo ""

'(中略)

    If (strInput = "Y" or strInput = "y") Then
        WScript.Echo "Installing updates..."
        Dim installer
        Set installer = updateSession.CreateUpdateInstaller()
        installer.Updates = updatesToInstall
        Dim installationResult
        Set installationResult = installer.Install()
 
        returnValue = 1
        'Output results of install
        WScript.Echo "Installation Result: " & _
        InstallationResultToText(installationResult.ResultCode) 
        WScript.Echo "Reboot Required: " & _ 
        installationResult.RebootRequired & vbCRLF 

        WScript.Echo "Listing of updates installed " & _
        "and individual installation results:" 

        For I = 0 to updatesToInstall.Count - 1
            WScript.Echo I + 1 & "> " & _
            UpdateDescription(updatesToInstall.Item(i)) & ": " & _
            InstallationResultToText(installationResult.GetUpdateResult(I).ResultCode) & _
            " HRESULT: " & installationResult.GetUpdateResult(I).HResult
            If installationResult.GetUpdateResult(I).HResult = -2145116147 Then
                WScript.Echo "An update needed additional downloaded content. Please rerun the script."
            End If
        Next
' --- customize ---
If (returnValue > 0) Then
        returnValue = installationResult.ResultCode
        If (returnValue > 1) And (returnValue < 6) Then
          If installationResult.RebootRequired Then
            returnValue = returnValue + 100
          End If
        End If
End If

' --- customize ---
        If installationResult.RebootRequired And WScript.Arguments.Named.Exists("RebootToComplete") Then
            strInput = "Y"
            If Not isAutomated Then
                WScript.Echo  vbCRLF & "Would you like to reboot now to complete the installation? (Y/[N])"
                strInput = WScript.StdIn.Readline
            End If
            If (strInput = "Y" or strInput = "y") Then
                WScript.Echo "Committing installation..." & vbCRLF
                installer.Commit(0)
                WScript.Echo "Triggering restart in 30 seconds..." & vbCRLF
                Dim oShell
                Set oShell = WScript.CreateObject("WScript.Shell")
                oShell.Run "shutdown /r /t 30", 1
            End If     
        End If
    End If
End If

WScript.Quit (returnValue)

 いくつかのケースでテストしてみましたが、期待通りに動作しているようです(画面3、画面4、画面5)。ただし、動作を保証するものではありません。

画面3
画面3 /NoDownload、/NoInstallパラメーター使用時、終了コード「0」

画面4
画面4 /Automateパラメーター使用時、すべて成功、再起動不要で終了コード「2」、その直後の再実行時、更新なしで終了コード「0」

画面5
画面5 /Automateパラメーター使用時、すべて成功、再起動必要で終了コード「102」

 

blog_yamanxworld_subscribe

blog_yamanxworld_comment

blog_yamanxworld_WP_ws2025

最新記事