tech.guitarrapc.cóm

Technical updates

第24回◯◯o◯裏番組シェル芸勉強会 を PowerShell と C# でやってみる #シェル芸

久々のシェル芸です。

Bsh on Ubuntu on Windows でやろうと思ったのですが、手元の環境が入らないのでそっとじ..。

今回は途中で飽きるまでということで、やってみました。

目次

問題

安定の一撃サイトです。

https://blog.ueda.asia/?p=8639

コード全体

今回の回答です。BoUW かなぁと思いつつ、PowerShell がインストールできなかったのでやめました*1

6問まで回答しています。

言語 回答数 環境
C# 1,2,3 問 LinqPad
PowerShell 1,2,3,5,6 問 PowerShell.exe on Windows 10

gist.github.com

回答

縛りは2つ、「ワンライナー」、「ファイル読み込み」、「複数行になっても問題ない」、です。

Q1. Q1ファイルについて、次のような出力を得てください。

Q1 ファイル

玉子 卵 玉子 玉子 玉子 玉子
玉子 玉子 卵 卵 卵 玉子
卵 玉子 卵 玉子 玉子 玉子
卵 玉子 卵 卵 卵 卵
玉子 卵 玉子

出力

玉子:5 卵:1 
玉子:3 卵:3 
玉子:4 卵:2 
玉子:1 卵:5 
玉子:2 卵:1 

PowerShell / C# ともに行ごとに読み取ってほげもげでした。

C# だと、Select して GroupBy して数をまとめて、OrderBy して並びを整えて、Selectで成形した文字列出力です。

PowerShell も同様です。オペレータの挙動が微妙に違うのですが大枠同じです。

Q2. 次のようなテキストについて、繰り返し出てきた文字の2つ目以降を省いて出力してください。

例えばQ2のファイル

へのへのもへじ

の場合、「へのもじ」が正解の出力になります。

これは、C#では .Distinct() を使えば一瞬です。

PowerShell でも同様ですが、2つ一応用意しました。一つはHashtable のキーが重複不許可なことを利用しているのと、同様に Distinct() です。Hashset でもなんでもいいと思いました。

ちなみに using NameSpace を使っていいなら、

using Namespace System.Linq;
cat .\Q2.txt -Encoding utf8 | %{[string]::new([Enumerable]::Distinct([char[]]$_))}

です。

Q3. 第一フィールドをキーにして%%でレコードを区切ってください。
金 日成
キム ワイプ
金 正日
キム タオル
金 正男

というデータを、

%%
キム タオル
キム ワイプ
%%
金 正男
金 正日
金 日成
%%

区切りをテキストで出す意味...は、おいておいてやります。

C# では、初期化変数を用意しちゃいました。なしで書くとどうなるか思いつかない当りできない子です。あと、ずるいと思いつつTuple使ってます。

PowerShell は、awk と同じ要領ですね。

Q4. Q4.xlsxのA1のセルには数字が書いてあります。その数字を出力してください。A4には文字列が書いてあるので余裕がある人はそれも特定してみましょう。

やりません*2

Q5. ファイルQ5について、xに好きな数を代入して各行の式を計算してください。
x + x^2
x + 1/x
x*x*x

余裕のある人は、例えばxに2を代入したければ、

$ echo 2 | ...

というようにecho <代入したい数>から始めてワンライナーで解いてみてください。

echo から初めてと言われた時点で C# はやめて、PowerShell のみで。また、2^2 という構文がないため、Math.Pow(x,2) に書き換えています。

PowerShell の場合、ScriptBlock を Invoke という手と Invoke-Expression を使うのが楽です。C# も似たようなものですね。

今回は Invoke-Expression (iex) を使っています。

Q6. 「玉子」と「卵」の数を数えて、数が少ない方を数が大きい方で置換してください。
卵卵玉子玉子玉子玉子玉子卵卵卵玉子玉子卵玉子玉子玉子玉子卵卵玉子卵玉子卵卵玉子卵玉子

ずるしちゃいました。汎用性がないのであまり好きじゃないのですが。

Q7. 飽きました
Q8. 飽きました

まとめ

C# というか、Linq できれいにつながると楽しいですね。PowerShell でつないでいくよりも可読性が圧倒的に高いのはいいなぁと。

ただ、コードは長くなりがちというのは仕方ないとはいえ感じました。

雑魚回答なので、もっと良い回答をぜひ。

*1:bashではやりません

*2:COM使ったら怒られそうだし。PowerShell はモジュール使っていいなら1 Cmdletでできちゃいます

Windows 資格情報マネージャーをPowerShell DSC で構成管理するリソースを公開しました

PowerShell DSC は PowerShell が目指してきた姿の1つの目標であり、強力な技術基盤です。

PowerShell は DSC を提供にするにあたり、OMI の実装を WMI から クロスプラットフォームな CIMに移してきました。実際 Linux で PowerShell DSC を利用する PowerShell-DSC-for-Linux も公開されています。

github.com

また WMF5.1 からは、PowerShell Core、つまり .NET Core 実装のクロスプラットフォーム PowerShell も提供されます。シェルとして使い心地に課題は残るものの、着実にPowerShell でやると楽な部分、という意味での存在価値は間違いなく存在します。

さて、そんな PowerShell ですが WMF5 からは、DSC の実行ユーザーが BuiltIn/SYSTEM だけでなく任意のユーザー指定が可能になりました。これによりユーザープロファイルに依存する処理も管理可能になりました。

ということで、先日 資格情報マネージャーのリソースを公開したのでご紹介です。*1

目次

資格情報マネージャーの操作

以前 資格情報マネージャーを PowerShell で操作する方法を示しました。

tech.guitarrapc.com

現在はもう少しコードを修正していますが、ようはこれを PowerShell DSC でやれるようにします。

PowerShell Gallery からの導入

WMF5 以降の環境ではワンライナーでDSCリソースを利用できます。

Install-Module -Name GraniResource -Scope AllUsers

幸いにも PowerShell Team が公開している以外のリソースとして上位に位置するダウンロード数のようで嬉しい限りです。

www.powershellgallery.com

実はリポジトリ名を DSCResources から GraniResource へこっそり変えていますが、ソースコードも変わらず公開しています。*2

github.com

利用

Grani_CredentialManager を利用することで、資格情報マネージャーを管理できます。デフォルトは、DSCの実行ユーザー、つまり SYSTEM アカウントが対象です。このアカウントができるということは、SYSTEM で動作するサービスの資格情報を管理できます。

gist.github.com

また、PsRunAsCredential を指定することで任意のアカウントの資格情報も操作できます。

gist.github.com

Grani_CredentialManager について

前回のリソース作成から、PowerShell チームへの今トリビュートを重ねる中で得た知見を使って少し書き方を変えました。

本体とヘルパーの分離

いわゆる *-TargetResource が PowerShell DSC を関数で記述したときの本体です。*3

今回のリソースは、ロジックを C# に閉じ込めていたこともあり本体の処理は非常にシンプルです。が、それでもヘルパーとなる補助関数は *helper.psm1 として分離しました。

ポイントは .psm1、つまりモジュールとしての分離です。本体の Grani_CredentialManager.psm1 で公開していないので内部モジュール扱いなのですが、Pester テストを含めた取り扱いの良さから .ps1 ではなく .psm1 にしています。この手法は、xTimeZone リソースでも行っておりPowerShell におけるプライベートな関数を閉じ込めるにあたりとてもいい手法だと思います。

github.com

呼び出しもシンプルですしね。

Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath "Grani_CredentialManagerHelper.psm1") -Verbose:$false -Force
PowerShell のまずいポイント対策

PowerShell には致命的なポイントがあります。それが、バイナリモジュールを利用する場合 DLL をロックしてしまうということです。つまり、C# で書いてビルドしたDLL を DSC などで利用すると、DSC実行中はファイルロックされます。これはデプロイの観点からみると非常に問題で、Windows 嫌いになります。その対策として、以前 Assembly.Load を利用したダイナミックモジュールによるロック回避も提示しました。

tech.guitarrapc.com

tech.guitarrapc.com

しかし PowerShell DSC に限らず、この手法は正直いやです。人道的とか言いながら逆説的な複雑性を増しています。

そこで今回をはじめとして、PowerShell の Add-Type Cmdlet を利用した C# コードのオンザフライコンパイルによるコード注入を好んで使っています。これならばファイルロックを回避できます。問題になりえるのは、同一AppDomain への 同一名前空間、クラス、シグネチャの多重インポートですが PowerShell DSC は毎回の実行で別AppDomain を起動するので問題ありません。

具体的にはこのC#コード を、ヘルパーとして差し込んでいます。

    $code = Get-Content -Path (Join-Path -Path $PSScriptRoot -ChildPath "CredentialManager.cs") -Raw
    $referenceAssemblies = @("System", "System.Linq", "System.ComponentModel", "System.Management.Automation", "System.Runtime.InteropServices")
    Add-Type -TypeDefinition $code -ReferencedAssemblies $referenceAssemblies -ErrorAction SilentlyContinue;

あとは、PowerShell から扱いやすいように薄くラッパを作って、DSC本体から呼び出すだけです。

PowerShell Team 公式リソースのテスト

PoweShell Team は、DSC リソースに関して、DscResources/CONTRIBUTING.mdにコントリビューションガイドラインを公開しています。

Grani_CredentialManager は、ガイドラインに沿うようにテストを組んであります。今後 DSC リソースを書いて公開する場合のサンプルにでもなれば幸いです。

https://github.com/guitarrapc/GraniResource/tree/master/Test/Grani_CredentialManagergithub.com

特にこの辺りは基礎となります。

$global:dscModuleName = 'GraniResource'

# Import
$modulePath = (Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)).Replace("Test","DSCResources")
$global:dscResourceName = (Split-Path -Path $modulePath -leaf)
$moduleFileName = $global:dscResourceName + ".psm1"
Import-Module -Name (Join-Path -Path $modulePath -ChildPath $moduleFileName) -Force

# Prerequisite for Initialize-TestEnvironment in Domain Environment
Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process

# Initialize
[String] $moduleRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $Script:MyInvocation.MyCommand.Path))
if ( (-not (Test-Path -Path (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests'))) -or `
     (-not (Test-Path -Path (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) )
{
    & git @('clone','https://github.com/PowerShell/DscResource.Tests.git',(Join-Path -Path $moduleRoot -ChildPath '\DSCResource.Tests\'))
}
else
{
    & git @('-C',(Join-Path -Path $moduleRoot -ChildPath '\DSCResource.Tests\'),'pull')
}
Import-Module (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force
$TestEnvironment = Initialize-TestEnvironment -DSCModuleName $global:dscModuleName -DSCResourceName $global:dscResourceName -TestType Unit

# Begin Test
try
{
    InModuleScope $global:dscResourceName {
        # テストロジック
    }
}
finaly
{
    Restore-TestEnvironment -TestEnvironment $TestEnvironment
}

コントリビューションガイドの通り、PesterDscResource.Tests を使うのがいいでしょう。もう少し パイプラインベースでテストを組めると書きやすいんですが、まだまだフレームワークもなく事実上 Pester しか確立したテスト手法がないですね。

DscResource.Testsは AppVeyor でのテストを通すことを前提で組んでるのか、ローカルでは妙な挙動をするのでバグ報告もしておきました。とりあえず現状でもふつーに使えるはずです。*4

github.com

github.com

まとめ

これで資格情報マネージャーも PowerShell DSC でハンドルできました。そもそも 資格情報が必要になる処理は RunDeck のように外から管理したところですが、なかなか難しいです。

悩ましいものの、いったんは目指した課題解決はできたので試していただけると幸いです。

*1:資格情報マネージャーは当然 Windows 固有実装なんですけどね。

*2:git からの直接ダウンロードで利用しやするための変更です。ご理解いただければと思います。

*3:ちなみにSet/Get/Test がクラスで書いたときの本体です

*4:ゴミカス残したりするのでアレですが

PowerShell 5.0 (WMF5) における PULL Server 構築時のPSDesiredStateConfiguration_1.0 問題

最新情報

2016/8/6 WMF5.1 Preview で本件が修正されました。Windows 10 Anniversary Update から確認ができます。

WMF5 においては、ワークアラウンドを利用するしかないのでご注意ください。

以前の状況

WMF5 がリリースされて数か月たちました。そろそろ皆様の環境も PowerShell 5.0 に置き換わったころではないでしょうか?特に DSC に関しては、これまで CIM Method を直接たたかなくては操作ができなかった部分が、Cmdlet で置き換えられています。また、ConfigurationId も撤廃され、PULL Server から PULL Node への mof document 配信も格段に楽になりました。

と、いいこと尽くめならいいのですが、まだまだ発展途上の技術です。バグも多くあります。ことごとく踏み抜いて対処してきましたが、1つ皆様の投票 (Vote) ご協力いただきたい事案があります。PowerShell Team から User Voiceを依頼されたので挙げましたが、投票数によって優先順位が変わるため PULL Server をお使いの(予定も含めて)方はぜひ清き一票を... (うさんくさい

https://windowsserver.uservoice.com/forums/301869-powershell/suggestions/15496698-minimumcompatibleversion-cause-cannot-find-modulewindowsserver.uservoice.com

目次

概要

DSC を PULL で組んでおいたとしましょう。PULL クライアントが PULL サーバーから Update-DscConfiguration や 定期Consistency Check で最新mof を取得したときに、以下のエラーがおこる状況がありえます。

Cannot find module PSDesiredStateConfiguration_1.0 from the server https://DSCPull:8080/PSDSCPullServer.svc/Modules(ModuleName='PSDesiredStateConfiguration',ModuleVersion='1.0')/ModuleContent";

日本語でも、同様の意味で PSDesiredStateConfiguration_1.0 を PULL Server から 取得できません という内容です。

このエラーが出たが最後、このPULLクライアントは対処しない限り、Consistency Check が実行できなくなります。つらい。

どういうときにおこるのか

初めに、WMF5 にしようとしている方が不安に思わないように条件を明示します。

  • WMF4 では起こりません
  • WMF5 でも WMF4 と全く同じ Configuration を使っていると起こりません

つまりタダのアップグレードなら発生しません。やったね。必ずしも起こるわけではなく、次の条件を満たしたときに発生します。

  • PULL クライアントが PULL サーバーから モジュールを取得する
  • Configuration で、PSDesiredStateConfiguration リソースを利用している
  • MinimumCompatibleVersion が 2.0 になっている

端的に言うと次の状況をすべて満たしたときに必ず発生します。

  • Import-DscResource -ModuleName PSDesiredStateConfiguration -ModuleVersion 1.1 を明示していない
  • WMF 5 で追加された Configuration のプロパティ、例えば PsDscRunAsCredential を使う
  • PSDesiredStateConfiguration のいずれかのリソースを利用している
  • PSDesiredStateConfiguration 以外のカスタムリソースを併用している
  • Configurationをネストしている
  • PULL モードを利用した PULL クライアントを利用している

何が問題なのか

少し説明しましょう。

Import-DscResource -ModuleName PSDesiredStateConfiguration -ModuleVersion 1.1 を明示していない

PowerShell DSC を使っている方の多くは数10の Configuration を用意して、それぞれで様々なリソースを利用すると思います。しかし、Configuration で 利用するリソースは、Import-DscResource を使ってインポートしないと利用できません。*1

また、リソースはどんどんアップデートされるので、PULL Server に最新リソースだけ配置することで、Import-DscResource でリソースのバージョンを明示せず最新版のみを自動的に使うようにするのではないでしょうか。*2

ここはいいのです。当然の ユーザーシナリオです。

問題の始まりは、 WMF5 にはPSDesiredStateConfiguration 1.1 しかないにも関わらず、Import-DscResource -ModuleName PSDesiredStateConfiguration をしただけ/あるいは明示しなかった場合、mof document は ModuleVersion 1.0 となる ことにあります。

順に見てみましょう。まず、Get-DscResource をすると、PSDesiredStateConfiguration1.1 とわかります。

gist.github.com

次に、PSDesiredStateConfiguration の WindowsFeature リソースを使った簡単な Configuration を書き、mof にコンパイルすると次の結果が得られます。

gist.github.com

コンパイルされた mof の PSDesiredStateConfiguration の下にある、ModuleVersion を見てください。1.0 になっていますね。

通常のリソースでは、mofで利用したリソースのバージョンが出力されます。たとえば、一覧にあった GraniResource は 3.7.8.1 なので Import-DscResource -ModuleName GraniResourceとすると ModuleVersion は 3.7.8.1 がmof にも出ます。当然です。ここを見てPULLノードはサーバーに取得依頼するモジュールバージョンを決定しているので、mof の ModuleVersion は、Configuration から mof をコンパイル時に利用したバージョンに厳密であるべきなのです

WMF 5 で追加された Configuration のプロパティ、例えば PsDscRunAsCredential を使う

先ほどの ModuleVersionの明示がなくても、MinimumCompatibleVersion が 1.0 のうちは問題ありません。1.0 はつまり WMF4 と同様の機能しか使っていないことを示します。この場合、「PSDesiredStateConfiguration に限ってModuleVersionが1.0 と PULLクライアントに存在しなくても PULLServerに問い合わせに行かずエラーが発生しない」という挙動をします。(なんだこれ

しかし、MinimumCompatibleVersion が 2.0 になると話は変わります。「PSDesiredStateConfiguration も、他のリソース同様に PULL クライアントに無い ModuleVersion 1.0 が提示されたため、PULLServerに問い合わせに行きますが、PULL Server も WMF5 なので 1.1 のモジュールしかなくエラーで終わる」という挙動に変わり、PULLクライアントが永遠にPULLを開始できない状況になります。

さて、MinimumCompatibleVersion が 2.0 になる条件が、WMF5 で追加された Configuration の追加プロパティ、「PsDscRunAsCredentialを使う」などといったことです。通常 DSC の実行は SYSTEM アカウントですが、PsDscRunAsCredential を使うと指定したユーザープロファイルで動作します。素晴らしいプロパティです。使いどころによっては、これまでのプロファイルを横断する方法をとらなくて済むので最高ですが、まさかこんな落とし穴が。

gist.github.com

なお、PSDesiredStateConfiguration リソースで PsDscRunAsCredential を使うと、ModuleVersion が 0.0 になり問題は回避できます。ただし、無駄に指定が必要ですが。

PSDesiredStateConfiguration のいずれかのリソースを利用している

問題は、PSDesiredStateConfiguration と、そのほかのカスタムリソースの併用で起こります。さて、先日の記事でも書きましたが、xPSDesiredStateConfiguration モジュールは、ビルトインの PSDesiredStateConfiguration モジュールの機能も移植して機能改善を高品質、高速に回そうとしています。

tech.guitarrapc.com

github.com

さて、今回の問題は PSDesiredStateConfiguraion のリソースを使うと発生します。この中で Fileリソース を除いて*3 xPSDesiredStateConfiguration モジュールの xリソースに置き換えが可能です。

逆にいうと、Fileリソースを利用している場合は、遭遇してしまう可能性があるということです。

Configurationをネストしている

これまでの条件をすべて満たす Configuration のネストで発生します。具体的には次のようなものです。

gist.github.com

ここまでの事例のいずれかのみを満たすだけなら問題は発生しません。

単純な Configuration

gist.github.com

PsDscRunAsCredentialの利用

gist.github.com

同一 Configuration での指定

gist.github.com

PSDesiredStateConfiguration のみの ネスト

gist.github.com

PULL モードを利用した PULL クライアントを利用している

本件は、PUSH では発生しません。PULL でのみ発生します。

一時対応 (Workaround)

問題が明確なので、一時対応も取れます。

PSDesiredStateConfiguraion を xPSDesiredStateConfiguraion で置き換える。

今後は、xPSDesiredStateConfiguraionを 使うことを検討してください。WMF5 なら、Import-Module xPSDesiredStateConfiguration で最新版を取得可能なのでいい感じででしょう。

唯一 File リソースを使っている場合のみ次の方法をとって回避してください。

方法1. Import-DSCResource -ModuleName PSDesiredStateConfiguraion -ModuleVersion 1.1 を明示する

1つの方法は、先述したバージョンの明示です。これで mof のPSDesiredStateConfiguration バージョンが PULL クライアント (WMF5) の 1.1 と合致するので問題が回避できます。

ただし、WMF4 と WMF5 が混在する環境では、WMF4 用の Configuration と WMF5 用のConfiguration で分ける必要があるため、後方互換性が失われます。

方法2. mof document の ModuleVersion を 0.0 に書き換える

先ほど、mofに書かれた PSDesiredStateConfiguraion の ModuleVersion が 1.1と言いました。実は、この ModuleVersion を 0.0 にすると、PULLクライアントは自分が持っているリソースの最新バージョンを使おうとします。

これでいい場合は、mof 生成後にパースかけて置き換えればいいでしょう。

しかし、本来mof は人が読む、操作することを前提にしていません。何しろ mof 生成のための糖衣構文こそが PowerShell DSCですから。本質ではないのです。

方法3. PSDesiredStateConfiguration の利用では必ず PsDscRunAsCredential を利用する

PsDscRunAsCredential を使うと、PSDesiredStateConfiguration の ModuleVersion が 0.0 になるのでこれでも回避できます。

gist.github.com

方法4. ネストを避ける

なるほどありえない。

根本解決の手段

mof を触るのが一番楽です。後方互換性もあります。しかし現状のImport-DscResource は mof で ModuleVersion 0.0 を生成する方法を持っていません。そこで、根本解決として、Import-DscResource[bool]AllowUseLatest のパラメータ追加を提示しています。

これによって、Configuration から mof で利用するバージョン を 0.0 にする手段を得ることができ、柔軟性が高まる上、副作用もないはずです。

まとめ

ここまで読まれた方はまずいらっしゃらないでしょうが、これが問題の概要です。今後の Configuration の書き心地、リソースの更新の負荷に大きくかかわるため、ぜひVoteをしていただけると嬉しいです。

https://windowsserver.uservoice.com/forums/301869-powershell/suggestions/15496698-minimumcompatibleversion-cause-cannot-find-modulewindowsserver.uservoice.com

Vote が 100件行けば即対応入る可能性が高いので、ぜひ Vote していただけると嬉しいです。意見などは UserVoice に書いていただけると PowerShell Team に直で伝わります。

よろしくお願いします。

ちなみに私は、-ModuleVersion を明示しました。今後のWMF更新が鬱ですが仕方ない...。

*1:PSDesiredStateConfiguration に含まれる標準リソースを除く

*2:Import-DscResource -ModuleName GraniResource -ModuleVersion 3.7.8.1などと、バージョンを明示するするのは手間すぎてまずいない

*3:Group もちょっとバグや挙動が変わるので今月のリリース待ち

PowerShell DSC の xTimeZone リソースにPR がマージされたお話し

過去にもいくつかの PowerShell DSC リポジトリでやりとりをやっているのですが、先日 xTimeZone にあった結構困ったバグ修正のPRをおくったところマージされました。

日本語はもろに影響を受けるので良かったよかった、とともに軽くメモに残しておきます。

あと、最近になって Powershellチームによる Desired State Configuration の開発に変化が出てきているのでその辺も。

目次

PowerShell Desired State Configuration の開発状況

ビルトインリソースと言われるのが、Windows 8.1/10/2012 R2/2016 に同梱の標準リソースです。

それに対して、x(エクスペリメンタル Experimental) なリソースが、Github 上で開発されています。

github.com

バージョニングの改善

この x 付というやり方は、冷静に考えても当時から現在まで多くの問題を起こしています。バージョニングを持たない当時、「互換性を維持するためにモジュール名を変える」ことを推奨することで無理やり実現しようとしたのです。日頃の感覚では、バージョニングを用いて利用者に負担を少なく行うのが一般的な開発シーンなので、わざわざ名前の変更をするガイドラインは違和感にあふれます。*1

現在は、当時と異なっています。多くの面で、バージョン管理が重要になってきました。

  • Github 上でのオープンな開発
  • PowerShell でバージョンのサイドローディングが可能に
  • PowerShellGet でのモジュールの公開、展開

これらを受けて、PowerShell Module や xなりソースのバージョンを良くしようとしています。Semantic Versioning もその具体例の1つです。xがどうなるかはまだ不透明です。

https://github.com/PowerShell/PowerShell-RFC/blob/master/1-Draft/RFC0004-PowerShell-Module-Versioning.mdgithub.com

フィードバックをどしどし受け付けているので、ぜひぜひご意見してください。

github.com

xPSDesiredStateConfiguration を High Quality Resource Moduleへ

High Quality Resource Module (HQRM) の意味を先に。Issue において、次のように明言されています。

willing to use this module in production.

github.com

そのための要素として、次を挙げています。

  1. Fix PSSA issues per the DSC Resource Kit PSSA Rule Severity List (not yet published publicly, sorry)
  2. Ensure unit tests are present for each resource with more than 70% code coverage
  3. Ensure examples run correctly, work as expected, and are documented clearly
  4. Ensure clear documentation is provided
  5. Ensure the PSDesiredStateConfiguration module follows the standard DSC Resource Kit module format
  6. Fix code styling to match the DSC Resource Kit Style Guidelines

この内容は、Nano Server と Full Server で利用できるコンポーネントの違いにも起因しており、かなり注目です。

https://github.com/PowerShell/xPSDesiredStateConfiguration/blob/dev/HighQualityResourceModulePlan.mdgithub.com

このような動きもありつつ、さて本題に行きましょう。

xTimeZone リソースのバグ

Issue に概要、原因、解決方法をまとめています。

github.com

原因

シンプルで、TimeZone の適用(SET) 時には TimeZoneInfoのId を用いているのに、TEST/GET では TimeZoneInfoの StandardName を用いていることです。StandardName は.NETの外でローカライズされてしまうので、英語以外の環境では TESTが必ず FAILED になってしまいます。マサカの英語OS以外全滅!*2

TimeZoneInfo クラス (System) | Microsoft Learn

対策

Id でのマッチングが一番簡単なのです。

stackoverflow.com

ただ、どうやら PowerShell Team はCIM Method を使って現在の TimeZone StandardName を取得したがっていたので、StandardName と Id の変換を行うようにしました。

gist.github.com

あとは判定して終わりです。

PRからマージの流れ

PowerShell チームのコントリビューションガイドに則ります。

Code of Conduct | Microsoft Open Source

github.com

おおよそ次の流れです。

  1. Issue で報告
  2. 対象のリポジトリをFork
  3. ブランチを切る
  4. コードを修正
  5. 関数追加などの場合、Pester のテストを追加
  6. Upstream に PR を投げる
  7. この時点で AppVeyor テストが走るのでテスト結果確認
  8. コードレビュー対応
  9. 問題なければ マージ

以前、xNetworking でもやったのですが、直したいのが一行に対してPester テストの追加が 1000行を超える修正になるなど、個人的にはPesterごにょごにょがイヤポヨです。*3

今回は、以前からやり取りある @PlagueHO が反応したので、甘えてPester だけお願いしちゃいました。

github.com

レビューは、PR 上や Reviewable で行われます。*4

問題なくマージされているので、次回のリリースで含まれます。

まとめ

ということで、現在最新の xTimeZone 1.4.0.0 は、英語以外では致命的なバグがあります。1.3.0.0 を使って、1.5.0.0 を待ってください。

最近 C# ばかりで、DSC Resourceあまり書いていませんが、PowerShell の開発も普通のPRのやり取りで楽なものです。

これからもDSC は良くなっていくので、ぜひぜひ使っていってほしいですね。いい加減入門とかはやめて、使って下しあ。

*1:現在ほとんどの利用者は守っていないようです。これは Github や Gist 上で検索したり、各種ブログを見れば容易にわかります。まぁ、守る必要、ないと思います。

*2:ざるというより、tzutil.exe を使っていたところに頑張って .NET化したけどこの時点では知らなかったのかな xTimeZone module should use .NET APIs instead of tzutil.exe · Issue #5 · dsccommunity/xTimeZone · GitHub ほんと、この提案者は、そこかしこで発言するはいいけど色々ぐんにょり

*3:それだけ影響のある変更だったのですが流石にあの時は....

*4:なぜか Reviewable好きなんですよね、彼ら

PowerShell の Set-ExecutionPolicy 設定時のスコープ対処

PowerShell をシェルとして利用するときに誰もが一度はひっかかるのが ExecutionPolicy です。

今回は、Set-ExecutionPolicy RemoteSignedをしようとしたら、以下の警告が出た場合の対処です。

ExecutionPolicyOverride,Microsoft.PowerShell.Commands.SetExecutionPolicyCommand : セキュリティ エラーです。

目次

ExecutionPolicy の目的

敢えて長い間ふれてきませんでしたが、そもそもに触れましょう。

スクリプトは信頼できることのセーフガード、VBScript など過去の経験、そして JEA に見られるような必要な権限を必要な時にという考え、あるいは 「あ、間違えて実行しちゃった!」を防ぐための機能として ExecutionPolicy があると説明されています。*1

大事なのはこの辺です。

  • What you are trying to protect. In PowerShell’s case, this is almost entirely "code execution.”
  • Sources of data, and how that data flows. In PowerShell’s case, these are scripts sent to you through email, scripts downloaded from the internet, your profile, user input, and other similar sources. From there, this data flows through many PowerShell features – the parser, cmdlet invocation, formatting and output, etc

  • Boundaries between untrusted data and trusted data

    • PowerShell doesn’t trust scripts that you download from the internet
    • PowerShell doesn’t trust a random script or executable lying in the current location of your hard drive
    • PowerShell does trust user input
    • PowerShell does trust the administrator of the machine
  • PowerShell does trust a running script

blogs.msdn.microsoft.com

blogs.msdn.microsoft.com

blogs.msdn.microsoft.com

コンセプト自体は理解できるのですが、ちょっと心が折れそうになることが多いのではないでしょうか。

中にはデフォルトから変更しないことをすすめる記事もあります。これはこれで有益です。特に PowerShell.exe -ExeuctionPolicy RemoteSigned -Command "なにか処理やps1"は 私自身よく利用しています。

qiita.com

他にもバイパスする手段は数多くあります。

blog.netspi.com

「バイパスされるようなものなんて」といいたくなる気持ちが湧いた方もいらっしゃるでしょうが、Windows Server 2012R2 ではデフォルトが RemoteSigned なあたりに考えの変化も見て取れます。*2

Office365 や Exchange Online などは、相変わらずのようですがMicrosoft 社内でも見解はそれぞれなのでしょう。

https://powershell.office.com/scenarios/setting-execution-policies-on-windows-powershell

どうするといいのか

ExecutionPolicy とは、というのは結局のところ利用シーン次第です。

  • グラニでは、RemoteSigned に変更してしまいます。それは背景に、Disposable な環境で一度きりの環境構築、以降は構成変更があれば捨てて新しく立てる。 という仕組み、コンセプトが根付いているからです。このケースにおいて、Restricted + -ExecutionPolicy などでやる意味は乏しいのはご理解いただけるでしょう

  • PowerShell スクリプト開発をされる方にとっては、当然のように RemoteSigned にしたくなるでしょう。*3

  • しかし、スクリプトも実行しないようなPCまで強制する必要はありません。Restricted でいいでしょう

多くの方にとって、それぞれの利用シーンがあります。ExecutionPolicy が面倒なのは事実ですが、背景はまず共有しておきたいと思います。

Set-ExecutionPolicy

さて、今回の記事は、Set-ExecutionPolicy を実行すると、ExecutionPolicyOverride,Microsoft.PowerShell.Commands.SetExecutionPolicyCommand と出た場合の対処です。

Set-ExecutionPolicy : Windows PowerShell updated your execution policy successfully, but the setting is overridden by
a policy defined at a more specific scope.  Due to the override, your shell will retain its current effective
execution policy of RemoteSigned. Type "Get-ExecutionPolicy -List" to view your execution policy settings. For more
information please see "Get-Help Set-ExecutionPolicy".
At line:1 char:1
+ Set-ExecutionPolicy Unrestricted
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : PermissionDenied: (:) [Set-ExecutionPolicy], SecurityException
    + FullyQualifiedErrorId : ExecutionPolicyOverride,Microsoft.PowerShell.Commands.SetExecutionPolicyCommand
Set-ExecutionPolicy : Windows PowerShell により実行ポリシーは正常に更新されましたが、設定は範囲がより明確に定義されたポ
リシーで上書きされました。この上書きにより、シェルで現在有効な実行ポリシー Restricted が保持されます。実行ポリシーの設
定を表示するには、「Get-ExecutionPolicy -List」と入力してください。詳細については、"Get-Help Set-ExecutionPolicy" を参
照してください。
発生場所 行:1 文字:1
+ Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : PermissionDenied: (:) [Set-ExecutionPolicy], SecurityException
    + FullyQualifiedErrorId : ExecutionPolicyOverride,Microsoft.PowerShell.Commands.SetExecutionPolicyCommand

結論からいうと、このエラーが出る場合は設定しようとしたスコープよりも下のスコープで制限している ことが原因です。

見てみましょう。

PowerShell の実行権限スコープ

Get-ExecutionPolicy -List をすることで一覧がみれます。MachinePolicy と UserPolicy は、GPOに関連するので触りません。

この場合、既に LocalMachine で RemoteSigned になっています。

権限スコープは、Process < CurrentUser < LocalMachineの順に広くなります。

権限スコープ 範囲 設定箇所
Process 現在のPowerShell セッションのみ。別のPowerShell セッションには影響を与えません。 環境変数 PSExecutionPolicyPreference
CurrentUser 現在のユーザーのPowerShell実行のみ。別のユーザーには影響を与えません レジストリ HEKY_CURRENT_USER\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell
LocalMachine マシン全体のスコープ。どのユーザーで実行しても、同一の権限になる。 レジストリ HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell

例えば、PowerShell.exe -ExecutionPolicy RemoteSigned などは、Process スコープの変更です。

ここまで理解できれば話は簡単です。

狭いスコープから変更していく

エラーが示しているのは、変更しようとしたスコープよりも低いスコープで制限されていることを意味します。

たとえば、CurrentUser スコープがRestricted にします。

この状態で、LocalMachine スコープを RemoteSigned にしようとするとエラーがでます。

対処は簡単です。CurrentUser を先に RemoteSigned などにしてから、LocalMachine を変えましょう。

Process スコープが Restricted になっていた場合でも、同様に Process スコープを RemoteSigned などにすれば回避できます。

怒られませんね。

まとめ

Disposable なら ExecutionPolicy も気にせず済むので、オススメです。

*1:Twitter上での Jeffery Snover 御大の発言は時期によって意味が変わるので切り取りません

*2:Windows 10 のようなクライアントOSではRestricted なのもそれなりに理解はできます

*3:このブログはそういった方を対象にしているのも自明です