しばたテックブログ

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

PowerShell Core 6.0 破壊的変更まとめ

公式情報はこちら。

https://github.com/PowerShell/PowerShell/blob/master/docs/BREAKINGCHANGES.md

公式情報では結構説明が端折られており、元になったIssueやPull Requestを読み込まないとわかりにくい部分があったため、本エントリではそのあたりを補足しつつ説明していきます。
一部の内容は新機能まとめと被っているのですがそのまま説明します。

PowerShell Coreでサポートされなくなった機能

PowerShell Workflow

PowerShell 3.0より導入されたワークフローの機能ですが、内部基盤であるWorkflow Foundationが.NET Coreに移植されておらずその予定も無いこともありPowerShell Coreには導入されません。
PowerShell Teamとしても導入の予定はないと明言しています。

正直なところ「これ使ってるひといる?」といった現状なので廃止もやむを得ないと思います。

カスタムスナップイン

スナップインの機能はPowerShell 1.0からの機能ですが、PowerShell 2.0から導入されたモジュールによってその役割は置き換えられ現在では非推奨の機能となっています。
この前提もありPowerShell Core 6.0では完全にサポートされなくなりました。

現在ところ、この変更によりPowerShell CoreではActiveDirectoryDnsClientモジュールが利用できなくなる影響が出ています。

WMI v1 Cmdlets

Get-WmiObjectなどの古いWMI関連のコマンドレットは廃止されました。
これは最初のPowerShell CoreであるNano Server向けのPowerShell 5.1がリリースされた時*1からの変更になります。

代わりにGet-CimInstanceなどのCIM Cmdletsを使用して下さい。

Microsoft.PowerShell.LocalAccounts

新機能まとめでも触れましたが、Microsoft.PowerShell.LocalAccountsモジュールはサポートされていないAPIを使用しているため削除されています。

GitHubの内容を見る限りでは、良い実装方法が見つかれば機能を復活させる様です。

*-Counter Cmdlets

パフォーマンスカウンター系の機能もMicrosoft.PowerShell.LocalAccountsと同様の理由で削除されています。

エンジン/言語に関する変更

powershell(.exe)からpwsh(.exe)への改名 (#5101)

本ブログでも何度か触れている通りです。

blog.shibata.tech

(Tableフォーマットを除いて)出力に改行を入れなくなりました (#5193)

タイトルがわかりにくいのですが、これまでのPowerShellにおいてコンソールへの文字列出力はコンソール幅の影響を受け、コンソール幅を超えるときは出力が打ち切られることがありました。

これを回避するためにOut-String -Widthを使って出力幅を指定する必要がありました。

# 例) 出力結果がコンソール幅で打ち切られない様にOut-String -Widthを使う
Get-StartApps | Out-String -Width 1024

PowerShell Core 6.0ではこの点が改善され、コンソールへの出力内容はコンソール幅の影響を受けなくなりました。

「Tableフォーマットを除いて」とあるのはTableフォーマットの場合は列幅が決められている場合があるので、その場合は出力の省略がこれまで通り行われます。

値型のコレクションに対するNULL要素のチェックが廃止されました (#5432)

Mandatory属性やその内部で使用されるValidateNotNullValidateNotNullOrEmpty属性において、検証処理の性能改善のために要素が値型であるコレクションが渡された場合は(値型であればNULLにならないため)そのNULLチェックをしない様に内部処理が変更されています。

この変更による影響ですが、要素の検証がスキップされる(=要素へのアクセスが無い)のでコレクションのGetEnumerator()の挙動に差異が発生します。
元のPull Request(#5432)にある例を出しておきます。

function bar {
   param ([ValidateNotNull()] $object)
   "MoveNext:  $($object.MoveNext())"
}

bar -object @(1,2,3).GetEnumerator()

Windows PowerShellではこの結果が$false(検証時に全要素にアクセスしているため)となり、PowerShell Coreでは$true(要素へのアクセスが発生しないため)となります。

# PowerShell 5.1まで
PS C:\> bar -object @(1,2,3).GetEnumerator()
MoveNext:  False

# PowerShell Core 6.0
PS C:\> bar -object @(1,2,3).GetEnumerator()
MoveNext:  True

$OutputEncodingの既定値をASCIIからBOMなしUTF-8に変更しました (#5369)

blog.shibata.tech

で触れたとおりです。

非WindowsなOSはだいたいUTF-8で動くのと、WindowsにおいてもASCIIは厳しいといった現状から変更に至っています。
事の経緯についてはこちらのIssue(#4681)をご覧ください。

ほぼ全ての既定のエイリアスからAllScopeオプションを取り除きました (#5268)

エイリアスのAllScopeオプションは、ヘルプから引用すると、

エイリアスは、新たに作成されるすべてのスコープにコピーされます。

といった役割を持つものですが、通常のPowerShellの利用においては基本気にしなくても問題ないでしょう。

この変更はパフォーマンスの改善を目的として実施されています。
これにより15~20%程度スコープ作成処理の高速化が見込みまれているそうです。

GitHubのコミットログを見ると

  • foreach
  • %
  • where
  • ?
  • select
  • cp
  • cd
  • dir
  • echo

以外のエイリアスが対象となっています。
上記エイリアスを変更しなかったのは、よく使われるであろうエイリアスに対してはAllScopeオプションを指定していた方がエイリアスのルックアップが早くなるためだそうです。

-Verbose-Debugパラメーターは$ErrorActionPreferenceの設定を上書きしなくなります (#5113)

PowerShellの微妙な仕様として、コマンドレットの実行時に-Verboseを指定すると$ErrorActionPreferenceの値をContinueに強制してしまう挙動があります。
(-Debugを指定した場合はInquireが強制されます)

例)

PowerShell Core 6.0ではこの挙動が改善され、-Verbose-Debugパラメーターを指定した場合に$ErrorActionPreferenceの設定を上書きしない様になりました。


余談なのですが、この挙動は$WarningPreferenceについても同様なのですが修正の対象とはなっていないので$WarningPreferenceは従来通りの挙動となります。

もし$WarningPreferenceについても気になる方がいらっしゃればIssueを上げるとよいでしょう。
(個人的には$WarningPreferenceはたいして使わないでしょうし問題ないと判断しています。)

コマンドレットの変更

Invoke-RestMethodはデータが何もなかった場合は情報を返さななくなりました (#5320)

Invoke-RestMethodはWEB APIがnullを返す際に文字列の"null"と解釈することがあり、今回の変更で$nullとして扱い情報を返さない様になったとのことです。

ただ、もとのIssueを読みながら色々試してみたのですが、良い感じの例を出すことができませんでした...

*-Computerなコマンドレットから-ComputerNameパラメーターを削除しました (#5227)

  • Rename-Computer
  • Restart-Computer
  • Stop-Computer

が対象です。
対象マシンを指定する-ComputerNameパラメーターはDCOM依存であり、.NET CoreでDCOMがサポートされていないための削除となります。
代わりにInvoke-Commandを使ってリモートコマンドを実行してください。


と、書かれてるのですが...ソースを読む限りではあくまでもDCOM(=>WMIv1)に依存するコードのみ削除されており-ComputerNameは普通に残っており使えます。
-ComputerNameパラメーターを使った場合はWSMan(=>CIM)を使ってリモートコンピューターに対してコマンドを実行し、代わりに-Protocolパラメーターが削除されています。

単純に記述が間違っている様です。

*-Serviceなコマンドレットから-ComputerNameパラメーターを削除しました (#5090)

基本的には前項と同様です。
PowerShell Coreのコマンドレット全般で-ComputerNameパラメーターは削除されています。

-LiteralPathパラメーターがワイルドカード文字を展開しなくなりました (#5197)

例として

Get-Item -LiteralPath a*b

が挙げられており、Windows PowerShellではこの場合-Path a*bと同様に扱われワイルドカード検索していましたが、PowerShell Coreではワイルドカードの展開は抑制されます。

Import-CsvPSTypeNamesの指定を受け入れる様になりました。 (#5134)

Windows PowerShellではExport-CSVで出力したCSVについているPSTypeNames(先頭の#TYPE [型名]の部分)がImport-Csvで正しく反映されなかったのですが、これがPowerShell Core 6.0で改善されています。

もとのIssue(#5134)からサンプルを例示しておきます。

  • エクスポート
[PSCustomObject]@{
    PSTypeName = 'My.Custom.Object'
    Column1 = 'values1'
} | Export-Csv .\csv.csv
  • 出力されるCSV
#TYPE My.Custom.Object
"Column1"
"values1"
  • インポート
$import = Import-Csv .\csv.csv
$import.PSObject.TypeNames
  • 結果
# PowerShell 5.1まで
CSV:My.Custom.Object

# PowerShell Core 6.0
My.Custom.Object
CSV:My.Custom.Object

Export-Csv-NoTypeInformationパラメーターが既定となりました (#5131)

-NoTypeInformationは前項で説明したPSTypeNamesを出力しないオプションになります。

単純にCSVファイルを出したい場合PSTypeNamesは不要な情報であり、実際不満の声が大きかった様でデフォルトでPSTypeNamesを出力しない様に改善されました。

PowerShell Coreではこれまでとは逆に-IncludeTypeInformationパラメーターをつけることでPSTypeNamesを出力する様になります。

Web Cmdletsでは暗号化されていない通信で-Credentialを使うと警告を出す様になりました (#5112)

暗号化されていない通信はHTTP接続のことです。

「警告」と言っていますが、実際には-AllowUnencryptedAuthenticationパラメーターをつけない限りはエラーとなります。

# PowerShell Core 6.0
$cred = Get-Credential

# 警告メッセージを出してエラー
Invoke-WebRequest http://contoso.com/ -Credential $cred

# 実行可能
Invoke-WebRequest http://contoso.com/ -Credential $cred -AllowUnencryptedAuthentication

APIの変更

AddTypeCommandBaseクラスは削除されました (#5407)

Add-Typeコマンドレットの性能改善の一環としての変更です。
Add-Type以外に影響はないとのことです。

ちなみに、新機能まとめでも触れていますが、Add-Typeはこれから破壊的変更が入る見込みです。

-EncodingパラメーターがSystem.Text.Encoding型に統一されました (#5080)

blog.shibata.tech

で触れた内容です。  

Get-Date-UFormatパラメーターのエラーメッセージが改善されました (#5055)

Get-Date -UFormat ''

の様に-UFormat''$nullを指定した場合、これまでのPowerShellではIndexOutOfRangeExceptionが発生してしまっていたのですが、PowerShell Core 6.0からは通常のパラメーターチェックエラーメッセージが出る様に改善されました。

コンソールコードのクリーンアップ (#4995)

こちらは元のPull Request(#4995)を見てもらうかない感じです。

PowerShell Coreの開発にあたって(主に.NET Coreで)サポートされない機能を削除しコードをクリーンアップしています。

主な変更点として、

  • pwsh起動時の-Sta-PSConsoleFile-Importsystemmodulesパラメーターの削除
  • コンソールフォント変更に関わるコード
  • #if CORECLRディレクティブの整理

が挙げられています。

RunspaceConfigurationサポートの削除 (#4942)

InitialSessionStateが導入されているためレガシーなRunspaceConfigurationは削除したとのことです。
この変更についてほとんどの人に影響はないと思います。
自作プログラム内からPowerShellをホストするケースでRunspaceConfigurationを使っている場合は影響が出そうですが正直なんともいえません...

PowerShellのコードからだと

$Host.Runspace.RunspaceConfiguration

$Host.Runspace.InitialSessionState

とするとそれぞれのプロパティにアクセスすることができます。
(だからどうしたという感じですが...)

CommandInvocationIntrinsics.InvokeScriptの引数が$argsでなく$inputにバインドされているのが修正されました (#4923)

正直、これは元のIssue(#4923)を見てもらうしかないでしょう...

PowerShell内部でスクリプトブロックを実行する際に$arg自動変数に設定すべき内容を$input自動変数に設定してしまっている不具合を修正したものだそうです。
GitHub上の会話を見る限りではこの誤った呼び方でスクリプトブロックが実行されるケースは少ないだろうとの判断で修正を入れています。

Get-Helpから-ShowWIndowパラメーターが削除されました (#4903)

-ShowWindowパラメーターで表示されるヘルプウィンドウはWPF製であり、.NET CoreでWPFはサポートされないための削除となります。

Remove-Itemでレジストリのパスを削除する際に*を受け入れる様になりました (#4866)

レジストリのパスには*が使用可能なのですが、PowerShellでは*はワイルドカードとして扱われてしまうためRemove-Itemが正しく動作しないケースがあり、PowerShell Core 6.0でこれが修正されています。

Set-Service-StartupTypeパラメーターの修正 (#4802)

元のタイトル*2がよくわからない感じなのですが、従来New-ServiceSet-Service-StartupTypeに不正な値を指定した際に無視されてしまう不具合があり、PowerShell Core 6.0では不正なパラメーターとしてエラーになる様に改善された様です。

$IsOSX自動変数が$IsMacOSに改名されています (#4700)

これはアルファ版からPowerShell Core 6.0を使っている人にしかわからないと思います。

当初macOSでの実行を判別するための自動変数は$IsOSXという名前だったのですが、開発中にOS XからmacOSにOSの名前が変更されたため$IsMacOSに改名されました。

GA後のPowerShell Core 6.0から使っている人であれば初めから$IsMacOSなので関係のないはなしになります。

pwsh起動時のパラメーター指定に関するエラーメッセージが改善されました (#4573)

元のタイトル*3が「お前は何をいっているんだ?」という感じなのですが、大元のIssue(#4351)を読む限りでは、pwsh(.exe)の起動時にあいまいなパラメーター名(-noだけなど)を指定した際のエラーメッセージが不親切であり、macOS版のPowerShellではエラーメッセージを出しつつも起動してしまうといった不具合があったため、不具合の改善に合わせてエラーメッセージの改善もした様です。

実際、

pwsh -no -command get-date

の様にあいまいなパラメーターを指定すると以下のエラーメッセージが表示されます。

PS C:\> pwsh -no -command get-date
Invalid argument '-no', did you mean:

  -nologo
  -noexit
  -noprofile
  -noninteractive

LocalAccountDiagnosticsモジュールのコマンドレットの削除 (#4302, #4303)

"PowerShell Coreでサポートされなくなった機能"の項で説明したMicrosoft.PowerShell.LocalAccounts*-Counter Cmdletsを削除した件のことです。

スクリプト実行時にBool型のパラメーターが有効にならないのを修正 (#4036)

これまでのPowerShellではpowershell.exeを起動する際のパラメーターにBool型の値を設定することができませんでした。

以下の様なbool型のパラメーターを持つスクリプトに対して、

# Test.ps1
param (
    [Parameter(Mandatory=$true)][bool]$BoolValue
)
Write-Output $BoolValue

パラメーターを指定して起動するとエラーとなりスクリプトは実行できません。

# Windows PowerShell
# 起動時エラーとなる
powershell.exe -File .\Test.ps1 -BoolValue $true

これまでは回避策として-Commandパラメーターを使うしかありませんでした。

# Windows PowerShell
# -Commandパラメーターで回避可能
powershell.exe -Command {.\Test.ps1 -BoolValue $true}

PowerShell Core 6.0ではこの点が改善され、bool型のパラメーターはSwitchパラメーターと同様の指定方法で設定することが可能になりました。

# PowerShell Core 6.0 からbool型のパラメーターも設定可能に
pwsh -File .\Test.ps1 -BoolValue:$true

$PSVersionTableからClrVersionプロパティを削除しました (#4027)

新機能まとめで説明した通りです。
PowerShell Coreの基盤である.NET Coreではこのプロパティの値が意味を成さなくなったため廃止されました。

pwsh(.exe)を起動する際の位置指定パラメーターは-Commandから-Fileになりました (#4019)

従来のPowerShellでは、パラメーター名を明示しない場合以下の様に

# -Fileパラメーター名を省略
powershell.exe .\hello.ps1

# -Commandパラメーター名を省略
powershell.exe "Write-Output 'Hello'"

-File-Commandの両方を位置指定パラメーターとして使えました。
PowerShell Core 6.0では主に非Windows環境のShebangからのPowerShell実行に対応するために位置指定パラメーターを-Fileのみに限定しています。

# PowerShell Core 6.0
# 〇 : -Fileパラメーター名は省略可
pwsh .\hello.ps1

# × : -Commandパラメーターは省略不可
pwsh "Write-Output 'Hello'"
# パラメーター名を明示する必要がある
pwsh -Command "Write-Output 'Hello'"
# -cでも良い
pwsh -c "Write-Output 'Hello'"

Unicode文字のエスケープが実装されました (#3958)

新機能まとめで説明した通りです。
`u#### または `u{####} でエスケープできます。

非Windows環境ではNew-ModuleManifestのエンコーディングがBOM無しUTF-8になります (#3940)

クロスプラットフォーム化対応の一環です。
New-ModuleManifestで出力されるマニフェストファイル(.psd1)のエンコーディングが、Windowsでは従来通りBOM付きUTF-16、非Windows環境ではBOM無しUTF-8になります。

Get-ChildItemはシンボリックリンクのリンク先を再帰検索しない様になりました (#1875, #3780)

Get-ChildItemの挙動をWindowsのdir /s、Unixのls -rと合わせるための仕様変更になります。
この変更があったため、これまでのPowerShellの挙動と互換をとるための-FollowSymLinksパラメーターが追加されています。(#3951)

Get-Content-Delimiterを指定した際にデリミタ自身が残ってしまう不具合が解消されました (#3706)

新機能まとめで説明した通りです。

Format-HexがC#で実装しなおされました (#3320)

もともとFormat-Hexはスクリプトコマンドレットだったのですが、機能追加の要望と併せてC#で再実装されることになりました。
この変更により-Rawパラメーターが存在はするものの何もしないパラメーターとなったため破壊的変更に加えられています。

ちなみに機能追加の要望はまだ取り入れられていません。

scriptコマンドでPowerShellが既定のシェルとして動作しない不具合を改善しています (#3319)

クロスプラットフォーム対応の一環です。
chshコマンドで既定のシェルをPowerShellに変えた後にscriptコマンドを実行するとPowerShellの起動に失敗する不具合があり、これを解消するためにpwsh(.exe)-Interactive(-i)オプションが導入されました。
従来のPowerShellには-NonInteractiveオプションがあり、これを逆転させた形となります。

Get-ComputerInfoで取得されるプロパティ名のTypoを修正 (#3167)

BiosSeralNumberBiosSerialNumberに修正されています。

Get-StringHashGet-FileHashの追加 (#3024)

Get-FileHashの改善の一環として新たにGet-StringHashコマンドレットを増やす提案がなされたのですが、要RFCということで導入は見送られています。
Get-FileHashから.NET Coreでサポートされなくなった、

  • MACTripleDES
  • RIPEMD16

のアルゴリズムが選択不可になっています。

幾つかのGet-*なコマンドレットで$nullが許容されていたのを廃止しました (#2672)

以下のコマンドのパラメーターで$nullが許容されていたのに対してValidateNotNullOrEmpty属性を付けてNULLチェックをする様になりました。

  • Get-Credential -UserName
  • Get-Event -SourceIdentifier
  • Get-EventSubscriber -SourceIdentifier
  • Get-Help -Name
  • Get-PSBreakpoint -Script
  • Get-PSProvider -PSProvider
  • Get-PSSessionConfiguration -Name
  • Get-PSSnapin -Name
  • Get-Runspace -Name
  • Get-RunspaceDebug -RunspaceName
  • Get-Service -Name
  • Get-TraceSource -Name
  • Get-Variable -Name
  • Get-WmiObject -Class
  • Get-WmiObject -Property

事の発端はこちらのUserVoiceからで、NULLを許容してしまうコマンドを洗い出しての修正となった様です。

Import-CsvにW3C拡張ログファイルのサポートが追加されました (#2482)

IISやExchangeで使われているW3C拡張ログファイルをImport-Csvで取り込める様になりました。
W3C拡張ログにはコメントヘッダーがあるため、ヘッダの取り扱いに破壊的変更が発生している様です。

ログファイル例)

#Version: 1.0
#Date: 12-Jan-1996 00:00:00
#Fields: time cs-method cs-uri

【2019.03.26追記】

ぎたぱそ先生の調査によりこの変更はExchangeログを対象にしていることがわかりました。

tech.guitarrapc.com

【追記ここまで】

関数の引数にValueFromRemainingArguments属性を付けた際のバインディングの問題 (#2035)

これは非常に説明しにくいのでもとのIssue(#2035)を見てください...
(私も完全に把握できていません...)

基本的にはValueFromRemainingArguments属性を付けたパラメーターに配列が渡されたときに、従来のPowerShellでは配列をまとめた形でバインドされるのですが、配列の要素ごとに展開すべきでは?という議論から始まっています。

ValueFromRemainingArguments属性を使うことはあまり無いと思いますので、ふつうにPowerShellを使う分には気にしなくても問題ないと思います。

$PSVersionTableからBuildVersionプロパティが削除されました (#1415)

新機能まとめで説明した通りです。
BuildVersionプロパティはWindowsのビルドと強く結びついているため廃止され、代わりにGitCommitIdプロパティが導入されています。

Web Cmdletsの変更

新機能まとめでも説明しましたが、PowerShell Core 6.0では基盤が.NET Coreになったこともあり、Web Cmdletsの基盤がWebRequestクラスからHttpClientクラスに代わっており、中身は完全に作り替えられています。
また、WindowsのWEBアクセス処理全般的な話としてIE*4に依存する部分があり、Windows PowerShellのWeb CmdletsにもIE依存な部分がありましたが、PowerShell CoreになってIE依存な処理が無くなったため、それがそのまま破壊的変更に繋がっています。

以下にInvoke-WebRequestInvoke-RestMethodの破壊的変更を記載します。

  • Invoke-WebRequestbasic HTML Parsingのみサポートします。(常に-UseBasicParsing指定と同等)
    これによりInvoke-WebReqestは常にBasicHtmlWebResponseObjectクラスのオブジェクトを返します。IEに依存するParsedHtmlオブジェクトは削除されました。
  • BasicHtmlWebResponseObject.Headersの値はstring型からstring[]に変更されました。

  • BasicHtmlWebResponseObject.BaseResponseSystem.Net.Http.HttpResponseMessage型のオブジェクトになりました。

  • 例外発生時のプロパティはSystem.Net.Http.HttpResponseMessage型のオブジェクトになりました。(参考)

  • -Headers-UserAgentパラメーターではRFCに厳密なヘッダ解析がデフォルトになりました。(参考)
    これを回避するには-SkipHeaderValidationパラメーターを指定します。

  • file://ftp://スキーマはサポートされません。(参考)

  • System.Net.ServicePointManagerクラスの設定は反映されなくなりました。(参考)

  • macOSでは今のところ証明書ベースの認証ができません。(#4650)

  • HTTP接続で-Credentialパラメーター使用した場合はエラーとなります。
    HTTPS接続にするか-AllowUnencryptedAuthenticationパラメーターを指定することでエラーを回避できます。

最後に

ざっとこんな感じです。
まだ正確に把握できていない箇所が残っているので分かり次第内容を更新していきたいと思います。

*1:当時はPowerShell Coreとは呼ばれていませんでしたが...

*2:Fix Set-Service failing test

*3:Make error message consistent when invalid script is passed to -File, better error when passed ambiguous argumen

*4:正確にはIEコンポーネントでしょうか

PowerShell Core 6.0 新機能まとめ

基本的には公式ドキュメントの内容をベースに説明が不足している個所を補足してまとめています。
正確な情報が必要な場合は英語版の公式ドキュメントをあたってください。

公式ドキュメント

本エントリを書き始めたときは日本語版はなかったのですが最近できた様です。
ただ、明らかに機械翻訳で日本語が怪しい個所がちらほらあるのでできるだけ英語版を見る方が良いでしょう。

新機能一覧

ここから本題に入ります。
構成は公式ドキュメントとできるだけ近くしましたが微妙に変えています。
また、前回説明している部分に関しては端折っています。

ログ

PowerShell内部のイベント発行システムがETWだけでなく、Linuxではsyslogを、macOSではunified logging systemを使う様になりました。

あくまで内部のログ出力が変わっただけでありGet-EventLogなどのコマンドレットがLinuxやmacOSで使える様になったわけではないのでご注意ください。

ファイルシステム

WindowsだけでなくLinuxやmacOSをサポートしたことにより、ファイルシステムに関する制約や挙動に以下の変更が加えられています。

  • パスセパレーターは/\の両方使えます。(これはWindows PowerShellでもサポートされていますが...)
  • LinuxやmacOSにおいてはXDG Base Directoryの仕様に従う様になっています。
    • CurrentUser*なプロファイルは ~/.config/powershell/ 配下に配置されます。
    • PSReadilneのヒストリーは ~/.local/share/powershell/PSReadline/ConsoleHost_history.txt に配置されます。
    • ユーザーモジュールは ~/.local/share/powershell/Modules に配置されます
  • LinuxやmacOSではWindowsで禁止さえれている文字列(:,など)を含むファイルやディレクトリ名がサポートされます。
  • ファイル操作系のCmdletで-LiteralPathが指定された場合はワイルドカード文字(*など)の展開が抑制されます。
  • Get-ChildItemの挙動がよりUnixのls -R、コマンドプロンプトのdir /Sに近くなりました。
    新しいGet-ChildItemはデフォルトでシンボリックリンクのリンク先を再帰検索しない様になっています。

  • PowerShell Coreでは260文字を超えるパスを使用できるようになっています。(#3960)

大文字と小文字の区別

言語としてのPowerShellは基本的に大文字・小文字を区別しませんが、クロスプラットフォーム化したことによりLinuxやmacOSにおいてはいくかのケースで大文字・小文字が区別されます。

  • パス指定
# Windows : 大文字・小文字は区別されない
PS C:\> Test-Path C:\
True
PS C:\> Test-Path c:\
True

# Linux/Mac : 大文字・小文字は区別される
PS /> Test-Path /var/
True
PS /> Test-Path /Var/
False
  • 環境変数
# Windows : 大文字・小文字は区別されない
PS C:\> $env:HOMEDRIVE
C:
PS C:\> $env:HomeDrive
C:

# Linux/Mac : 大文字・小文字は区別される
PS /> $env:HOME
/home/vagrant
PS /> $env:Home

サイドバイサイドインストールのサポート

PowerShell CoreはポータブルなZipファイルのパッケージを持っており、各バージョンをサイドバイサイドでインストールすることが可能です。

WindowsでMSIインストーラーを使用してインストールした場合は過去バージョンのPowerShell Coreは削除されるインプレースアップデートとなります。

プログラム名の変更

PowerShell Coreのプログラム名がpowershell(.exe)からpwsh(.exe)に変更されました。

細かい話はこちらのエントリをご覧ください。

SSHベースのPowerShellリモート処理

PowerShell Core 6.0から通信の下回りにSSHを使用するリモート接続であるPowerShell Remoting over SSHが導入されました。

細かい話はこちらのエントリをご覧ください。

ちなみに、LinuxやmacOSに対して従来のPSRPによるリモート接続も可能です。

既定のエンコーディングに関して

PowerShell Core 6.0からファイル主力に関わるエンコーディングが見直され、BOMなしUTF-8がデフォルトエンコーディングになりました。

細かい話はこちらのエントリをご覧ください。

&によるバックグラウンドジョブ実行

PowerShell Core 6.0からパイプラインの最後にアンパサンド(&)を付けることでジョブを起動することができる様になりました。

細かい話はこちらのエントリをご覧ください。

Dockerサポート

PowerShell CoreはDocker上でも動作させることが可能です。

Docker Hubに公式なもの・非公式なもの合わせてイメージが公開されており、以下に一例を挙げておきます。

またGitHub上で各種OS用のDcokerfileも公開されています。

セマンティックバージョニング

PowerShell Core 6.0からセマンティックバージョニング 2.0と互換のあるバージョン番号が付く様になりました。
$PSVersionTable.PSVersionの型が従来のSystem.Versionから新しいSystem.Management.Automation.SemanticVersionに変更されています。

# PowerShell 5.1まで
PS C:\> ($PSVersionTable.PSVersion).GetType().FullName
System.Version

# PowerShell 6.0
PS C:\> ($PSVersionTable.PSVersion).GetType().FullName
System.Management.Automation.SemanticVersion

ただし、運用としては厳密にセマンティックバージョニングを適用する気は無く、半年ごとのリリースでマイナーバージョンを上げるものとなっています。 (次のリリースはPowerShell Core 6.1になる予定ですがこれには確実に破壊的変更も含まれるでしょう。この場合セマンティックバージョニングを厳密に適用するならPowerShell Core 7.0にしなければなりません。)
あくまでもバージョニングの体系を互換させるのと、アルファ、ベータリリースに対応するためだけに導入した感じです。

言語機能の更新

Unicode文字のエスケープ (#3958)

発端は#2751のIssueで、新しくUnicode文字をエスケープするための構文が追加されました。

`u{CodePoint}の形式でエスケープすることが可能です。

Write-Host "`u{1F363}  `u{1F37A}"

f:id:stknohg:20180209162834p:plain

(通常のコンソールでは絵文字を表示できないためHyperを使っています)

エスケープキャラクター `e のサポート

ESCをエスケープするために`eがサポートされました。

ConvertTo-Jsonの更新 (#4318)

ConvertTo-Jsonコマンドレットに-EnumsAsStringパラメーターが追加され、列挙型を文字列に変換する機能が追加されました。

# PowerShell 5.1 まで
PS C:\> [System.Management.Automation.ActionPreference]::Ignore | ConvertTo-Json
4

# PowerShell Core 6.0 から
PS C:\> [System.Management.Automation.ActionPreference]::Ignore | ConvertTo-Json -EnumsAsString
"Ignore"

範囲演算子がChar型をサポートする様になりました (#5026)

範囲演算子(..演算子)が拡張され、Int型だけでなくChar型も扱える様になりました。

細かい話はこちらのエントリをご覧ください。

読み取り専用であるべき変数が上書きされてしまう問題が修正されました

おそらく#2400の修正を指していると思われます。

[datetime]$HOME=1

の様な本来であれば上書きされてはいけない変数($HOME)が上書きされない様に変更されています。
(従来のWindows PowerShellでは上記コードで$HOME変数の内容が更新されてしまう。)

ドットソース演算子で読み込んだScript Cmdletの自動変数のスコープが修正されました (#4709)

もとのIssueは#4688

pwsh(.exe) -Fileパラメーターおよび、.演算子からソースを読み込みScript Cmdletを呼び出した際の自動変数(元のIssueでは$PSScriptRoot)が正しく設定されない不具合が修正されています。

-split演算子に'Singleline, Multiline'オプションを同時に指定した際の挙動が改善されました (#4721)

PowerShell 5.1までは-split演算子のオプションにSingleline, Multilineを同時に指定するとパラメーターエラーとなりましたが、PowerShell 6.0ではこの制約が取り除かれました。

もともと内部で使わているC#のRegExクラスではSplitOptions.SinglelineSplitOptions.Multilineが併用可能であり、この制約を取り除いても挙動に問題はないと判断された様です。

PowerShellエンジンの更新

$PSVersionTable でサポートするプロパティが増えました

$PSVersionTableに以下のプロパティが増えました。

  • PSEdition : PowerShellのエディション
  • GitCommitId : ソースのコミットID
  • OS : 実行OS情報
  • Platform : プラットフォーム名

$PSVersionTable から BuildVersion プロパティが削除されました

このBuildVersionプロパティはWindowsのビルドと強く結びついているため廃止され、代わりにGitCommitIdプロパティが導入されています。

$PSVersionTable から ClrVersion プロパティが削除されました

PowerShell Coreの基盤である.NET Coreではこのプロパティの値が意味を成さなくなったため廃止されました。

OSを識別するための自動変数が追加されました

OSを識別するために以下の自動変数が追加されました。

  • $IsWindows : Windowsの場合$True。
  • $IsMacOS : macOSの場合$True。
  • $IsLinux : Linuxの場合$True。

割とそのままですが、内部的には.NET CoreのSystem.Runtime.InteropServices.RuntimeInformationクラスの機能が使われており、

# $IsWindows
[System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::Windows)

# $IsMacOS
[System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::OSX)

# $IsLinux
[System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::Linux)

といったコードと等価になります。

PowerShell起動時のバナーにGitCommitIdが追加されました

こちらは割とそのままで、pwsh(.exe)起動時のバナーにGitCommitId(v6.0.1の部分)が表示される様になりいちいち$PSVersionTableの内容を参照しないでもバージョン確認ができる様に改善されています。

f:id:stknohg:20180209162906p:plain

$PSHOMEディレクトリにJSONの設定ファイルが追加されました

$PSHOMEディレクトリにpowershell.config.jsonというJSONの設定ファイルが配置されPowerShell起動時の設定を記述することが出来る様になりました。

今のところWindowsのみ配置されており、初期状態では、

{"Microsoft.PowerShell:ExecutionPolicy":"RemoteSigned"}

とExecutionPolicyの既定値が設定されています。

そのほかのパラメーターについては順次ドキュメント化されていく(#5885)様ですが、まだまとまった資料はありません。

とりあえず現状ログ出力にかかわるパラメーターに関する記述だけ見つけました。

  • LogIdentity
  • LogChannels
  • LogLevel
  • LogKeywords

といった設定項目がある様です。

Windows EXEの実行中はパイプラインをブロックしません

こちらはどのIssueに対する改善か名言されていないのですが、Windowsに限らずネイティブコマンドをパイプラインする際に処理がブロックされる、レスポンスが悪いといった問題がいくつかあり、それに対する改善がPowerShell Core 6.0でされています。

COMコレクションの列挙が有効になりました (#4553)

元になるIssue(#3775)およびPull Request(#4553)を読む限りでは新機能ではなく互換性の保証の様に見受けられます。

現状の.NET CoreではCOMオブジェクトの列挙がサポートされていないためPowerShellで独自に実装してPowerShell 5.1との互換を取ったものの様です。

Windows PowerShellとの後方互換性について

以前ロードマップを公開した時にも述べましたが、PowerShell CoreにはWindows PowerShellとの互換性を持ちWindows PowerShellを置き換えていきたい展望があります。(現状は多くの互換性が切り捨てられています)

互換性保持の一つとしてWindows PowerShellのモジュールをPowerShell Coreから利用するためにWindowsPSModulePathというモジュールが用意されています。

このモジュールにはAdd-WindowsPSModulePathコマンドが用意されており、これを実行するとPowerShell CoreのPSModulePath環境変数にWindows PowerShellのモジュールパスを追加し、PowerShell CoreからWindows PowerShellのモジュールを参照・実行可能になります。

Install-Module WindowsPSModulePath -Force
Add-WindowsPSModulePath

ただし、現状ではあくまでもパスを追加しているだけですので、実行されるモジュール側に互換性がなければ実行時エラーとなりますのでご注意ください。

コマンドレットの更新

ここから各種コマンドレットの更新情報について触れていきます。

新しいコマンドレット

新しく以下のコマンドレットが導入されました。
細かい説明は不要でしょう。

  • Get-Uptime
  • Remove-Alias
  • Remove-Service

Webコマンドレット

PowerShell Core 6.0では基盤が.NET Coreになったこともあり、Web Cmdletsの基盤がWebRequestクラスからHttpClientクラスに代わっており、中身は完全に作り替えられています。
これに伴いHTTP/HTTPSアクセスに係る古い仕様を取り除き新しい仕組みに対応できる様に修正されています。

これらの修正についてほとんどテキサスのMVPであるMark Krausさんが直しており、正直公式ドキュメントより彼のブログ等を見た方が良いでしょう。

JSONコマンドレット

JSON関連のコマンドレットはPowerShell Coreで内部的にJson.NETを使用する様に変更されています。
PowerShell Core 6.0ではこの変更に起因する修正や新機能の導入がされています。
また、ドキュメント化されていない部分で細かな挙動にWindows PowerShellとの差異が発生している様です。

ConvertFrom-Jsonに戻り値をHashTableにして返す-AsHashtableパラメーターが追加されました (#5043)

通常 ConvertFrom-JsonPSCustomObjectを返しますが、-AsHashtableパラメーターを指定した場合はHashTableを返します。

# 通常は PSCustomObjectを返す
PS C:\> '{"a" :', '"x"}' | ConvertFrom-Json | Get-Member

   TypeName: System.Management.Automation.PSCustomObject

   (後略)

# -AsHashtableをつけるとHashtableを返す
PS C:\> '{"a" :', '"x"}' | ConvertFrom-Json -AsHashtable | Get-Member

   TypeName: System.Collections.Hashtable

   (後略)

ConvertTo-Jsonの出力フォーマットが改善されました (#2787)

PowerShell Core 6.0で出力されるJSONはWindows PowerShellよりコンパクトになっています。
基本的には4タブから2タブになったのとクローズブレース(}など)の付き方が変更されています。

# PowerShell 5.1まで
PS C:\> [ordered]@{'one' = 1; 'two' = @{'value' = 2}} | ConvertTo-Json
{
    "one":  1,
    "two":  {
                "value":  2
            }
}

# PowerShell Core 6.0
PS C:\> [ordered]@{'one' = 1; 'two' = @{'value' = 2}} | ConvertTo-Json
{
  "one": 1,
  "two": {
    "value": 2
  }
}

ConvertTo-JsonNewtonsoft.Json.Linq.JObject型のオブジェクトのシリアル化がサポートされました (#5141)

Json.NET対応の一環での機能改善の様です。

複数行にかかわるデータに対するConvertFrom-Jsonの挙動が改善されました (#3823)

元のIssueを見てもいまいち理解しきれない部分があったのですが、

Get-Content .\sample.json | ConvertFrom-Json

の様な場合でGet-Contentの結果であるstring[]の入力を正しく解釈できないことがありそれを改善した様です。

System.Arrayに定義されていたCountエイリアスプロパティが削除され、JSONの配列を扱うケースで不要なCountプロパティが付かない様に改善されました (#3231)

例えば

(1,,"2") | ConvertTo-Json

といった例の場合、Windows PowerShellでは

# PowerShell 5.1まで
PS C:\> (1,,"2") | ConvertTo-Json
[
    1,
    {
        "value":  [
                      "2"
                  ],
        "Count":  1
    }
]

と配列のCountプロパティが出力されてしまいますが、PowerShell Core 6.0からは

# PowerShell Core 6.0
PS C:\> (1,,"2") | ConvertTo-Json
[
  1,
  [
    "2"
  ]
]

の様にCountプロパティは付かない様に改善されています。

CSVコマンドレット

Import-CsvConvertFrom-CsvPSTypeNameのサポートが追加されました (#5389)

これはExport-CsvConvertTo-CSVで付与されるPSTypName(#TYPE [型名]の部分)がImport-CsvConvertFrom-Csvで正しく反映されかった点の改善となります。

Import-Csvの行区切りにCRLFだけでなくCRLFが使える様になりました (#5363)

クロスプラットフォーム化対応での改善です。

Export-CsvConvertTo-Csv-NoTypeInformationを既定値に変更しました (#5164)

こちらは発端となったIssue(#5131)を見ればわかりますが、CSV出力する際にPSTypeNameのヘッダが出力されるのが煩わしいという声が多かったため改善されました。

サービスのコマンドレット

Get-Serviceなどの戻り値であるServiceControllerクラスのプロパティにUserNameDescriptionDelayedAutoStartBinaryPathNameStartupTypeが追加されました (#4907)

UserVoiceのこちらの要望に対する対応として機能追加されました。
ただしWindows PowerShellにバックポートされる見込みは低いでしょう...

Set-Serviceに資格情報を設定するための-Credentialパラメーターが追加されました (#4844)

New-Serviceには-CredentialパラメーターがあるのにSet-Serviceにはなかったことに対する改善です。

その他のコマンドレット

結構大きな変更もあるのですが本エントリでは公式ドキュメントの内容を軽く補足する程度にとどめておきます。

Get-ChildItemにシンボリックリンクを追跡するための-FollowSymlinkパラメーターが追加されました (#4020)

加えてシンボリックリンクにより一度走査したディレクトリを多重に走査しないかチェックする機能がサポートされています。

Add-Type-LanguageCSharpVersion7が追加されました (#3933)

余談ですが、今後の方針としてAdd-Typeで使える言語からJScriptが削除されC#のバージョン指定も簡素化される予定です。

サポートされないAPIを使っていたためMicrosoft.PowerShell.LocalAccountsモジュールは削除されました (#4302)

「サポートされない」の意味に関して.NET CoreとしてのサポートなのかWindowsのサポートなのかは不明です。

サポートされないAPIを使っていたためMicrosoft.PowerShell.Diagnosticsモジュールの*-Countrコマンドレットは削除されました (#4303)

「サポートされない」の意味に関して.NET CoreとしてのサポートなのかWindowsのサポートなのかは不明です。

Split-Path-Extension-LeafBaseパラメーターが追加されました (#2721)

-Extensionは拡張子を、-LeafBaseは拡張子なしのファイル名を取得するためのスイッチパラメーターです。

PS C:\> Split-Path C:\Temp\sample.txt
C:\Temp
PS C:\> Split-Path C:\Temp\sample.txt -Extension
.txt
PS C:\> Split-Path C:\Temp\sample.txt -LeafBase
sample

Sort-Object-Top-Bottomパラメーターが追加されました

概ね以下のコードと等価になります。

# PowerShell 5.1まで
1,3,2,5,4 | Sort-Object | Select-Object -First 2
1,3,2,5,4 | Sort-Object | Select-Object -Last 2

# PowerShell 6.0より
1,3,2,5,4 | Sort-Object -Top 2
1,3,2,5,4 | Sort-Object -Bottom 2

Get-Processなどの戻り値のSystem.Diagnostics.ProcessクラスにコードプロパティParentが追加され、該当プロセスの親プロセスを取得できる様になりました (#2850)

地味ですが便利な改善だと思います。

# PowerShell Core 6.0 から親プロセスの取得が容易に
PS C:\> (Get-Process -id $PID)

 NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
 ------    -----      -----     ------      --  -- -----------
     69    43.47      80.04       2.16    2484   1 pwsh

PS C:\> (Get-Process -id $PID).Parent

 NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
 ------    -----      -----     ------      --  -- -----------
    191   110.25     119.97   1,865.97    7500   1 explorer

Get-Processの結果を表示する際のメモリ使用量に関する列がKB単位からMB単位に変更されました

こちらは下図を見てもらうのが手っ取り早いでしょう。

f:id:stknohg:20180209164043p:plain

Out-String-NoNewLineパラメーターが追加されました (#5056)

こちらは割とそのままの改善です。
-NoNewlineをつけた時は改行コードがないことが見て取れます。

PS C:\> 'a' | Out-String | Format-Hex


           00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

00000000   61 0D 0A                                         a..


PS C:\> 'a' | Out-String -NoNewline | Format-Hex


           00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

00000000   61                                               a

Move-Item-Include-Exclude-Filterパラメーターの挙動が改善されました (#3878)

レジストリに対するRemove-Item*が使える様になりました (#4866)

レジストリのパスには*が使用可能なのですが、PowerShellでは*はワイルドカードとして扱われてしまうためRemove-Itemが正しく動作しないケースがあり、PowerShell Core 6.0でこれが修正されています。

Get-Credential-Titleパラメーターが追加されました

PowerShell Coreで認証ダイアログがCUI化したことによる機能追加になります。

Test-Connection-TimeOutパラメーターが追加されました (#2492)

ただし、Test-Connectionはまだ正式にPowerShell Coreに組み込まれていません...(#5328)

Get-AuthenticodeSignatureコマンドレットでTimeStamperCertificateプロパティが取得できなかったのが改善されました (#4061)

こちらは新機能というよりはベータ版で発生した不具合の改善ぽいです。

Get-Helpから-ShowWindowパラメータが削除されました (#4903)

.NET CoreではGUIサポートがないための変更です。
ただ、今後の方針としてConsole GUIおよびそのためのライブラリの導入を検討(#3957)しているのでいずれ復活するかも...しれません。

Get-Content-Delimiterを指定した際にデリミタ自身が残ってしまう不具合が解消されました (#3706)

こちらは以下の例を見てもらうのが手っ取り早いでしょう。

# PowerShell 5.1まではデリミタ "-" が結果に残ってしまう。
PS C:\> "00-01-02-03-04-05-06" > .\mac.txt
PS C:\> Get-Content .\mac.txt -Delimiter "-"
00-
01-
02-
03-
04-
05-
06

# PowerShell 6.0からこの挙動は改善されます
PS C:\> "00-01-02-03-04-05-06" > .\mac.txt
PS C:\> Get-Content .\mac.txt -Delimiter "-"
00
01
02
03
04
05
06

ConvertTo-HTML-Meta-Charset-Transitionalパラメーターが追加されました (#4184)

Get-ComputerInfoの実行結果にWindowsUBRWindowsVersionプロパティが追加されました

なお、現状Get-ComputerInfoはWindowsでのみ利用可能です。

Get-Verb-Groupパラメーターが追加されました

指定したグループだけを抽出可能になっています。

PS C:\> Get-Verb -Group Other

Verb AliasPrefix Group Description
---- ----------- ----- -----------
Use  u           Other Uses or includes a resource to do something

New-FileCatalogTest-FileCatalog-WhatIf-Confirmパラメーターが動作しなかったのが改善されました (#3074)

内部的にはShouldProcessのサポートが正しく実装されました。

Start-Process-WhatIfパラメーターが追加されました (#4735)

併せて-Confirmパラメーターも増えています。

多くの既存コマンドレットのパラメーターにValidateNotNullOrEmpty属性を追加しています

おそらく#2672の改善を指している様です。

引数なしのcdcd ~として動作する様になりました (#1534)

これはUnixのcdコマンドの挙動に合わせた変更となります。

テレメトリ

現在PowerShell Coreではテレメトリとして以下の情報を収集しています。

  • OSのプラットフォーム($PSVersionTable.OSDescription)
  • PowerShellの正確なバージョン($PSVersionTable.GitCommitId)

これをオプトアウトするには$PSHome\DELETE_ME_TO_DISABLE_CONSOLEHOST_TELEMETRYファイルを削除する必要があります。

ただ、この方式はAppImage版のPowerShell Coreなど$PSHOMEが読み取り専用となっているシステムでは使用できないため、オプトアウトの方法をファイルの削除から環境変数の設定に変更する計画が立てられており、PowerShell Core 6.0.2で採用される見込みです。(#6063)

最後に

とりあえずこんな感じです。
ボリュームがありまとめるのも一苦労でした。

VagrantのWinSSH Communicatorを試してみた

www.vagrantup.com

Vagrant 1.9.4からWindowsゲストに対してWindowsにポートされたWin32-OpenSSHを使いSSHで通信を行うWinSSH Communicatorが導入されており*1、先日この機能を知り気になったので試してみました。

WinSSH Communicatorは利用者が全然いないのかドキュメントがあまり整備されておらず本エントリの内容もかなりてさぐりな感じとなっています。

検証環境について

今回は以下の環境で検証しました。

ホスト

  • 64bit版 Windows 10 Pro (1703)
  • Vagrant 2.0.1
  • VirtualBox 5.2.6
  • OpenSSH Client 0.0.24.0

ゲスト

  • Windows Server 2016 (GUIあり)
    • OSインストール直後の状態に最新のWindows Updateを適用
  • OpenSSH Server 0.0.24.0

Win32-OpenSSH Serverのインストール

WinSSH CommunicatorではゲストのWindowsにWin32-OpenSSHをインストールしてsshdを動作させておく必要があります。

インストール手順

基本的に公式サイトの手順をそのまま実行するだけです。

github.com

本エントリでは最新のVer.0.0.24.0を使用します。
以降の作業は要管理者権限です。

# 要管理者権限
# OpenSSHのダウンロードと展開
# C:\Program Files\配下に展開しています
Invoke-WebRequest -Uri "https://github.com/PowerShell/Win32-OpenSSH/releases/download/0.0.24.0/OpenSSH-Win64.zip" -OutFile "OpenSSH-Win64.zip"
Expand-Archive -Path ".\OpenSSH-Win64.zip" -DestinationPath "$env:ProgramFiles"

# Pathの追加
[Environment]::SetEnvironmentVariable('PATH', [Environment]::GetEnvironmentVariable('PATH') + ";$(Join-Path $env:ProgramFiles "OpenSSH-Win64")")

# sshdのセットアップ
# カレントディレクトリを移動しておかないと ssh-keygen で作られるキーの場所が不正になる
cd (Join-Path $env:ProgramFiles "OpenSSH-Win64")
.\install-sshd.ps1

# ホストキーの作成とアクセス権の更新
.\ssh-keygen.exe -A
.\FixHostFilePermissions.ps1 -Confirm:$false

# Firewallを設定しポートを開けておく
New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server (sshd)' -Service sshd -Enabled True -Direction Inbound -Protocol TCP -Action Allow

続けて鍵認証を許可するためにsshd_configの以下のコメントを解除します。

#RSAAuthentication yes
#PubkeyAuthentication yes

↓ コメント解除

RSAAuthentication yes
PubkeyAuthentication yes

PowerShellからだと以下の様にして置換できます。

# sshd_configを更新し、鍵認証を許可する
cd (Join-Path $env:ProgramFiles "OpenSSH-Win64")
(Get-Content .\sshd_config -Encoding utf8) -replace '#RSAAuthentication yes','RSAAuthentication yes' -replace '#PubkeyAuthentication yes','PubkeyAuthentication yes' `
  | Out-String `
  | % { [Text.Encoding]::UTF8.GetBytes($_) } `
  | Set-Content -Path .\sshd_config -Encoding Byte

また、OpenSSHで接続した際のデフォルトシェルはコマンドプロンプトなのですが、レジストリ設定で変更することが可能です。
本エントリでは以下のコマンドを実行してPowerShellに変えておきます。

# OpenSSHのデフォルトシェルをPowerShellに変更
$RegPath = "HKLM:\SOFTWARE\OpenSSH"
If ( !(Test-Path $RegPath -PathType Container) ) {
    New-Item $RegPath
}
Set-ItemProperty $RegPath -Name DefaultShell -Type String -value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"

最後にsshdサービスの設定を変更し起動します。

Set-Service sshd -StartupType Automatic
Set-Service ssh-agent -StartupType Automatic
Start-Service sshd

これでOpenSSH Serverの設定は完了です。

Vagrant Boxの設定

WinSSH Communicatorを使う際のVagrant Boxの設定については残念ながらドキュメント化されていません。

基本的にはWinRM Communicatorを使う際の手順にSSHで接続するための設定を追加しておけば良いかと思います。
以前に書いたこちらのエントリを参考にしてください。

blog.shibata.tech

本エントリでは、上記エントリをベースにしつつできるだけ最小限の設定にできないか試し、以降の手順で動作させることができました。

1. WinRMの有効化

WinSSH CommunicatorではWinRMを使わないのでWinRMの設定は不要かと思っていたのですが、Vagrantのネットワーク設定にWinRMのバージョンチェックがあり、Communicatorの設定に関わらずこの処理が呼ばれているためWinRMを有効にしていないとvagrant up時に異常終了してしまいます。

今後この挙動は改善されるかもしれませんが現状WinSSH Communicatorを使う際もWinRMを有効にしておく必要があります。

PowerShellから以下のコマンドを実行しておいてください。

# 要管理者権限
winrm quickconfig -q
winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="512"}'
winrm set winrm/config '@{MaxTimeoutms="1800000"}'
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
winrm set winrm/config/service/auth '@{Basic="true"}'
sc.exe config WinRM start= auto

また、Vagrantと直接関連しないのですが、Windows Server 2012 R2以降であればSet-WSManQuickConfigを実行しておいた方が良いでしょう。

# Windows Server 2012 R2以降
Set-WSManQuickConfig -Force

blogs.technet.microsoft.com

2. シャットダウンイベントの追跡ツールの無効化(Server Core以外)

vagrant haltコマンド実行時にシャットダウンの問い合わせを出さない様にするためにシャットダウンイベントの追跡ツールの無効化を行います。

# このコマンドからシャットダウンイベントの追跡ツールを無効化した場合、ローカルグループポリシーエディタ上に反映されません。

# デフォルト状態ではReliabilityキーは存在していません。
$RegPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Reliability"
If ( ! (Test-Path $RegPath -PathType Container) ) {
    New-Item $RegPath
}
Set-ItemProperty $RegPath -name ShutdownReasonOn -Type DWord -value 0
Set-ItemProperty $RegPath -name ShutdownReasonUI -Type DWord -value 0

3. サーバーマネージャがログオン時に表示されないようにする(Server Core以外)

サーバーマネージャがログオン時に表示されないようにするため、以下の手順で現在ログオンしているユーザーとDefaultユーザーのレジストリ設定を変更しておきます。

# 現在ログオンしているユーザーと`Default`ユーザーの設定を更新します。
# この更新により新しくユーザープロファイルが作られる際にサーバーマネージャーを自動的に表示しない様にしています。

# Change Current User Setting
$RegPath = "HKCU:\SOFTWARE\Microsoft\ServerManager"
If ( Test-Path $RegPath -PathType Container ) {
    Set-ItemProperty $RegPath -name DoNotOpenServerManagerAtLogon -Type DWord -value 1
}

# Change 'Default' User Setting
$DefaultRegFile = Join-Path (Split-Path $env:USERPROFILE -Parent) 'default\NTUSER.dat'
try {
    reg load HKLM\DefaultUser "$DefaultRegFile"
    $RegPath = "HKLM:\DefaultUser\SOFTWARE\Microsoft\ServerManager"
    If ( Test-Path $RegPath -PathType Container ) {
        Set-ItemProperty $RegPath -name DoNotOpenServerManagerAtLogon -Type DWord -value 1
    }
} finally {
    reg unload HKLM\DefaultUser
}

4. パスワードの複雑性の無効化

次の手順で作るvagrantユーザーに対して単純なパスワードを設定するためにパスワードの複雑性を無効化します。 PowerShellから以下のコマンドを実行してください。

$TempCfgFile = ".\_secpol_{0}.inf" -F [System.IO.Path]::GetRandomFileName()
# Export temp file
secedit /export /cfg $TempCfgFile
# Disable PasswordComplexity and Change password age.
( Get-Content $TempCfgFile -Encoding Unicode ) -replace 'PasswordComplexity = 1','PasswordComplexity = 0' -replace 'MaximumPasswordAge = 42','MaximumPasswordAge = -1' | Out-File $TempCfgFile
# Update security policy
secedit /configure /db $env:windir\security\database\secedit-new.sdb /cfg $TempCfgFile /areas SECURITYPOLICY
# Delete temp file
If( Test-Path -Path $TempCfgFile -PathType Leaf ) {
    Remove-Item -Path $TempCfgFile
}

ちなみにWindows Server 2016であれば上記の代わりにBackup-SecurityPolicyRestore-SecurityPolicyコマンドレットを使うことも可能です。

#
# Windows Server 2016以降であれば以下の様にしても良い。
#
$TempCfgFile = ".\_secpol_{0}.inf" -F [System.IO.Path]::GetRandomFileName()
# Export temp file
Backup-SecurityPolicy $TempCfgFile
# Disable PasswordComplexity and Change password age.
( Get-Content $TempCfgFile -Encoding Unicode ) -replace 'PasswordComplexity = 1','PasswordComplexity = 0' -replace 'MaximumPasswordAge = 42','MaximumPasswordAge = -1' | Out-File $TempCfgFile
# Restore security policy
Restore-SecurityPolicy -Path (Resolve-Path $TempCfgFile)
# Delete temp file
If( Test-Path -Path $TempCfgFile -PathType Leaf ) {
    Remove-Item -Path $TempCfgFile
}

5. Vagrantユーザーの追加

ローカルユーザーにVagrantユーザーを追加し、Administratorsグループに参加させます。
ユーザーのパスワードは"vagrant"にしています。

ADSIを使う場合は以下の手順となります。

# Create Vagrant User
$Computer = [ADSI]"WinNT://$Env:COMPUTERNAME,Computer"
$NewUser = $Computer.Create("user","vagrant")
$NewUser.SetPassword("vagrant")
$NewUser.SetInfo()
$NewUser.FullName = "Vagrant"
$NewUser.UserFlags = 65536 # ADS_UF_DONT_EXPIRE_PASSWD
$NewUser.SetInfo()
# Join Administrators Group
$AdminGroup = [ADSI]"WinNT://$Env:COMPUTERNAME/Administrators,group"
$AdminGroup.Add($NewUser.Path)

Windows Server 2016であれば上記の代わりにNew-LocalUserAdd-LocalGroupMemberコマンドレットを使うことも可能です。

#
# Windows Server 2016以降であれば以下の様にしても良い。
#
New-LocalUser -Name "vagrant" -Password (ConvertTo-SecureString "vagrant" -AsPlainText -Force) -AccountNeverExpires -PasswordNeverExpires
Add-LocalGroupMember -Member "vagrant" -Group "Users"
Add-LocalGroupMember -Member "vagrant" -Group "Administrators"

6. Vagrantユーザーの公開鍵配置

この手順はWinSSH Communicatorに特有のものです。

VagrantがゲストVMに初回接続する際に必要な鍵(insecure_key)をVagrantユーザーの~\.sshディレクトリに配置しておきます。

この処理を行うためにはユーザープロファイルが出来ている必要があるため、前項の手順でVagrantユーザーを作った後にVagrantユーザーでログインして行ってください。

# Vagrantユーザーでログインして行う前提

# ~\.sshディレクトリの作成
if ( -not (Test-Path "~\.ssh" -PathType Container)) {
    mkdir "~\.ssh"
}
# insecure_key の配置
Invoke-WebRequest -Uri "https://raw.github.com/mitchellh/vagrant/master/keys/vagrant.pub" -OutFile "~\.ssh\authorized_keys"
# キーのアクセス権を設定
$acl = Get-Acl "~\.ssh\authorized_keys"
$ar = New-Object System.Security.AccessControl.FileSystemAccessRule("NT Service\sshd", "Read", "Allow")
$acl.SetAccessRule($ar)
Set-Acl "~\.ssh\authorized_keys" $acl

ちなみに、このinsecure_keyconfig.ssh.insert_key = trueと設定されていれば初回接続後に別の新しい鍵に差し替えられる様になっています。

Boxファイルの作成と登録

Boxファイルの作成と登録についてはこちらのエントリを参照してください。

blog.shibata.tech

Vagrantfile設定例

WinSSH Communicatorを使う際はVagrantfileのCommunicatorを

config.vm.communicator = "winssh"

の様にwinsshと指定するだけでOKです。
SSHに関する設定はSSH Communicatorと共用されており、そのほかの設定についてはWinRM Communicatorの場合と同様です。

以下に簡単な設定例を挙げておきます。

# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|

  config.vm.box = "Win2016SSHBox"
  config.vm.guest = "windows"

  # WinSSH Communicatorを使う
  config.vm.communicator = "winssh"
  
  # VMの設定は適当に
  config.vm.provider "virtualbox" do |vb|
    vb.gui = true
    vb.memory = "4096"
    vb.customize ["modifyvm", :id, "--cpus", "2"]
    vb.customize ["modifyvm", :id, "--paravirtprovider", "hyperv"]
  end
end

実行例

ここまで設定できればあとはvagrant upでゲストVMが立ち上がり良しなに設定がなされるはずです。

f:id:stknohg:20180119153233p:plain

vagrant sshコマンドでゲストに接続することが可能です。

f:id:stknohg:20180119153250p:plain

ちなみに、上手く動作しない場合は--debugオプションをつけて実行し、デバッグログを読んで気合で対処してください。

注意事項

ざっとWinSSH Communicatorについて説明してきましたがいくつか注意事項があります。

vagrant ssh -cコマンドが正しく動作しません

vagrant sshコマンドには-c [実行したいコマンド]をつけることでコマンドの単体実行が可能です。

ただ、このオプションの実装が、

[config.ssh.shellで設定されるシェル] -c [実行したいコマンド]

となっており、config.ssh.shellのデフォルトはbash -lである点、-cがハードコーディングされている点からWindowsゲストに対しては実質使えない*2ものとなっています。

一応、

config.ssh.shell = "powershell"

とすれば上手くいきそうなのですが、私の環境ではコンソールの描画が上手くいかずコマンドが成功しているのか判別できませんでした。

これは明らかに実装の考慮漏れなのでいずれ改善されるとは思います。
(気になるひとはIssue上げてください...)

Windows 10 Fall Creators Updateに組み込まれているOpenSSHを使う場合

本エントリを書くにあたり、事前にWindows 10 Fall Creators Update(1709)に組み込まれているOpenSSH Serverの機能を使った検証を行ったのですが、OSに組み込まれているOpenSSH Serverは現状ホストキーがed25519鍵しか対応しておらず、そのためかVagrantで上手く操作できませんでした。
根本的な原因を突き止めることはできませんでした...

github.com

このOpenSSH Serverの問題は今後のリリースで解消される見込みですがVagrant BoxにWindows 10を使う際はご注意ください。

*1:通常Windowsゲストに対してはWinRMで通信します。

*2:WLSがある環境ならワンチャンあるかも...