PowerShellでのWindows更新プログラムの適用

#前提条件
・WindowsServer2012R2
・PowerShell2.0以上
・ActiveDirectoryドメイン環境
・実行サーバ：ActiveDirectory(ドメインコントローラ)サーバ
　⇒ホスト名：CONTOSOADSVR(こちらで操作を行う)
・実行ユーザ：Domain Adminsグループのユーザ
・適用対象サーバ：WindowsServer2012サーバ
　⇒ホスト名：CONTOSOMEMSVR
・管理共有が有効であること
　⇒　「\\\対象サーバ名\C$」でエクスプローラを開くと、対象サーバのCドライブ直下が参照できる状態。

#前段
「いまどきWindowsサーバの更新プログラムなんて、WSUS(Windows Server Update Service)サーバで、一括で適用すればいいんじゃん」

そう思ったあなた、そこのあなた。
世の中にはWSUSを立てず、サーバを何台も管理しているシステムもあるんです。そんなふうに、Windowsサーバがたくさんあるシステムで、大量の更新プログラムを適用しなければならない状況になったら・・・。

#想定
ここでは複数のサーバに対して、同一の更新プログラムを適用する場合を想定。

#豆知識
Windowsサーバの更新プログラムは、大きく2つに分類されるようです。
・WindowsOSの更新プログラム
・MS製品(Office類)の更新プログラム
紹介するのは、前者のWindowsOSの更新プログラムです。これは主に「セキュリティパッチ」などとも呼ばれています。
また、以下にご紹介する手順はPowerShellと書きつつも、cmdの機能も組み合わせて利用しています。

#流れ
　・事前準備
　・更新プログラムの確認(事前)
　・更新プログラムの入手(MSUpdateカタログ)
　・更新プログラムの解凍
　・更新プログラムの配布
　・更新プログラムの適用
　・OS再起動
　・更新プログラムの確認(事後)
　・配布した更新プログラムの削除

##事前準備
PowerShellを開いて、cdしておく。
以下は、実行サーバ：ActiveDirectory(ドメインコントローラ)サーバにて、workフォルダにチェンジディレクトリ

```powershell
cd C:\work
```


##更新プログラムの確認(事前)
事前に更新プログラムの適用状況を出力。
以下は実行サーバであるActiveDirectoryサーバで行う場合。

```powershell
get-hotfix | ft HotFixID,InstalledOn -AutoSize
```

ftをパイプラインとして使い、必要な項目だけ出力している。主に必要となる情報は、更新プログラムのID(俗にいう”KB=KnowledgeBase”番号)と、インストール日。-AutoSizeで出力結果の文字途切れを防止している。

配布するサーバに対して、更新プログラムを出力したい場合。

```powershell
get-hotfix -ComputerName CONTOSOMEMSVR | ft HotFixID,InstalledOn -AutoSize
```

さらにテキスト出力してログを残す場合(実施しておくと、適用後の確認がラク)。

```powershell
get-hotfix -ComputerName CONTOSOMEMSVR | ft HotFixID,InstalledOn -AutoSize > CONTOSOMEMSVR-beforeKB.txt
```

##更新プログラムの入手(MSUpdateカタログ)
以下リンクより、更新プログラムを入手する。
MicrosoftUpdate カタログ 
https://catalog.update.microsoft.com/v7/site/Home.aspx

検索ボックスに「KB＊＊＊」を入力すると、入手したい更新プログラムがダウンロードできます。
事前に適用対象のOS種類と、32bitなのか、64bitなのかを調べておくとベター。
今回はWindowsServer2012R2なので、必然的に64bitとなる(WindowsServerのR2シリーズは基本的に64bitのみ、2016は知らん)。

MSUpdateカタログで入手した更新プログラムは、「2018-08 Security Only Quality Update for Windows Server 2012 R2 for x64-based Systems (KBXXXXXXX)」といったフォルダでダウンロードされる。フォルダの中には「AMD64_X86-all-windows8.1-kbXXXXXXX-x64_YYYYYYY.msu」といったmsuファイルが入っている。フォルダ名もファイル名もとても長いので、事前に変更しておくとベター(マイクロソフトは嫌がらせをしているとかしか思えない・・・)。

変更前フォルダ名：2018-08 Security Only Quality Update for Windows Server 2012 R2 for x64-based Systems (KBXXXXXXX)
変更後フォルダ名：Windows_Server_2012_R2-KBXXXXXXX

変更前ファイル名：AMD64_X86-all-windows8.1-kbXXXXXXX-x64_YYYYYYY.msu
変更後ファイル名：kbXXXXXXX-x64.msu
※「X」は番号

##更新プログラムの解凍
msuファイルだけでは更新プログラムの適用ができない。そのためwusaコマンドにて、msuファイルを解凍(extract)する必要がある(最近流行の「カモンベイビーアメリカ」と関連はない)。ここではActiveDirectoryサーバの「C:\work」フォルダ内に、先ほどのダウンロードしたフォルダ(とmsuファイル)を置いている。以下の手順では、解凍先もmsuファイルとと同じフォルダに解凍する場合となる。

msuファイル配置先：C:\work\Windows_Server_2012_R2-KBXXXXXXX\kbXXXXXXX-x64.msu
解凍先：C:\work\Windows_Server_2012_R2-KBXXXXXXX

```powershell
wusa "C:\work\Windows_Server_2012_R2-KBXXXXXXX\kbXXXXXXX-x64.msu" /extract:"C:\work\Windows_Server_2012_R2-KBXXXXXXX"
```

以上はコマンドプロンプトでも実施可能(というよりcmd標準機能)。

実行すると、「C:\work\Windows_Server_2012_R2-KBXXXXXXX」フォルダに、以下のようなcabファイル(キャビネットファイル)が作られる。

「Windows8.1-KBXXXXXXX-x64.cab」

このファイルが更新プログラムのインストーラに相当する。「WSUSSCAN.cab」などのキャビネットファイルも作成されるが、こちらは利用しない。

##更新プログラムの配布
コピー元サーバ「CONTOSOADSVR」内の「C:\work\Windows_Server_2012_R2-KBXXXXXXX」フォルダ(と、フォルダ内のファイルすべて)を、コピー先サーバに配布する。配布方法はrobocopyコマンドを使う。

```powershell
robocopy "C:\work\Windows_Server_2012_R2-KBXXXXXXX" "\\CONTOSOMEMSVR\C$\work\Windows_Server_2012_R2-KBXXXXXXX" /R:0 /W:0 /LOG+:"C:\work\CONTOSOMEMSVR-robocopylog.txt" /TEE /XJD /XJF /V /E
```

コピー先は管理共有で指定。コピー先に「C:\work\Windows_Server_2012_R2-KBXXXXXXX」フォルダがなかったとしても、robocopyを使えば自動で作成してくれる。以上コマンドは、コピーに失敗した場合のリトライや再試行はさせないため(する場合、コピーに時間がかかるため)、/R:0 /W:0にて0に指定。実行時のプロンプトにもコピー結果は表示されるが、以上コマンドではrobocopyの結果をログを「C:\work\CONTOSOMEMSVR-robocopylog.txt」として残している。

##更新プログラムの適用
dismコマンドにて更新プログラムの適用を行う。適用状態が表示されるため、100%表示で適用状況がわかる。以下は実行サーバであるCONTOSOADSRVで行う場合。

```powershell
dism /online /add-package /PackagePath:"C:\work\Windows_Server_2012_R2-KBXXXXXXX\Windows8.1-KBXXXXXXX-x64.cab" /norestart
```

適用対象サーバである、CONTOSOMEMSVRに適用したい場合、ActiveDirectoryサーバから以下のリモートコマンドより適用する。

```powershell
Invoke-Command CONTOSOMEMSVR -ScriptBlock { cmd /c dism /online /add-package /PackagePath:"C:\work\Windows_Server_2012_R2-KBXXXXXXX\Windows8.1-KBXXXXXXX-x64.cab" /norestart }
```

/norestartオプションで、更新プログラム適用後の再起動を抑止している。

##OS再起動
更新プログラムを適用したら、OS再起動をする。

```powershell
shutdown /r /t 0
```

適用対象サーバである、CONTOSOMEMSVRをOS再起動したい場合、ActiveDirectoryサーバから以下のリモートコマンドよりOS再起動を行う。

```powershell
Invoke-Command CONTOSOMEMSVR -ScriptBlock { cmd /c shutdown /r /t 0 }
```

##更新プログラムの確認(事後)
適用対象サーバである、CONTOSOMEMSVRの更新プログラム一覧を、ファイルとして取得する。

```powershell
get-hotfix -ComputerName CONTOSOMEMSVR | ft HotFixID,InstalledOn -AutoSize > CONTOSOMEMSVR-afterKB.txt
```

事前の確認時に取得した「CONTOSOMEMSVR-beforeKB.txt」と比較すると、適用されたことを確実に確認できる。

```powershell
Compare-Object -ReferenceObject $(Get-Content C:\work\CONTOSOMEMSVR-beforeKB.txt) -DifferenceObject $(Get-Content C:\work\CONTOSOMEMSVR-afterKB.txt)
```

##配布した更新プログラムの削除
クローズ作業に相当。実行サーバでキャビネットファイルやmsuファイルを消す場合。

```powershell
Remove-Item -Path "C:\work\Windows_Server_2012_R2-KBXXXXXXX" -Recurse
```

適用対象サーバでのキャビネットファイルやmsuファイルを消す場合(リモート)。

```powershell
Remove-Item -Path "\\CONTOSOMEMSVR\C$\work\Windows_Server_2012_R2-KBXXXXXXX" -Recurse
```

以下は削除確認。返り値がFalseであれば、削除完了。

```powershell
Test-Path "C:\work\Windows_Server_2012_R2-KBXXXXXXX"
```

```powershell
Test-Path "\\CONTOSOMEMSVR\C$\work\Windows_Server_2012_R2-KBXXXXXXX"
```

#まとめ
時間があればforeachなどで以上をps1スクリプトにしようかとも考えたけど、しんどくなってやめてしまった（笑）かなりルーチン的な処理なので、時間があるときに以上のスクリプト化をしてみたい。

#余談
先日、映画『ミッションインポッシブル6：フォールアウト』を見たのですが、「トムクルーズのジャッキーチェン化」が止まらなくて最高でした。エンドロールにNG集がないのが不思議に感じたぐらいでした。とんでもない映像ばかりで、普段の仕事でもトムの勇姿を見習おうと思いました。

[link1]:https://www.youtube.com/watch?v=Z_aCOQi5tm4

[Mission: Impossible-Fallout (2018)- "All Stunts"- Paramount Pictures ][link1]