概要
Windows サーバのセキュリティパッチ適用は手間のかかる作業です。サーバの台数にも依りますが、システム停止日に全てのサーバをアップデートするよう計画するためには、僅かな手間も省略したいところです。
そこで手間を省略するために Windows Update を PowerShell から実行する方法を検討しました(とりあえず PowerShell の流れ)。
方針
Windows Update Agent API を使います。これは COM として呼び出せるので New-Object -Com Microsoft.Update.Session
することができます。後はMSDNのサンプルを書き換えるだけです。
手順
Windows サーバにログインして PowerShell を管理者権限で実行します。続いて、以下のスクリプトを全て貼り付けるとサーバの再起動まで自動で進行します。サーバ管理者は「更新プログラムの確認」やダウンロードを待つことなく、次のサーバの作業に移ることができます。
# 1. ログの記録を開始する
$log = ".\$($env:ComputerName)_$(date -f yyyyMMdd).txt"
Start-Transcript $log
$Env:ComputerName
$Env:UserName
date
# 2. Windows Update を確認するセッションを開始
$updateSession = New-Object -com Microsoft.Update.Session
# 3. Windows Update の検索
$searcher = $updateSession.CreateUpdateSearcher()
$searchResult = $searcher.search("IsInstalled=0 and Type='software'")
# 4. Windows Update の結果確認
$searchResult.Updates | % { $_.title -replace ".*(KB\d+).*", "`$1`t$&" }
# 5. 手作業が不要な項目だけを抽出する(通常は検索結果すべて)
$updatesToDownload = New-Object -com Microsoft.Update.UpdateColl
$searchResult.Updates | ? { -not $_.InstallationBehavior.CanRequestUserInput } | ? { $_.EulaAccepted } | % { [void]$updatesToDownload.add($_) }
# 6. ダウンロードする
$downloader = $updateSession.CreateUpdateDownloader()
$downloader.Updates = $updatesToDownload
$downloader.Download()
# 7. ダウンロードが完了したものだけ抽出(通常は全てダウンロードされる)
$updatesToInstall = New-Object -com Microsoft.Update.UpdateColl
$searchResult.Updates | ? { $_.IsDownloaded } | % { [void]$updatesToInstall.add($_) }
# 8. インストールする
$installer = $updateSession.CreateUpdateInstaller()
$installer.Updates = $updatesToInstall
$installationResult = $installer.Install()
# 9. インストールの結果を確認する(ResultCode 2 なら成功、3以上なら一部または全て失敗)
$installationResult
# 10. 再起動する(自動でStop-Transcriptされる)
date
restart-computer
再起動後のサーバにログインし、以下のコードを PowerShell に貼り付けて更新プログラムが無いことを確認しておきましょう。
# 11. 続きからログを記録する
$log = Get-Item .\$($env:ComputerName)_$(date -f yyyyMMdd).txt
Start-Transcript -append $log
# 12. Windows Update を確認するセッションを開始
$updateSession = New-Object -com Microsoft.Update.Session
# 13. Windows Update の検索
$searcher = $updateSession.CreateUpdateSearcher()
$searchResult = $searcher.search("IsInstalled=0 and Type='software'")
# 14. 検索結果の確認(結果が表示されたら 5 からリトライするか、手動で windows update を実行すること)
$searchResult.Updates | % { $_.title -replace '^(.*)\((KB\d+).*$', "`$2`t`$1" }
# 15. ロギングの停止
Stop-Transcript
最後にログを回収してください。
# 16. ログの回収
Set-Clipboard -Path $log
# 17. ログの削除
rm $log
心のこり
本当は Invoke-Command
でやりたかったのですが、$downloader = $updateSession.CreateUpdateDownloader()
でアクセス拒否エラーが発生するので諦めました。
追記:アクセス拒否エラーの回避策
以下のURLで公開されているモジュールを参考にしたところ、タスクスケジューラで実行するよう実装されていました。