Help us understand the problem. What is going on with this article?

PowerShell で Windows Update を実行する

More than 1 year has passed since last update.

概要

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で公開されているモジュールを参考にしたところ、タスクスケジューラで実行するよう実装されていました。

https://gallery.technet.microsoft.com/scriptcenter/2d191bcd-3308-4edd-9de2-88dff796b0bc

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away