
かつて山市良と呼ばれたおじさんのブログ
セイテクエンジニアのブログ かつて山市良と呼ばれたおじさんのブログ vol.124 WUA_SearchDownloadInstall.vbsのPowerShell化|Windowsトラブル解決
2025年07月24日配信
執筆者:山内 和朗
Microsoftが公開しているWindows Update用のサンプルスクリプト「WUA_SearchDownloadInstall.vbs」は、運用環境向けではありません。また、スクリプト実行時にアプリケーションエラーを発生させることがあることを確認しています。VBScriptも非推奨になってから間もなく2年経ちます。そこで、このスクリプトと同等の機能をPowerShell化してみました。
Microsoftは、以下のサイトでWindows Update Agent(WUA) APIを使用した更新プログラムの検索、ダウンロード、インストール用スクリプト「WUA_SearchDownloadInstall.vbs」を公開しています。
更新プログラムを検索、ダウンロード、インストールする|Windowsアプリ開発(Microsoft Learn)
このブログや製品コラムでも、スクリプトをそのまま、あるいは少しだけ改造して利用してきました。
メモ. Windows Update補完計画、フェーズ2
メモ. Windows Update補完計画、フェーズ2.1
Windows Updateの更新を全自動化-Job Director R16活用例: おじさんのブログと連動|製品コラム
さまざまなオプションスイッチやパラメーター(自動実行、検索のみ、オフラインスキャンのみ、更新ソースの指定など)を備えた、良くできた便利なスクリプトですが、「WUA_SearchDownloadInstall.vbs」の使用にはいくつか問題があります。それは、スクリプト提供元のページに以下のように記されているように、運用環境向けではく、Microsoftがこのスクリプトをサポートしていない(WUA APIはサポート)ということです。
“このスクリプトは、Windows Update エージェント API の使用法を示し、開発者がこれらの API を使って問題を解決する方法の例を提供することを意図しています。 このスクリプトは運用環境コードとして意図されてません。また、スクリプト自体は Microsoft によってサポートされていません”
もう1つの問題は最近確認した問題で、このスクリプトを使用して更新プログラムのインストールが行われると、Cscriptエンジンがアプリケーションエラー「-1073741819(0xC0000005、アクセス違反)」で異常終了することがあるという問題です(画面1、画面2)。このエラーが発生しても、更新プログラムのインストールには影響せず、このエラーを理由にインストールが失敗するということはありません。しかし、Microsoftがサポートしないスクリプトである以上、この問題の原因を特定し、解消するのは難しいでしょう。
画面1 「WUA_SearchDownloadInstall.vbs」はEXITコード0(インストールなし)または1(インストールあり)で終了するが、稀にアプリケーションエラー「-1073741819(0xC0000005、アクセス違反)」を返す
画面2 「WUA_SearchDownloadInstall.vbs」が発生させたアプリケーションエラーは、イベントログや信頼性モニター(perfmon /rel)で確認できる
関連:
Windows Updateの更新を全自動、アクセス違反エラーの回避-Job Director R16活用例|製品コラム
※この製品コラムでも「WUA_SearchDownloadInstall.ps1」を紹介していますが、問題回避のために急いで作成したスクリプトであり、今回のものとは異なります。
最後の問題は、Microsoftが2023年10月に「VBScript(Microsoft Visual Basic Scripting Edition)」を非推奨機能(開発されなくなった機能)に追加したことです。現時点では、VBScriptはオンデマンド機能(オプション機能)化されただけで、既定で有効になっていますが、廃止までに次のフェーズ(2027年頃を予定)では既定で無効になる予定です。現在、VBScriptで記述されたスクリプトなどある場合は、VBScript非依存のものに移行(例えばPowerShellスクリプトへの変換)を進めるべきです。
ITニュース. VBScript廃止までのタイムライン発表、ただし廃止時期は未定
「WUA_SearchDownloadInstall.vbs」には多数のコマンドラインパラメーターとスイッチがサポートされていますが、その“一部”を実装した「WUA_SearchDownloadInstall.ps1」を作成しました。少なくとも、「WUA_SearchDownloadInstall.vbs」でアプリケーションエラーが発生するのと同じ環境で、「WUA_SearchDownloadInstall.ps1」が問題なく動作することを確認しました。
「WUA_SearchDownloadInstall.ps1」で使用可能なスイッチ/パラメーターの一覧を以下に示します。
コマンドラインスイッチ/パラメーター | 説明 |
-Automate | プロンプトを表示せずに自動的に次に進む。-RebootToCompleteと同時に指定しない限り、自動的な再起動は行わない。 |
-Criteria "検索条件" | 検索条件の指定。既定(省略時)は、-Criteria "IsInstalled=0 and Type='Software' and IsHidden=0" |
-NoDownload | 更新プログラムをダウンロードしないで次へ進む。 |
-NoInstall | 更新プログラムをインストールせずに終了する。 |
-RebootToComplete | インストール後に再起動が必要な場合に自動的に再起動する |
※スキャンキャブ(wsusscn2.cab)によるオフラインスキャンについては、こちら(Microsoft Learn)にPowerShellスクリプトがあります。
コマンドラインパラメーターやスイッチを何も指定せずに実行すると、検索、ダウンロード、インストール、再起動(必要時のみ)の各段階で次に進むかプロンプトを表示して問い合わせます(画面3)。この方法のみ、個別の更新プログラムのインストールに対応する予定ですが(Sで応答時)、その実装は別のスクリプト「WUA_SearchDownloadInstallS.ps1」で行う予定です。
画面3 パラメーターやスイッチなしで実行すると、対話的に実行可能
次のように、-Automateと-RebootToCompleteスイッチを指定すると、スクリプト開始後は対話なしで最後まで進み、再起動が必要であれば自動的に再起動します(画面4)。
.\WUA_SearchDownloadInstall.ps1 -Automate -RebootToComplete |
画面4 -Automateと-RebootToCompleteパラメーターを指定すると、(60秒経過後)再起動開始まで自動的に行ってくれる
次のように、-NoDownloadと-NoInstallスイッチを指定すると、更新プログラムの検索だけを行い、一覧に表示して終了します(画面5)。
.\WUA_SearchDownloadInstall.ps1 -NoDownload -NoInstall |
画面5 -NoDownloadと-NoInstallスイッチを指定すると、利用可能な更新プログラムの検索だけを行う
[WUA_SearchDownloadInstall.ps1](プレーンテキストで表示)
param(
[switch]$Automate,
[string]$Criteria = "IsInstalled=0 and Type='Software' and IsHidden=0",
[switch]$NoDownload,
[switch]$NoInstall,
[switch]$RebootToComplete
)
$ScriptforSigleUpdatePath = ".\WUA_SearchDownloadInstallS.ps1"
function InstallationResultToText($result) {
switch ($result) {
2 { "Succeeded" }
3 { "Succeeded with errors" }
4 { "Failed" }
5 { "Cancelled" }
default { "Unexpected ($result)" }
}
}
Write-Host "### Command line parameters you specify. ###"
Write-Host "-Automate: $Automate"
Write-Host "-Criteria: $Criteria"
Write-Host "-NoDownload: $NoDownload"
Write-Host "-NoInstall: $NoInstall"
Write-Host "-RebootToComplete: $RebootToComplete"
Write-Host ""
Write-Host "### Running Windows Update ###"
Write-Host "Searching for updates..."
Write-Host ""
$updateSession = new-object -com "Microsoft.Update.Session"
$updateSearcher = $updateSession.CreateupdateSearcher()
$searchResult = $updateSearcher.Search($criteria)
Write-Host "List of applicable items on the machine:"
if ($searchResult.Updates.Count -eq 0) {
Write-Host "There are no applicable updates."
exit 0
} else {
$downloadReq = $False
$i = 0
foreach ($update in $searchResult.Updates){
$i++
if ( $update.IsDownloaded ) {
Write-Host $i">" $update.Title "(downloaded)"
} else {
$downloadReq = $true
Write-Host $i">" $update.Title "(not downloaded)"
}
}
}
Write-Host ""
if (-not $Automate ) {
$response = Read-Host "Next step: download all updates(Y) or download/install single update(S). May I continue? (Y/N/S)"
if ($response -match '^[Yy]$') {
Write-Host "Proceed to the next step: download all updates."
} elseif ($response -match '^[Ss]$') {
Write-Host "Proceed to the next step: download/install single update."
If (Test-Path $ScriptforSigleUpdatePath) {
. $ScriptforSigleUpdatePath
exit $LASTEXITCODE
} else {
Write-Host "$ScriptforSigleUpdatePath not found."
exit 0
}
} else {
Write-Host "Aborted."
exit 0
}
} else {
Write-Host "Proceed to the net step: download updates. (-Automate)"
}
if ( $NoDownload ) {
Write-Host "Skip downloads. (-NoDownload)"
} elseif ( $downloadReq ) {
Write-Host "Creating collection of updates to download..."
$updatesToDownload = new-object -com "Microsoft.Update.UpdateColl"
foreach ($update in $searchResult.Updates){
$updatesToDownload.Add($update) | out-null
}
Write-Host "Downloading updates..."
$downloader = $updateSession.CreateUpdateDownloader()
$downloader.Updates = $updatesToDownload
$downloader.Download()
Write-Host "List of downloaded updates:"
$i = 0
foreach ($update in $searchResult.Updates){
$i++
if ( $update.IsDownloaded ) {
Write-Host $i">" $update.Title "(downloaded)"
} else {
Write-Host $i">" $update.Title "(not downloaded)"
}
}
} else {
Write-Host "All updates are already downloaded."
}
Write-Host ""
if (-not $Automate ) {
$response = Read-Host "Next step: install updates. May I continue? (Y/N)"
if ($response -match '^[Yy]$') {
Write-Host "Proceed to the next step."
} else {
Write-Host "Aborted."
exit 0
}
} else {
Write-Host "Proceed to the net step: install updates. (-Automate)"
}
if ( $NoInstall ) {
Write-Host "Skip installation. (-NoInstall)"
exit 0
}
$updatesToInstall = new-object -com "Microsoft.Update.UpdateColl"
Write-Host ""
Write-Host "Creating collection of downloaded updates to install..."
foreach ($update in $searchResult.Updates){
if ( $update.IsDownloaded ) {
$updatesToInstall.Add($update) | out-null
}
}
$returnValue = 1
if ( $updatesToInstall.Count -eq 0 ) {
Write-Host "Not ready for installation."
} else {
Write-Host "Installing" $updatesToInstall.Count "updates..."
$installer = $updateSession.CreateUpdateInstaller()
$installer.Updates = $updatesToInstall
$installationResult = $installer.Install()
if ( $installationResult.ResultCode -eq 2 ) {
Write-Host "All updates installed successfully."
} else {
Write-Host "Some updates could not installed."
}
}
# $returnValue = $installationResult.ResultCode
if ( $installationResult.RebootRequired ) {
Write-Host "One or more updates are requiring reboot."
# $returnValue = $returnValue + 100
} else {
Write-Host "Finished. Reboot are not required."
}
Write-Host ""
Write-Host "Listing of updates installed and indivisual installation results:"
$i = 0
foreach ($update in $updatesToInstall){
$result = $installationResult.GetUpdateResult($i)
$i++
Write-Host $i">" $update.Title ":" $(InstallationResultToText $result.ResultCode) ", HRESULT:" $($result.HResult)
}
if ($installationResult.RebootRequired) {
if ($RebootToComplete) {
Write-Host "After 60 seconds, the system will initiate a restart automatically. To abort , press Ctrl+C. (-RebootToComplete)"
for ($i = 60; $i -gt 0; $i--) {
Write-Host "." -NoNewLine
Start-Sleep -Seconds 1
}
restart-Computer -Force
} elseif (-not $Automate) {
$response = Read-Host "Reboot is pending. Would you like to reboot? (Y/N)"
if ($response -match '^[Yy]$') {
Write-Host "Reboot now!..."
sleep -Seconds 3
restart-Computer -Force
} else {
Write-Host "You need to reboot this device later."
}
}
}
Exit $returnValue
このスクリプトは、「WUA_SearchDownloadInstall.vbs」と同じように、インストールがなければ「0」、あれば「1」のEXITコードで終了します。赤字の部分のコメントアウトを外せば(行頭の#を取れば)、「1」の代わりに「2~5(成功/失敗/中止)、102~105(保留中の再起動ありの成功/失敗/中止)を返すように変更することができ、他のジョブ管理ツール(弊社Job Directorなど)と連携が容易になります。
ご参考まで... Microsoft提供のものではありませんが、PowerShellを使用したWindows Updateについては、PowerShell Galleryで公開されているMichalGajda氏作成のPSWindowsUpdateモジュールのコマンドレットを使用する方法もあります。
https://www.powershellgallery.com/packages/PSWindowsUpdate/