検索
連載

Windows 10のWindows Updateが再起動待ちになったら自動シャットダウンするスクリプト山市良のうぃんどうず日記(179)

前回は、特定のプロセスを監視して、終了後にシャットダウンするPowerShellスクリプトを紹介しました。今回もその応用で、Windows 10のWindows Updateが再起動待ちになったら自動的にシャットダウンを開始するスクリプトを紹介します。

PC用表示 関連情報
Share
Tweet
LINE
Hatena
「山市良のうぃんどうず日記」のインデックス

山市良のうぃんどうず日記

Windows 10の品質/機能更新プログラムは再起動するまで終わらない

 前回に続き、何かタスクの実行が完了した後に自動的にシャットダウンして、コンピュータを停止するように細工することで、後は放置して楽をするPowerShellスクリプトの例を紹介します。今回の監視対象は「Windows 10」のWindows Updateです。

 自動更新が有効になっている場合、少なくとも1日に1回は自動的に利用可能な(自動更新の対象の)更新プログラムがないかどうかチェックされ、見つかった場合はバックグラウンドでダウンロードとインストールが行われます。

 その後、再起動が必要な場合は(毎月のセキュリティ品質更新プログラムのためには必要です)、「アクションセンター」またはタスクバーの「通知領域」に再起動が必要である旨が通知されます。また、ユーザーが再起動の開始を指示せずに放置した場合は、アクティブ時間(既定は8:00〜17:00)外に自動的に再起動を開始するようになっています。

 これは、「設定」アプリの「Windows Update」で「更新プログラムのチェック」をクリックして更新プログラムの確認を開始した場合も同様で、更新プログラムのインストールは、再起動中の処理を経て完了します。Windows 10の新しいバージョンである機能更新プログラムをインストールした場合も同様です。

 更新プログラムのインストールを完了するまでの再起動を含めて、最終的にシャットダウンまでを自動化したいという要望があるようです。つまり、「再起動」ボタンの選択肢の他に、「再起動してシャットダウン」ボタンがあると、更新を開始してから電源をオフにするまで放置できて便利だろうということです。

 しかし、そのような機能が実装されることはなさそうです。代替手段としては「スタート」メニューの「電源」メニューなどから「更新してシャットダウン」を実行し、更新プログラムのインストールの途中段階(通常、30%完了の時点)で電源をオフにする方法があります(画面1)。

画面1
画面1 アクティブ時間外に自動的に再起動させずに、安全に電源をオフにしたい場合は、「更新してシャットダウン」を選択して終了する

 今回は、Windows Updateの更新プログラムのインストールを監視し、再起動待ちの状態を検出して自動的なシャットダウンを開始するスクリプトに挑戦します。Windows Updateを開始して放置したまま、アクティブ時間外の自動的な再起動が開始する前に、安全に電源をオフにしたいときに利用することを想定したものです。

Windows 10 バージョン1709以降の「WindowsUpdateProvider」モジュールを活用

 目的のスクリプトを作成するために、今回はWindows 10 バージョン1709以降と「Windows Server, version 1709」以降の「Windows PowerShell 5.1」および「PowerShell Core 7」(PowerShell Core 6は未確認)で利用可能な「WindowsUpdateProvider」モジュールを利用します。

 「WindowsUpdateProvider」モジュールに含まれるコマンドレット(実際にはFunction)は、「Get-Command -Module WindowsUpdateProvider」で確認できます。また、本連載第145回と筆者の別の連載でも紹介しています。

 作成したスクリプト「stoppcafterwu.ps1」を簡単に解説すると、「WindowsUpdateProvider」モジュールの「Get-WUIsPendingReboot」を30秒ごと実行し、「True」が返ってきたら“再起動待ち状態”と判断して、自動的にシャットダウンを開始するというものです。

Write-Host "System Information"
$cpuinfo = (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment").PROCESSOR_ARCHITECTURE
$wininfo = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion")
Write-Host "  Processor Architecture:" $cpuinfo
Write-Host "  Product Name:" $wininfo.ProductName
Write-Host -NoNewLine "  Version (Build): "$wininfo.ReleaseId
Write-Host -NoNewLine " ("
Write-Host -NoNewLine $wininfo.CurrentBuild
Write-Host -NoNewLine "."
Write-Host -NoNewLine $wininfo.UBR
Write-Host ")"
Write-Host ""
Import-Module WindowsUpdateProvider -ErrorAction SilentlyContinue 
$checkmodule = (Get-Module WindowsUpdateProvider)
if ($checkmodule -eq $null) { 
  Write-Host -ForegroundColor "Red" "This script requires Windows 10/Server version 1709 or later."
  exit 
}
Write-Host "Windows Update Status"
Write-Host "  Last Check date: "(Get-WULastScanSuccessDate).DateTime
Write-Host "  Last Installation date: "(Get-WULastInstallationDate).DateTime
Write-Host ""
if (Get-WUIsPendingReboot) {
  Write-Host -ForegroundColor "Red" "This computer is awaiting reboot. You can reboot or shutdown this computer immediately. This script doesn't do anything."
  Write-Host "Open Windows Update Settings ..."
  start-process ms-settings:windowsupdate
  exit
}
Write-Host "Open Windows Update Settings ..."
usoclient StartScan
start-process ms-settings:windowsupdate
Write-Host -NoNewLine "Waiting for Windows Update to reboot (Press Ctrl + C to cancel) "
while(1) {
  if (Get-WUIsPendingReboot) {
    $procid = (Get-Process -ProcessName "TiWorker" -ErrorAction SilentlyContinue).Id
    if ($procid.Length -eq 0) {
      Write-Host ""
      Write-Host ""
      Write-Host -ForegroundColor "Red" "State detected pending reboot"
      break
    } else {
	Write-Host -NoNewLine "*"
    }
  } else {
	Write-Host -NoNewLine "."
  } 
  Start-Sleep -seconds 10
}
Write-Host ""
Write-Host -ForegroundColor "Red" "Start shutdown this host..."
Write-Host -ForegroundColor "Yellow" "Press Ctrl + C within 30 seconds to cancel shutdown"
TIMEOUT 30
Stop-Computer -Force
▲「stoppcafterwu.ps1」

 このスクリプトを実行するには、実行ポリシーでスクリプトの実行が許可されている場合(管理者として開いたPowerShellウィンドウで「Set-ExecutionPolicy RemoteSigned」を実行する)、PowerShellウィンドウ(管理者権限は不要)でスクリプトのファイル名を入力して実行します。以下はスクリプトがカレントディレクトリに存在する場合の例です。

.\stoppcafterwu.ps1

 また、以下のように、PowerShell(Windows PowerShellまたはPowerShell Core)のコマンドライン引数として、実行ポリシーを指定して実行することもできます。「ファイル名を指定して実行」、コマンドプロンプト、またはPowerShellウィンドウ(いずれも管理者権限は不要)で以下のコマンドラインを実行します。以下はスクリプトがカレントディレクトリに存在し、Windows PowerShell(powershell.exe)を使用する場合の例です。PowerShell Core 7の場合は、「powershell.exe」の部分を「pwsh.exe」に置き換えてください。

powershell.exe -ExecutionPolicy RemoteSined .\stoppcafterwu.ps1

 スクリプトの前半には、Windowsの製品名、アーキテクチャ(32bit「x86」か64bit「AMD64」か)、バージョン、ビルドの情報、Windows Updateの前回の更新プログラムを確認した日時、前回の更新プログラムのインストール成功日時の情報をレジストリから取得して表示し、「WindowsUpdateProvider」モジュールが利用可能であるかどうかをチェックしています。他にも応用できるので、ぜひ参考にしてください。

 スクリプトを開始したときに再起動待ちでない状態を検出すると、「設定」アプリの「Windows Update」を開いて、30秒ごとに監視を続けます(画面2)。「設定」アプリの「Windows Update」では、一定時間以上、自動的な更新プログラムの確認が行われていない場合は自動更新による更新プログラムの確認が始まります。

画面2
画面2 「stoppcafterwu.ps1」は更新プログラムのインストールで再起動が必要になるまで30秒ごとに監視する

 「更新プログラムのチェック」ボタンをクリックしたのと同じ動作、つまり手動で更新プログラムの確認を開始したい場合は、2つ目の「start-process ms-settings:windowsupdate」の行を「start-process ms-settings:windowsupdate-action」に書き換えてください。

 Windows Updateのダウンロードとインストールが進み、再起動待ちの状態になると、その状態を検出し、さらに30秒待ってから(シャットダウンを中止する猶予のため)、コンピュータのシャットダウンを開始します(画面3)。

画面3
画面3 再起動待ちの状態になると、さらに30秒後に自動的にシャットダウン、つまり更新してシャットダウンを開始する

 これは、「スタート」メニューから「更新してシャットダウン」を選択して電源をオフにするのと同じことです。再起動を要求する更新プログラムが複数ある場合や、多数の更新プログラムが同時にインストール対象となっている場合の「再起動の保留中」状態の発生を想定し、「TiWorker」(Windows Modules Installer Worker)プロセスが動作中の間はシャットダウンを開始しないようにしています(再起動保留状態を検出すると「.」を「*」表示に切り替えます)。これには、前回のプロセス監視のテクニックを利用しています。

 また、スクリプトの開始時に既に再起動待ちの状態になっている場合は、このスクリプトでは再起動させずに、「設定」アプリの「Windows Update」を開くようにしています(画面4)。このとき、再起動待ちの状態の場合もあれば、複数の更新プログラムのインストール途中で、そのうち1つ以上が再起動を保留している場合もあります。

画面4
画面4 スクリプトの開始時に既に再起動待ちの状態の場合は、「設定」アプリの「Windows Update」を開いて終了する

 なお、このスクリプトは自動または手動で更新プログラムの確認が行われ、再起動を要求する更新プログラムのダウンロードとインストールが行われることを想定しており、確認の結果、再起動を要求する更新プログラムがなかった場合でも監視を停止しません。監視を停止するには、[Ctrl]+[C]キーを押してスクリプトを停止してください。

 スクリプトの作成や実行が面倒なら、「設定」アプリの「Windows Update」で更新を開始してからPowerShellウィンドウ(管理者権限は不要)を開き、次の1行のコマンドラインを実行するだけでも同様のことが行えます(画面5)。

while(1){if(Get-WUIsPendingReboot){Stop-Computer -Force} else {Write-Host -NoNewLine "."; sleep 30}}
画面5
画面5 Windows Update後の再起動を効率化するために、PowerShellウィンドウを開いて1行のコマンドラインを実行しておく

 シャットダウンではなく、再起動させたいなら、「Stop-Computer」を「Restart-Computer」に置き換えてください。筆者は毎月、多数のWindows仮想マシンの更新作業を効率化するために、Windows 10 バージョン1709以降について最近はこのテクニックを利用して再起動待ちという時間のロスを最小化しています。

 このテクニックは、単一の更新プログラムの検出とインストールの場合にうまく機能するはずです。複数の更新プログラムのインストール中に実行すると、インストール途中でシャットダウンまたは再起動を開始してしまう可能性があるので注意してください(スクリプトではその点を考慮しています)。

更新プログラムのダウンロードとインストールを全てスクリプト化する

 前出の別の連載記事では、「WindowsUpdateProvider」モジュールのコマンドレットを利用した3行のコマンドラインで、更新プログラムの確認、ダウンロードとインストール、再起動までを記述したスクリプトを紹介しています(本連載第145回はそれをさらに充実させたスクリプトを紹介しています)。

 「WindowsUpdateProvider」モジュールを利用すれば、Windows Updateの自動更新や「設定」アプリの「Windows Update」のGUIを使用せずに、完全にスクリプト化することが可能です。そのとき紹介したスクリプトの「Restart-Computer」を「Stop-Computer」に置き換えたものが、以下のスクリプト「wuandstoppc.ps1」です。「Start-WUScan」がオプションの更新プログラムを検出したりしないように、検索条件(-SearchCriteria)を追加するという修正も加えています。

$updates = (Start-WUScan -SearchCriteria "IsInstalled=0 and Type='Software' and AutoSelectOnWebsites=1")
if ($updates.Count -gt 0) {Install-WUUpdates -Updates $updates} else {Write-Host "No update"}
if (Get-WUIsPendingReboot) {Stop-Computer -Force}
▲「wuandstoppc.ps1」

 Windows Updateの自動更新を無効化している場合や、Windows ServerのServer Coreインストールの場合は、こちらを利用することもできます(画面6)。

画面6
画面6 「WindowsUpdateProvider」モジュールを利用すれば、Windows Updateを完全にスクリプト化できる

 なお、このスクリプトは自動配布対象の定例(日本では第2火曜日の翌日)および定例外(緊急)のセキュリティ更新プログラム、「Windows Defender Antivirus」のセキュリティインテリジェンス更新プログラムのインストールを想定しており、オプションの更新プログラムは検出しません。

 本連載第145回でも説明しましたが、Windows 10では「Start-WUScan」が「Start-WUScan : Scan hit error: @{PSComputerName=}.ReturnValue」というエラーを返して失敗する、または利用可能な更新プログラムがあるのに検出せずに終了することがあります。

 その原因を筆者はいまだに見つけることができていませんし、現状、「Start-WUScan」などに対応したオンラインヘルプも存在しません。そこで今回の新しいスクリプト「stoppcafterwu.ps1」を作成したわけです。「Start-WUScan」を使用しない、自動更新または「設定」アプリの「Windows Update」のGUIと「WindowsUpdateProvider」モジュールの合わせ技ということです。

残念なお知らせ、Windows 10の最新バージョンではこのテクニックが使えなくなる

 最後に残念なお知らせがあります。「Windows 10 May 2020 Update(バージョン2004)」および「Windows Server, version 2004」では、「WindowsUpdateProvider」モジュールは期待通りには機能しないことをお伝えしないといけません。

 「Visual Studioサブスクリプション」(旧称、MSDNサブスクリプション)向けに2020年5月12日(米国時間)に提供された最新バージョンのISOイメージで確認したところ、このバージョンのWindows 10の新規インストールには「WindowsUpdateProvider」モジュールは含まれませんでした。

 Windows 10 バージョン1909以前からアップグレードした場合、モジュールは残るようですが、コマンドレットを実行しても「プロバイダーによる読み込みエラーです」というエラーを出力して終了します。

 どうやら「WindowsUpdateProvider」モジュールが依存する「Windows Update WMIプロバイダー」(名前空間root/Microsoft/Windows/WindowsUpdate)がWindows 10 バージョン2004で廃止されたようなのです。「Start-WUScan」の原因不明の問題解決は、予告なしの機能の廃止(通常は非推奨になってから廃止)によって達せられたということになります。

 期待通りに動くものかどうかまだテストが不十分ですが、スクリプトの「Get-WUIsPendingReboot」を次のコードに置き換えることができると思います(画面7)。

(Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending") -and (Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired")
画面7
画面7 「WindowsUpdateProvider」モジュールは、Windows 10 バージョン2004以降で利用できなくなると思われる。それに備えて「Get-WUIsPendingReboot」の代替コードをテスト中

 今回紹介したスクリプト「stoppcafterwu.ps1」と「wuandstoppc.ps1」で利用した「WindowsUpdateProvider」モジュールに関するその他のコマンドレットの代替については、関連する過去の連載記事などを参考に工夫してみてください。また、「stoppcafterwu.ps1」については、「WindowsUpdateProvider」モジュールのインポートとチェックの部分も削除する必要があります。

補足(2020年5月27日追記)

 以下のサポート情報で説明されているように、Windows 10の高速スタートアップ機能の影響で、更新後の再起動待ちの状態からシャットダウンを実行すると、完全にシャットダウンされずに休止状態に入り、更新プログラムの保留中のインストール処理が行われないという既知の問題があります。

 本稿のスクリプト内で使用している「Stop-Computer -Force」や、これと同等の「shutdown /s /t 0」コマンドは完全シャットダウンを開始するものであり、この問題の影響は受けません。

 ちなみに、高速スタートアップを準備しながらのシャットダウンを実行するコマンドライン操作は、「shutdown /s /hybrid /t 0」になります。


筆者紹介

山市 良(やまいち りょう)

岩手県花巻市在住。Microsoft MVP:Cloud and Datacenter Management(2019-2020)。SIer、IT出版社、中堅企業のシステム管理者を経て、フリーのテクニカルライターに。Microsoft製品、テクノロジーを中心に、IT雑誌、Webサイトへの記事の寄稿、ドキュメント作成、事例取材などを手掛ける。個人ブログは『山市良のえぬなんとかわーるど』。近著は『Windows版Docker&Windowsコンテナーテクノロジ入門』(日経BP社)、『ITプロフェッショナル向けWindowsトラブル解決 コマンド&テクニック集』(日経BP社)。


Copyright © ITmedia, Inc. All Rights Reserved.

ページトップに戻る