タスクスケジューラーでWindowsUpdateを実行したい

Windows更新プログラムが自動適用されると困るんで、自動適用をオフにしてます。
が、更新プログラムを適用しなくいけなくなったとき、わざわざ手動で適用するのも面倒くさいと。

タスクスケジューラーを使って夜中に自動でWindowsUpdateができるようにしたいし
実行したログも残したいと思って作りました。

ざっくり説明すると、
・Winpdate_Taskschd.bat
・WUA_SearchDownloadInstall.vbs
の2つがあって、WUA_SearchDownloadInstall.vbsが本体になってます。

Winpdate_Taskschd.batは、WUA_SearchDownloadInstall.vbsをキックして起動したり実行ログを残すため用のバッチです。

          • -

("C:\Windows\System32\ja-jp\WUA_SearchDownloadInstall.vbs"を丸パクリしてつくってます)
(丸パクリで作ってるので、自分も作ったのを公開して誰かのパクリの種になれればとうれしいです)
(使ったことで何か問題が起きても責任はとれませんのでご承知を)

・WinUdate_Taskschd.bat

@echo off

rem -------------------------------------------
rem  WinUpdate_Taskschd.bat
rem -------------------------------------------
rem このバッチは何?
rem 
rem このパッチは、Windows Updateの更新プログラムを検索・ダウンロード・インストールする
rem スクリプト(WUA_SearchDownloadInstall.vbs)をキックして起動させるためのバッチ。
rem タスクスケジューラに登録して使うときに有効なので作った。
rem -------------------------------------------
rem  使い方
rem  コマンドプロンプトを起動して、以下のコマンドを実行
rem  
rem  WinUpdate_Taskschd.bat ALL_SCH 
rem  
rem 引数に指定できる名前は、以下の6つのうちのどれか1つ
rem パターン   引数名       意味  
rem   1       ALL_SCH     すべて(ALL)の更新プログラムを検索(Search:SCH)する 
rem   2       ALL_DWL     すべて(ALL)の更新プログラムをダウンロード(Download:DWL)する  
rem   3       ALL_IST     すべて(ALL)の更新プログラムをインストール(Install:IST)する  
rem   4       IMP_SCH     重要(Important:IMP)な更新プログラムを検索(Search:SCH)する
rem   5       IMP_DWL     重要(Important:IMP)な更新プログラムをダウンロード(Download:DWL)する 
rem   6       IMP_IST     重要(Important:IMP)な更新プログラムをインストール(Install:IST)する

rem ---- プログラムここから --------------------------

rem 本バッチが置かれているディレクトリに移動
cd /d %~dp0

rem ログファイルのファイル名に使用する日時
set yyyy=%date:~0,4%
set mm=%date:~5,2%
set dd=%date:~8,2%
set time2=%time: =0%
set hh=%time2:~0,2%
set mn=%time2:~3,2%
set ss=%time2:~6,2%

rem 実行ログ
set logname=%yyyy%%mm%%dd%_%hh%%mn%%ss%_%computername%_WindowsUpdate.log

rem 変数「installtype」に引数を1つ入れる
set installtype=%1

rem 実行処理
cscript //nologo WUA_SearchDownloadInstall.vbs %installtype% >> %logname% 2>&1

rem ---- プログラムここまで --------------------------

・WUA_SearchDownloadInstall.vbs

'-------------------------------------------
' WUA_SearchDownloadInstall.vbs
'-------------------------------------------
'
' 本スクリプトの概要
'  Windowsのエージェント(WUA:WindowsUpdateAgent)から
'  Windowsのアップデートサーバーに問い合わせをして
'  更新プログラムを検索・ダウンロード・インストールするスクリプト 
'
'-------------------------------------------
'
' スクリプトの実行条件
' ・Windowsのアップデートサーバーと通信がとれていること
' ・管理者権限でスクリプトを実行していること
' 
'-------------------------------------------
'
' スクリプトの実行方法
'  手順1) コマンドプロンプトを起動する
'  手順2) 「cscript」で本ファイルを実行する
'          cscript  WUA_SearchDownloadInstall.vbs オプション
'
' オプションの種類と実行例
' 例1:すべて(ALL)の更新プログラムを検索(Search:SCH)する
' cscript  WUA_SearchDownloadInstall.vbs ALL_SCH
' 
' 例2:すべて(ALL)の更新プログラムをダウンロード(Download:DWL)する
' cscript  WUA_SearchDownloadInstall.vbs ALL_DWL
' 
' 例3:すべて(ALL)の更新プログラムをインストール(Install:IST)する
' cscript  WUA_SearchDownloadInstall.vbs ALL_IST
'
' 例4:重要(Important:IMP)な更新プログラムを検索(Search:SCH)する
' cscript  WUA_SearchDownloadInstall.vbs IMP_SCH
' 
' 例5:重要(Important:IMP)な更新プログラムをダウンロード(Download:DWL)する
' cscript  WUA_SearchDownloadInstall.vbs IMP_DWL
' 
' 例6:重要(Important:IMP)な更新プログラムをインストール(Install:IST)する
' cscript  WUA_SearchDownloadInstall.vbs IMP_IST
'
'-------------------------------------------
'
' ALL_SCH、IMP_SCH(検索)を指定した時のスクリプトの動作
'        -> 検索のみを実施。ダウンロードとインストールは実行しない
'
' ALL_DWL、IMP_DWL(ダウンロード)を指定した時のスクリプトの動作
'        -> ダウンロードの前に検索が実施される
'        -> ダウンロードのあとはインストールを実行しない
'        -> 検索で更新プログラムが見つからなければ、ダウンロード処理は実行しない。
'
' ALL_IST、IMP_INT(インストール)を指定した時のスクリプトの動作
'        -> 検索、ダウンロード、インストールの順番で実施される。
'        -> 更新プログラムのインストール後はインストール結果をログに出力する
'        -> 検索で更新プログラムが見つからなければ、ダウンロード処理もインストール処理も実行しない。
'        -> 更新プログラムのインストール後、OS再起動が必要な場合は、即座に再起動を開始する。
' 
'-------------------------------------------
' その他
'    -> 大文字小文字を区別しない。「all_sch」「All_Sch」などは「ALL_SCH」と同じ意味となる
'    -> 実行ログをファイルを残す場合は以下のようにコマンドを実行する
'        cscript WUA_SearchDownloadInstall.vbs ALL_SCH >> update.log 2>&1
'  
'-------------------------------------------
' バージョン履歴
'          yyyy/mm/dd  変更の概要
' Ver 1.0  2020/03/08  新規作成
' 
' 
'-------------------------------------------
' スクリプトの変数宣言を強制化
Option Explicit

const L_Msg01_Text = "スクリプトを開始します"
const L_Msg02_Text = "このコンピューターに対して適用できる項目を表示します:"
const L_Msg03_Text = "適用可能な更新プログラムはありません。"
const L_Msg04_Text = "スクリプトを終了します"
const L_Msg05_Text = "ダウンロード結果: "
const L_Msg06_Text = "更新プログラムをダウンロードしています..."
const L_Msg07_Text = "更新プログラムをインストールしています..."
const L_Msg08_Text = "インストールされた更新プログラムの一覧と、それぞれのインストール結果を表示します:"
const L_Msg09_Text = "インストール結果: "
const L_Msg10_Text = "インストール最終結果: "
const L_Msg11_Text = "Windows Update を完了するには再起動が必要です。再起動を実行します"
const L_Msg12_Text = "Windows Update を完了します。再起動は不要です"
const L_Msg13_Text = "未開始"
const L_Msg14_Text = "処理中"
const L_Msg15_Text = "成功"
const L_Msg16_Text = "エラーを表示して終了しました"
const L_Msg17_Text = "失敗"
const L_Msg18_Text = "完了前に処理が停止しました"
const L_Msg19_Text = "再起動が必要"
const L_Msg20_Text = "すべての適用可能な更新プログラムを検索しています..."
const L_Msg21_Text = "重要な更新プログラムのみを検索しています..."
const L_Msg22_Text = "ダウンロードされた更新プログラムの一覧と、それぞれのダウンロード結果を表示します:"
const L_Msg23_Text = "引数がない。または、引数の数が多すぎます。"
const L_Msg24_Text = "正しい引数の名前を指定してください。"
const L_Msg25_Text = "ダウンロード済"
const L_Msg26_Text = "未ダウンロード"

const L_Msg91_Text = "このスクリプトはCSCRIPT.EXEを使用して実行して下さい。"
const L_Msg92_Text = "実行例: cscript  WUA_SearchDownloadInstall.vbs オプション"

' 本スクリプトの引数
const L_Arg01_Text = "ALL_SCH" ' ALL(すべて)の更新プログラムを SCH(Search:検索)
const L_Arg02_Text = "ALL_DWL" ' ALL(すべて)の更新プログラムを DWL(Download:ダウンロード)
const L_Arg03_Text = "ALL_IST" ' ALL(すべて)の更新プログラムを IST(Install:インストール)
const L_Arg04_Text = "IMP_SCH" ' IMP(Important:重要)な更新プログラムを SCH(Search:検索)
const L_Arg05_Text = "IMP_DWL" ' IMP(Important:重要)な更新プログラムを DWL(Download:ダウンロード)
const L_Arg06_Text = "IMP_IST" ' IMP(Important:重要)な更新プログラムを IST(Install:インストール)

const L_Pgm01_Text = "ALL"       ' すべての更新プログラム
const L_Pgm02_Text = "IMPORTANT" ' 重要な更新プログラムのみ

' 変数の宣言
Dim updateSession, updateSearcher, oShell
Dim searchResult, argType, pgmType
Dim updatesToInstall

' 誤爆処理 (ダブルクリックして起動させた場合)--------

If Right((LCase(WScript.FullName)),11) <> "cscript.exe" then
	WScript.Echo L_Msg91_Text & vbCRLF & L_Msg92_Text 
	WScript.Quit(0)
End if

' メイン処理 ここから +++++++++++++++++++++++++++++++++

Set updateSession = CreateObject("Microsoft.Update.Session")
Set updateSearcher = updateSession.CreateupdateSearcher()
Set oShell = WScript.CreateObject ("WScript.shell")

' スクリプト開始
WScript.Echo CStr(now) & " " & L_Msg01_Text

' 引数のチェック
ArgsCheck()

' 引数の内容で処理を分岐
If argType = L_Arg01_Text or argType = L_Arg04_Text Then
	' 更新プログラムを検索
	WUA_Search()
ElseIf argType = L_Arg02_Text or argType = L_Arg05_Text Then
	' 更新プログラムを検索してダウンロード
	WUA_Search()
	WUA_Download()
ElseIf argType = L_Arg03_Text or argType = L_Arg06_Text Then
	' 更新プログラムを検索してダウンロードしてインストール
	WUA_Search()
	WUA_Download()
	WUA_Install()
Else
	' なにもしない
End If

' スクリプト終了
WScript.Echo CStr(now) & " " & L_Msg04_Text 
WScript.Quit

' メイン処理 ここまで+++++++++++++++++++++++++++++++++

' ----------------------------------------

' 引数チェックと更新プログラムの種別(「すべて」or「重要のみ」)を指定
Function ArgsCheck()
	Dim oArgs

	Set oArgs = Wscript.Arguments
	
	If oArgs.Count <> 1 Then
		' 引数の数が1以外の場合
		Wscript.Echo CStr(now) & " " & L_Msg23_Text & L_Msg04_Text
		WScript.Quit
	Else
		argType = ucase(oArgs(0))
		Select Case argType
			Case L_Arg01_Text, L_Arg02_Text, L_Arg03_Text
				' すべての更新プログラムを指定
				Set searchResult = updateSearcher.Search("IsInstalled=0 and Type='Software'")
				pgmType = L_Pgm01_Text
			Case L_Arg04_Text, L_Arg05_Text, L_Arg06_Text
				' 重要な更新プログラムのみを指定
				Set searchResult = updateSearcher.Search("IsInstalled=0 and Type='Software' and AutoSelectOnWebsites=1")
				pgmType = L_Pgm02_Text
			Case Else
				' 指定の引数ではない場合
				Wscript.Echo CStr(now) & " " & L_Msg24_Text & L_Msg04_Text
				WScript.Quit
		end Select
	End If

End Function

' ----------------------------------------------

' 更新プログラムを検索
Function WUA_Search()
	Dim I, update 

	' 引数で指定した更新プログラムの種別(「すべて」or「重要のみ」)をログに出力
	If pgmType = L_Pgm01_Text then WScript.Echo CStr(now) & " " & L_Msg20_Text
	If pgmType = L_Pgm02_Text then WScript.Echo CStr(now) & " " & L_Msg21_Text

	If searchResult.Updates.Count = 0 Then
		' 更新プログラムがなければ終了
		WScript.Echo CStr(now) & " " & L_Msg03_Text
		WScript.Echo CStr(now) & " " & L_Msg04_Text
		WScript.Quit
	End If

	' 更新プログラムがあれば表示
	WScript.Echo CStr(now) & " " & L_Msg02_Text
	
	For I = 0 To searchResult.Updates.Count - 1
		Set update = searchResult.Updates.Item(I)
		Wscript.StdOut.Write CStr(now) & " " & I + 1 & "> " & update.Title 
		
		If update.InstallationBehavior.RebootBehavior > 0 Then
			' OS再起動が必要な更新プログラムならその旨を表示
			Wscript.StdOut.Write " : " & L_Msg19_Text
		End If

		If update.IsDownloaded = true Then
			' ダウンロード済ならその旨を表示
			Wscript.StdOut.Write " : " & L_Msg25_Text
		else 
			' 未ダウンロードならその旨を表示
			Wscript.StdOut.Write " : " & L_Msg26_Text
		End If
                '改行のためのEcho
		WScript.Echo
	Next

End Function

' ----------------------------------------------

' 更新プログラムをダウンロード
Function WUA_Download()
	Dim I, update, updatesToDownload, downloader, DownloadFlag
	
	WScript.Echo CStr(now) & " " & L_Msg06_Text

	Set updatesToDownload = CreateObject("Microsoft.Update.UpdateColl")

	DownloadFlag = false

	For I = 0 to searchResult.Updates.Count - 1
		' 更新プログラムをダウンロード対象として追加登録
		Set update = searchResult.Updates.Item(I)
		If update.IsDownloaded = true Then
			' ダウンロード済なら何もしない
		else 
			DownloadFlag = true
			' 未ダウンロードならダウンロード対象として追加
			updatesToDownload.Add(update)
		End If
	Next

	If DownloadFlag = true Then
		' ダウンロード実行
		Set downloader = updateSession.CreateUpdateDownloader()
		downloader.Updates = updatesToDownload
		downloader.Download()
	End if

	' ダウンロード完了後
	WScript.Echo CStr(now) & " " & L_Msg22_Text

	Set updatesToInstall = CreateObject("Microsoft.Update.UpdateColl")

	For I = 0 To searchResult.Updates.Count - 1
		' 更新プログラム毎のダウンロード結果を表示
		set update = searchResult.Updates.Item(I)
		Wscript.StdOut.Write CStr(now) & " " & I + 1 & "> " & L_Msg05_Text
		If update.IsDownloaded = true Then
			' ダウンロードに成功したらインストール対象として追加登録
			updatesToInstall.Add(update)
			Wscript.StdOut.Write L_Msg15_Text 
		Else
			' ダウンロードに失敗
			Wscript.StdOut.Write L_Msg17_Text
		End If
		Wscript.StdOut.Write " : " & update.Title & vbCRLF
	Next

End Function

' ----------------------------------------------

' 更新プログラムをインストール
Function WUA_Install()
	Dim I, installer,installationResult

	WScript.Echo CStr(now) & " " & L_Msg07_Text

	Set installer = updateSession.CreateUpdateInstaller()
	installer.Updates = updatesToInstall
	Set installationResult = installer.Install()

	' インストール結果
	WScript.Echo CStr(now) & " " & L_Msg08_Text

	For I = 0 to updatesToInstall.Count - 1
		WScript.Echo CStr(now) _
			& " " & I + 1 & "> " _
			& L_Msg09_Text _
			& ResultCodeText(installationResult.GetUpdateResult(i).ResultCode) _
			& " : " & updatesToInstall.Item(i).Title
	Next

	' 最終的なインストール結果
	WScript.Echo CStr(now) & " " & L_Msg10_Text & ResultCodeText(installationResult.ResultCode)

	If installationResult.RebootRequired then
		' OS再起動が必要ならOS再起動を実施
		WScript.Echo CStr(now) & " " & L_Msg11_Text 
		oShell.Run "shutdown /r /t 0",1
	Else
		' OS再起動が不要
		WScript.Echo CStr(now) & " " & L_Msg12_Text 
	end if

End Function

' ----------------------------------------------

' インストール結果コードを日本語に変換
Function ResultCodeText(resultcode)

	if resultcode = 0 then ResultCodeText = L_Msg13_Text
	if resultcode = 1 then ResultCodeText = L_Msg14_Text
	if resultcode = 2 then ResultCodeText = L_Msg15_Text
	if resultcode = 3 then ResultCodeText = L_Msg16_Text
	if resultcode = 4 then ResultCodeText = L_Msg17_Text
	if resultcode = 5 then ResultCodeText = L_Msg18_Text

End Function

' ----------------------------------------------

・実行ログの出力例(1)

2020/03/22 14:45:03 スクリプトを開始します
2020/03/22 14:45:21 すべての適用可能な更新プログラムを検索しています...
2020/03/22 14:45:21 このコンピューターに対して適用できる項目を表示します:
2020/03/22 14:45:21 1> 悪意のあるソフトウェアの削除ツール x64 - 2020 年 3 月 (KB890830) : 再起動が必要 : ダウンロード済
2020/03/22 14:45:21 2> 2020-03x64 ベース システム用 Windows Server 2016 サービス スタック更新プログラム (KB4540723) : ダウンロード済
2020/03/22 14:45:21 3> 2020-03 x64 ベース システム用 Windows Server 2016 の累積更新プログラム (KB4541329) : 再起動が必要 : ダウンロード済
2020/03/22 14:45:21 4> Windows Defender Antivirus のセキュリティ インテリジェンス更新プログラム - KB2267602 (バージョン 1.311.1714.0) : 未ダウンロード
2020/03/22 14:45:21 更新プログラムをダウンロードしています...
2020/03/22 14:45:23 ダウンロードされた更新プログラムの一覧と、それぞれのダウンロード結果を表示します:
2020/03/22 14:45:23 1> ダウンロード結果: 成功 : 悪意のあるソフトウェアの削除ツール x64 - 2020 年 3 月 (KB890830)
2020/03/22 14:45:23 2> ダウンロード結果: 成功 : 2020-03x64 ベース システム用 Windows Server 2016 サービス スタック更新プログラム (KB4540723)
2020/03/22 14:45:23 3> ダウンロード結果: 成功 : 2020-03 x64 ベース システム用 Windows Server 2016 の累積更新プログラム (KB4541329)
2020/03/22 14:45:23 4> ダウンロード結果: 成功 : Windows Defender Antivirus のセキュリティ インテリジェンス更新プログラム - KB2267602 (バージョン 1.311.1714.0)
2020/03/22 14:45:23 更新プログラムをインストールしています...
2020/03/22 15:03:22 インストールされた更新プログラムの一覧と、それぞれのインストール結果を表示します:
2020/03/22 15:03:22 1> インストール結果: 成功 : 悪意のあるソフトウェアの削除ツール x64 - 2020 年 3 月 (KB890830)
2020/03/22 15:03:22 2> インストール結果: 成功 : 2020-03x64 ベース システム用 Windows Server 2016 サービス スタック更新プログラム (KB4540723)
2020/03/22 15:03:22 3> インストール結果: 成功 : 2020-03 x64 ベース システム用 Windows Server 2016 の累積更新プログラム (KB4541329)
2020/03/22 15:03:22 4> インストール結果: 成功 : Windows Defender Antivirus のセキュリティ インテリジェンス更新プログラム - KB2267602 (バージョン 1.311.1714.0)
2020/03/22 15:03:22 インストール最終結果: 成功
2020/03/22 15:03:22 Windows Update を完了するには再起動が必要です。再起動を実行します
2020/03/22 15:03:25 スクリプトを終了します

・実行ログの出力例(2)

2020/03/22 22:45:00 スクリプトを開始します
2020/03/22 22:45:04 すべての適用可能な更新プログラムを検索しています...
2020/03/22 22:45:04 このコンピューターに対して適用できる項目を表示します:
2020/03/22 22:45:04 1> Windows Defender Antivirus のセキュリティ インテリジェンス更新プログラム - KB2267602 (バージョン 1.311.1744.0) : 未ダウンロード
2020/03/22 22:45:04 更新プログラムをダウンロードしています...
2020/03/22 22:45:05 ダウンロードされた更新プログラムの一覧と、それぞれのダウンロード結果を表示します:
2020/03/22 22:45:05 1> ダウンロード結果: 成功 : Windows Defender Antivirus のセキュリティ インテリジェンス更新プログラム - KB2267602 (バージョン 1.311.1744.0)
2020/03/22 22:45:05 更新プログラムをインストールしています...
2020/03/22 22:45:20 インストールされた更新プログラムの一覧と、それぞれのインストール結果を表示します:
2020/03/22 22:45:20 1> インストール結果: 成功 : Windows Defender Antivirus のセキュリティ インテリジェンス更新プログラム - KB2267602 (バージョン 1.311.1744.0)
2020/03/22 22:45:20 インストール最終結果: 成功
2020/03/22 22:45:20 Windows Update を完了します。再起動は不要です
2020/03/22 22:45:20 スクリプトを終了します

・実行ログの出力例(3)

2020/03/23 4:45:00 スクリプトを開始します
2020/03/23 4:45:07 すべての適用可能な更新プログラムを検索しています...
2020/03/23 4:45:07 適用可能な更新プログラムはありません。
2020/03/23 4:45:07 スクリプトを終了します

Windows Updateでアップデート確認だけしたい

Windows Server 2016 でサーバー運用している時、更新プログラムを当てるのではなく、更新プログラムの配信だけを確認したいときがたまにある。

何も考えず「設定」から「Windows Update」画面を起動して、「更新プログラムのチェック」ボタンを押しちゃうと、更新プログラムのチェック だけでは終わらず

(更新プログラムがあれば)
  ↓
 ダウンロード !
  ↓
 インストール !!
  ↓
 OS再起動!!!

まで実行されちゃうことになる。

 

f:id:xlinux2020:20200510224831p:plain

 

なので、更新プログラムの確認だけを確認したいときは、"C:\Windows\System32\ja-jp\WUA_SearchDownloadInstall.vbs"を使う必要がある。

 

WUA_SearchDownloadInstall.vbsの使い方は以下の通り

  1. コマンドプロンプトを起動。
  2. "cd C:\Windows\System32\ja-jp"コマンドを実行して、ディレクトリを移動。
  3. "cscript WUA_SearchDownloadInstall.vbs"を実行して、スクリプト起動。
  4. スクリプトは対話形式で起動される。
  5. 「すべての更新プログラム(A)」を検索するか「推奨される更新プログラム(R)」を検索するか聞かれるので、どちらかを選択。
  6. 更新プログラムがなければ、「適用可能な更新プログラムはありません」と表示されるので、Enterキーを押して終了する。
  7. 更新プログラムがあれば、「すべての更新プログラムをインストールする (A)」、「更新プログラムをインストールしない (N)」、「または単一の更新プログラムを選択する (S)」 から選ぶことができるので、「N」を入力してプログラムを終了する。

f:id:xlinux2020:20200510231454p:plain

上記の画面は、全ての更新プログラムが適用されている状態。

 

ちなみに、WSUSとの通信がうまくいってない場合は、"C:\Windows\SoftwareDistribution\ReportingEvents.log" にエラーログが出力されているので、原因を特定できるかも。。。

 

参考URL

面倒な“Windows 10の更新”をスクリプト化できる新たな選択肢(その1)

https://www.atmarkit.co.jp/ait/articles/1808/06/news012.html

ReportingEvents.log の見方

https://social.msdn.microsoft.com/Forums/ja-JP/fd42498b-10fe-4d90-8bc1-a230d1d3dedc/reportingeventslog-123983521126041?forum=jpsccmwsus