しばたテックブログ

気分で書いている技術ブログです。

Nano Serverのアップデートについて私的まとめ

元ネタはこちら。

blogs.technet.microsoft.com

technet.microsoft.com

内容としては割とそのままで、正直なところ、ただの備忘録です。

2017/02/28ちょっと補足

本エントリでは更新するKBに、KB3199986とKB3206632を使用していますが、元ネタでは、

  • KB3176936 : 事前に必要な更新
  • KB3192366 : 当時最新の累積更新

となっています。
これらはそれぞれ、

  • KB3176936 → KB3199209 → KB3199986 : 事前に必要な更新
  • KB3192366 → KB3206632 : 2016/12/13時点の累積更新。(ちなみに現時点ではKB4010672、2017/01/30時点の累積更新が最新の様です)

に置き換えられています。

0. イメージの作成

はじめに今回使用するNano Serverのイメージ作成手順を記載しておきます。
日本語版OSだと問題があるため(詳細は後述)、英語版OSで基本となるイメージを作成します。

英語版のWindows Server 2016のISOイメージをマウントし、以下のスクリプトを実行してイメージを作成しています。

#$MediaPath  = (Get-WmiObject Win32_CDROMDrive -Filter "VolumeName='SSS_X64FREE_EN-US_DV9'").Drive
$MediaPath  = "E:\" # Windows 2016 ServerのISOイメージのあるドライブ
$BasePath   = "C:\Temp\NanoBase"
$TargetPath = "C:\Temp\nanosv01.vhdx"
Import-Module (Join-Path $MediaPath "\NanoServer\NanoServerImageGenerator\NanoServerImageGenerator.psm1")
$Params = @{
    MediaPath  = $MediaPath;
    BasePath   = $BasePath;
    TargetPath = $TargetPath;
    # デプロイの種類とエディション
    DeploymentType = "Guest";
    Edition        = "Standard";
    # ホスト名とAdministratorのパスワード
    ComputerName = "nanosv01";
    AdministratorPassword = (ConvertTo-SecureString "P@ssw0rd" -Force -AsPlainText);
    # リモート管理/EMS設定
    EnableRemoteManagementPort = $true;
    EnableEMS = $true;
}
New-NanoServerImage @Params

見ての通り最低限の機能だけとしています。

また、実行環境はWindows 10上のHyper-Vによる仮想マシンとしています。
(仮想マシンの細かい設定については割愛します)

初期状態の確認

イメージを作成したあとはEnter-PSSessionで作成した仮想マシンに接続します。

実行例

# PowerShell Directによる接続例 
$cred = Get-Credential administrator
Enter-PSSession -VMName nano01 -Credential $cred

そしてGet-ComputerInfoでOSのビルドバージョンを取得すると以下の様になります。

Get-ComputerInfo WindowsBuildLabEx

f:id:stknohg:20170110004911p:plain

この結果から、初期イメージはBuild 14393.0、2016年07月15日更新のイメージであることがわかります。
この状態をベースとして以降アップデート手順についてまとめていきます。

1. Windows Updateでの更新

まずは一番わかりやすいWindows Updateでの更新方法について説明します。

Nano ServerではWindows Updateを直接呼び出すコマンドなどは無く、WMIを使う必要があります。

日本語版Nano Serverの問題

山市さんのブログの以下の記事にある様に初期ビルド(14393.0)の日本語版のNano Serverではこの方法が正しく動作しません。

yamanxworld.blogspot.jp

これについてはMicrosoftがこの問題を解決した新しいビルドを同梱したISOイメージを提供してくれない限りどうしようもないです。打つ手なしです。

個人的にはNano Serverに関しては日本語OSを捨ててしまうのが得策だと思います。

更新の確認

上記の問題は置いておいて、更新の確認はMSFT_WUOperationsSessionクラスを使って以下の様にします。

$ci = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate -ClassName MSFT_WUOperationsSession  
$result = $ci | Invoke-CimMethod -MethodName ScanForUpdates -Arguments @{SearchCriteria="IsInstalled=0";OnlineScan=$true}
$result.Updates

実行結果

f:id:stknohg:20170110002305p:plain

この結果を見ると、KB3197356KB3199986KB3206632が対象となっています。

更新の適用

Invoke-CimMethodを使いMSFT_WUOperationsSession.ApplyApplicableUpdates()メソッドを呼ぶことでWindows Updateを実行します。
累積アップデートの更新のため、アップデートした結果は再起動後に反映されます。

$ci = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate -ClassName MSFT_WUOperationsSession
Invoke-CimMethod -InputObject $ci -MethodName ApplyApplicableUpdates
# 再起動後に反映
Restart-Computer; exit

実行結果

f:id:stknohg:20170110002642p:plain

適用結果の確認

再起動後は以下の様にして適用結果を確認します。

$ci = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate -ClassName MSFT_WUOperationsSession
$result = $ci | Invoke-CimMethod -MethodName ScanForUpdates -Arguments @{SearchCriteria="IsInstalled=1";OnlineScan=$true}
$result.Updates

実行結果

f:id:stknohg:20170110002749p:plain

この結果を見るとKB3199986KB3206632が適用されたことがわかります。
KB3197356についてはどちらかの更新で上書きされている様です。

また、Get-ComputerInfoでOSのビルドバージョンを取得すると、

f:id:stknohg:20170110005015p:plain

と、Build 14393.576、2016年12月08日更新イメージになっており、OSが更新されたことが確認できます。

ちなみに、この更新で初期状態のvhdxファイルは 495 MB → 2.48 GB に更新されていました。

2. New-NanoServerImage、Edit-NanoServerImageでの更新

Nano ServerではNew-NanoServerImageEdit-NanoServerImageコマンドレットを使い、サーバーイメージの作成・更新時にアップデートを適用することができます。

本エントリではわかりやすたのために0. イメージの作成で作成したイメージに対してEdit-NanoServerImageを適用するケースを例示します。

前準備

この方法を実施する前に予め更新対象となるKB(ここではKB3199986KB3206632)の更新プログラムをダウンロードし、そこからcabファイルを抽出する必要があります。

各KBはWindows Update Catalogからダウンロードします。

今回の場合は、

f:id:stknohg:20170110003952p:plain

f:id:stknohg:20170110004008p:plain

  • windows10.0-kb3199986-x64_5d4678c30de2de2bd7475073b061d0b3b2e5c3be.msu
  • windows10.0-kb3206632-x64_b2e20b7e1aa65288007de21e88cd21c3ffb05110.msu

の2ファイル(msu)をダウンロードします。

ダウンロードしたmsuファイルからexpandコマンドを以下の様に実行し、cabファイルを抽出します。

expand [msuファイル] -f:* [展開先ディレクトリ]

実行例

mkdir C:\Temp\kb3199986
mkdir C:\Temp\kb3206632
expand C:\Temp\windows10.0-kb3199986-x64_5d4678c30de2de2bd7475073b061d0b3b2e5c3be.msu -f:* C:\Temp\kb3199986\
expand C:\Temp\windows10.0-kb3206632-x64_b2e20b7e1aa65288007de21e88cd21c3ffb05110.msu -f:* C:\Temp\kb3206632\

実行結果

f:id:stknohg:20170110005224p:plain

この例の場合、抽出した、Windows10.0-KB3199986-x64.cabWindows10.0-KB3206632-x64.cabの2つのcabファイルを使用します。

更新の適用

そしてEdit-NanoServerImage-ServicingPackagePathパラメーターにこれらのcabファイルを指定してやることでイメージの更新をすることができます。
-TargetPathに更新を適用するイメージ(vhdx)を指定します。

実行例

$MediaPath  = "E:\"
Import-Module (Join-Path $MediaPath "\NanoServer\NanoServerImageGenerator\NanoServerImageGenerator.psm1")
$params = @{
    ServicingPackagePath = (
        'C:\Temp\kb3199986\Windows10.0-KB3199986-x64.cab', 
        'C:\Temp\kb3206632\Windows10.0-KB3206632-x64.cab'
    )
    TargetPath = 'C:\Temp\nanosv01.vhdx'
}
Edit-NanoServerImage @params

実行結果

f:id:stknohg:20170110005528p:plain

上図の様に実行結果はログフォルダにファイルとして出力されます。

f:id:stknohg:20170110005834p:plain

New-NanoServerImageEdit-NanoServerImageコマンドレットは内部的にdism.exeを使用しているため、ログフォルダにはコマンドのログとdismのログが出力されます。
コマンドのログの中身は以下の様になっておりdism.exe /Add-Packageコマンドで更新を適用していることがわかります。

# NanoServerImageGenerator.log
01/10/2017 00:09:05 ========================================
01/10/2017 00:09:05 Edit-NanoServerImage Cmdlet Started
01/10/2017 00:09:05 ========================================
01/10/2017 00:09:05 Edit-NanoServerImage -TargetPath:C:\Temp\nanosv01.vhdx -ServicingPackagePath:@(C:\Temp\kb3199986\Windows10.0-KB3199986-x64.cab, C:\Temp\kb3206632\Windows10.0-KB3206632-x64.cab)
01/10/2017 00:09:36 ========================================
01/10/2017 00:09:36 Edit-NanoServerImage Cmdlet Started
01/10/2017 00:09:36 ========================================
01/10/2017 00:09:36 Edit-NanoServerImage -TargetPath:C:\Temp\nanosv01.vhdx -ServicingPackagePath:@(C:\Temp\kb3199986\Windows10.0-KB3199986-x64.cab, C:\Temp\kb3206632\Windows10.0-KB3206632-x64.cab)
01/10/2017 00:09:36 dism.exe /Unmount-Image /MountDir:'C:\Users\********\AppData\Local\Temp\NanoServerImageGenerator\Temp\mount-windows' /Discard /LogLevel:4 /LogPath:'C:\Users\********\AppData\Local\Temp\NanoServerImageGenerator\Logs\2017-01-10_00-09-36-45\DISM.log' /Quiet
01/10/2017 00:09:36 
エラー: 50

この要求はサポートされていません。

01/10/2017 00:09:39 dism.exe /Image:'C:\Users\********\AppData\Local\Temp\NanoServerImageGenerator\Temp\mount-windows' /Get-Packages /LogLevel:4 /LogPath:'C:\Users\********\AppData\Local\Temp\NanoServerImageGenerator\Logs\2017-01-10_00-09-36-45\DISM.log' /Quiet
01/10/2017 00:09:43 dism.exe /Add-Package /PackagePath:'C:\Temp\kb3199986\Windows10.0-KB3199986-x64.cab' /Image:'C:\Users\********\AppData\Local\Temp\NanoServerImageGenerator\Temp\mount-windows' /LogLevel:4 /LogPath:'C:\Users\********\AppData\Local\Temp\NanoServerImageGenerator\Logs\2017-01-10_00-09-36-45\DISM.log' /Quiet
01/10/2017 00:09:56 dism.exe /Add-Package /PackagePath:'C:\Temp\kb3206632\Windows10.0-KB3206632-x64.cab' /Image:'C:\Users\********\AppData\Local\Temp\NanoServerImageGenerator\Temp\mount-windows' /LogLevel:4 /LogPath:'C:\Users\********\AppData\Local\Temp\NanoServerImageGenerator\Logs\2017-01-10_00-09-36-45\DISM.log' /Quiet

ちなみに最初に

エラー: 50

この要求はサポートされていません。

エラーが出ていますが、これは(おそらく前回のUnmount漏れ対策のために)最初にdism.exe /Unmount-Imageを実行しているからで特に問題はありません。

更新の確認

Windows Updateの場合と同様の手順で更新結果を確認すると、

f:id:stknohg:20170110012357p:plain

となり、ちゃんと更新されていることがわかります。

ちなみに、この更新で初期状態のvhdxファイルは 495 MB → 873 MB に更新されていました。

3. dismでイメージを更新

前項でNew-NanoServerImageEdit-NanoServerImageコマンドレットは内部的にdism.exeを使用していると説明しましたが、直接dism.exeを使ってイメージを更新しても構いません。

こちらについては元ネタを見てもらった方が早いと思いますので具体的な手順については本エントリでは割愛します。

元ネタではMount-WindowsImageDismount-WindowsImageAdd-WindowsPackageを使ったやり方も併せて例示されています。

最後に

はっきり言ってNano Serverのアップデートは非常にめんどうくさいです。
コマンドで一発というわけにもいかず、更新自体も基本的に累積パッケージのみを提供している*1様で一度の更新にかなり時間がかかってしまいます。

あくまで私見ですが、Nano Serverは従来のWindows Serverの様に随時Widows Updateを適用するものではなく、都度最新の更新を適用したOSイメージを作り直すDisposableなモデルに感じられます。
いちばん分かりやすい例としてはDocker Hubにあるコンテナイメージで、Microsoftの手によって随時最新のイメージが更新されていき、ユーザーは常に最新のイメージを使い続けるという感じです。

この考えが正しいかはわかりませんが、とりあえず現状最新のNano Serverを扱うにはコンテナイメージを使うのが一番楽ですし、それ以外の方法だと非常にめんどうで実環境(とくにオンプレ環境での)運用は厳しいと思われます。

*1:累積パッケージのくせに複数更新あり、その依存関係がわかりにくいというおまけ付きで…

PowerShell温故知新、Monad Technology Blogについて

PowerShell Advent Calendar 2016 最終日のエントリになります。

qiita.com

Monad Technology Blog

Monadといっても、

モナドは単なる自己関手の圏におけるモノイド対象だよ。

なヤツではなくて、PowerShell開発時のコードネームのことです。*1*2

blogs.msdn.microsoft.com

Monad Technology BlogWindows PowerShell Blogの前身で2005年にMonad(現PowerShell)のベータ版が出てから2006年に正式名称がPowerShellに変わるまで更新され、以降はWindows PowerShell Blogが後を引き継ぐことになりました。(当時はPowerShellに全然興味が無くこのあたりの事情を全く知らないのが悔やまれます...orz)

一応Monad Technology Blogの内容はすべてWindows PowerShell Blogに移行されており、PowerShell Blogの最初の方を確認すると同じ内容の記事が転載されています。(ただし日付は全て2006/04/25に更新されています)

で、このMonad Technology Blogの何が嬉しいかというと、Monadのベータ版からPowerShell 1.0のリリースに至るまでの時期に書かれたものであるため、PowerShellの基本的な部分やPowerShellの思想や意図に関する内容が多いという点になります。

PowerShellの思想に関するドキュメントはかなり少なく、自分の知る限りではMonado Manifesto(pdf)(詳細は後述)くらいしか見つけることができず、このブログは私としては宝の山に思えてなりません。

本エントリのタイトル通り、温故知新、当時のブログの内容から得るものは結構多いです。

独断と偏見によるエントリ紹介

Monad Technology Blogは全120エントリなので、読めるなら全部読むべきと言いたいのですが、流石にそれは厳しいので私がざっと見た感じでお勧めできるものを適当に紹介していきます。

掲載順は時系列順です。

1. Introduction to MshObject

Introduction to MshObject – Monad

事実上の最初のエントリになります。
最初からかなりコアな内容であり、Monad(PowerShell)の中心であるMshObject(現在のPSObject)についての概要となっています。

MshObject(PSObject)が実際の.NETのオブジェクトをラップしている点、NotePropertyScriptPropertyなどのプロパティの拡張について触れています。

PowerShell BlogになってからもPSObjectついて触れている記事はほとんど無く*3、おそらくこのエントリが公式としては唯一の記事じゃないかと思います。
そういう意味でかなり貴重なエントリです。

2. Duplicate Files (Part 1~2)

Duplicate Files – Monad

Duplicate Files 2 – Monad

ファイルのMD5ハッシュを取れば重複チェックができますよ、そのためにGet-MD5という関数が簡単につくれますよ、といった感じの内容です。

これはずばりPowerShell 4.0で導入されたGet-FileHashそのままの内容ですね。
このエントリが公開された2005年時点でコンセプト自体はあったのがわかり面白いです。

3. Debugging Monad Scripts, Part 3: Write-Host

Debugging Monad Scripts, Part 3: Write-Host – Monad

Write-Hostについての内容です。
Write-Hostは出力に影響せず、Unixでいうところのprintfecho > /dev/ttyに相当するとこの時点ではっきり明言されています。

4. PINVOKE or accessing WIN32 APIs

PINVOKE or accessing WIN32 APIs – Monad

C#のコードを動的にコンパイルしてPowerShellから利用可能にするという内容です。
Add-Typeが導入されたのはPowerShell 2.0からですが、その原型は最初から構想にあった様です。

5. Monad portal in Script Center.

Monad portal in Script Center. – Monad

スクリプトセンターの話です。

http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx は今はスクリプトセンターにリダイレクトされています。

6. Using Monad for logon scripts

Using Monad for logon scripts – Monad

Monad(PowerShell) Scriptをログオンスクリプトに組み込む方法について書かれています。
この時点で既にスクリプトに対してファイルの関連付けをする気が無いことを明確にしているのが興味深いです。

7. Single Shell vs Custom Shell

Single Shell vs Custom Shell – Monad

PowerShellのソースコードにもちょっと名前の出てくるSingle Shellについてです。

正直この機能については謎です。
おそらく今だと独自ホストを作るあたりの機能に相当するものだと思っているのですがよくわかりません...

わかる方がいたらぜひ教えてください。

8. Days till Xmas

Days till Xmas – Monad

function tillXmas ()
{
    $now = [DateTime]::Now
    [Datetime](“12/25/” + $now.Year) – $Now
}

メリークリスマス!

9. Monad breaking change announcement: Approved verb names enforcement.

Monad breaking change announcement: Approved verb names enforcement. – Monad

非推奨の動詞を使うコマンドレットには、動詞に__というプレフィックスを強制する(例 __Play-Nice など)変更をした様です。

当然ですが今のPowerShellにこの様な制約はありません。
コメント欄の荒れっぷりからわかる様に反対多数で取りやめになったのでしょう。

10. Verb-Noun vs Noun-Verb

Verb-Noun vs Noun-Verb – Monad

コマンドレットの命名規則を動詞-名詞にするか名詞-動詞にするかの議論のなかで動詞-名詞を選んだ理由についての内容です。

11. How does Match-String with with Pipelines of objects?

How does Match-String with with Pipelines of objects? – Monad

このブログの Select-Stringは具体的に何を検索しているのか? - しばたテックブログOut-Stringを使ってSelect-Stringをより便利に使う - しばたテックブログ そのままの内容です。

まさかPowerShellリリース以前に同じ情報があるとは思いもしませんでした。
PowerShellを学び始めたころのエントリで結構労力をかけて書いたもので、自力で学習してきたことが間違ってなかったのは嬉しいですが、できれば初めからこのエントリを知りたかったですね...

12. Why did you do that? $VAR/ {} / Weak Intellisense

Why did you do that? $VAR/ {} / Weak Intellisense – Monad

PowerShellの言語設計に関するはなしです。
変数の$はシェルスクリプトから採用してる点、C#との親和性をとるために{}スタイルを採用したことなどが書かれています。

13. Comparative Examples in MSH and KSH

Comparative Examples in MSH and KSH – Monad

MSH(=Monad Shell)とKSHのスタイルの違いについて例示している内容です。

14. Exposing the Power of .NET in a Admin-friendly way

Exposing the Power of .NET in a Admin-friendly way – Monad

このエントリではMonad(PowerShell)が誰に向けたものかについて触れられています。

PowerShellは基本管理者向けですが、管理者以外の開発者、スクリプト書き(初心者・上級者)といった人たちのニーズも満たす様に設計されています。
その具体的な内容として、

  1. Raw .NET access
  2. Enhanced .NET access
  3. Command access

を挙げており、それぞれ、

  1. Raw .NET access

    • C#やVB.NETに慣れている開発者向けに.NET Frameworkの機能に直接アクセスできる様にしている
  2. Enhanced .NET access

    • [xml][array][regex][scriptblock][hashtable]といった構文で.NET Frameworkの型に簡易にアクセスできる様にしている
       (文中では明記されていないがスクリプト書き向けと思われます)
  3. Command access

    • システム管理者向けにコマンド(cmdlet)による管理ができる様にしている

といった内容となっています。

15. Windows PowerShell (Monad) Has Arrived

Windows PowerShell (Monad) Has Arrived – Monad

最後にMonadからPowerShellに正式名称が変わったことをアナウンスする内容になっています。

このエントリでMonad Technology Blogとしては最後となりPowerShell Blogにその後を引き継いでます。

Monad Manifesto

最後に最初で少し触れたMonado Manifesto(pdf)について書いておきます。

この文書は当時のチーフアーキテクトであり、PowerShellの父とも称されるJeffrey Snoverさんによって2002年*4に書かれたもので、Monad(PowerShell)の基本構成や思想、対象とする問題領域などについて書かれたものになります。

文書の脚注に

This is not a Windows PowerShell whitepaper nor is it an accurate description of how V1.0 works.
This is a version of the original Monad Manifesto which articulated the long term vision and started the development effort which became PowerShell.

とある様に、Monad(PowerShell)開発時の文書なので、いまのPowerShellとすべてが一致するわけではありませんが、PowerShellの基本思想を学ぶのに非常に役立つものとなっています。

全16ページとボリュームもそれほど多くないので一読することをお勧めします。

あと、当時を振り返る以下のブログエントリも面白いのでお勧めです。

Monad Manifesto | Jeffrey Snover's blog

Monad (AKA PowerShell): Introducing the MSH Command Shell and Language

Monad (AKA PowerShell): Introducing the MSH Command Shell and Language

*1:なお、このMoandの語源に関しては哲学のモナド(単子論)に由来しており、私の知る限り、関数型言語や圏論のモナドとは関係していません。関数型言語や圏論のモナドも語源は哲学のモナドに由来するそうなので、100%無関係と断じにくいところですが、同じ語源を持つ以上のつながりは無いと思われます。

*2:https://technet.microsoft.com/en-us/library/2005.11.scripting.aspx

*3:あるんですかね?

*4:つまりMonadの開発を始める時ですね

メリクリ

🎄メリクリ🎄

f:id:stknohg:20161224104222p:plain

なにこれ?

bannerコマンドのPowerShell版を作ってみました。

以前シェル芸人たちの間でbannerコマンドが流行ったことがありまして、
(下記リンク参照)

togetter.com

その際に@mattnさんがgobannerというGo言語のbanner実装を作りました。

github.com

で、その実装方法に感銘を受けて、PowerShell版を作ってみたのがコレになります。
実はモノ自体は結構前に作っていて、公開するタイミングを失ってしばらく寝かせていたのですが、今なら良いかなーと思い公開することにしました。

PSBanner

ソースコードはGitHubに上げています。

github.com

インストールはPowerShell Galleryから可能です。

Install-Module -Name PSBanner -Scope CurrentUser

なお、実装の都合*1、Windowsのみでの動作となります。
動作確認はPowerShell 5.1で行っていますが、おそらくPowerShell 2.0以降であれば動くはずです。

基本的な使い方

Write-Bannerコマンドに好きな文字列を食わせてください。
-FontNameパラメーターで表示フォントを、-FontSizeパラメーターでフォントサイズを指定できます。

使用可能なフォント名はGet-FontFamiliesコマンドで取得可能です。

コード例はGitHubに載せているのでそちらを見ていただければと思います。

実装について

基本方針はgobannerの実装を参考にしています。
内部的に指定されたフォントでBitmapを描画し、不透明なドットを#に置き換えるというものです。

一度見てしまうと単純なやり方に思えますが、最初に見たときは「よくこんなこと思いつくなぁ!」と本当に驚きました。

gobannerに対して、フォントの指定は名称で可能なのと、Screen Widthはコンソールバッファの値を使う様に変えています。
あとPowerShellでは文字列以外のオブジェクトの入力もあるため、その辺に対する処理もちょっと入っています。

補足

本エントリはPowerShell Advent Calendar 2016 21日目の代理投稿になります。

*1:System.Drawing.*なクラスを使っているため、System.Drawing.dllのないPowerShell Coreでは動作させることができません