tech.guitarrapc.cóm

Technical updates

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:このブログはそういった方を対象にしているのも自明です

PowerShellGet の PSGallery が消えた場合の対処

PowerShellGet のデフォルトのPSRepository は、PowerShell Gallery です。

PowerShell Gallery | Home

デフォルトなので、何もせずとも設定されているのですが、過去に2回設定が消えたことがあります。原因がいまいちつかめていないので、復旧方法のメモだけ。

目次

PowerShell Gallery

通常は、Get-PSRepository を実行すると、PSGallery が表示されます。

結果、Find-ModuleInstall-Module といった PSGallery を使ったモジュールの検索、インストールが可能になります。

対処方法

しかし、状況によって、Get-PSRepository をしても PSGallery がないことがあります。

その場合、以下を PowerShell で実行しましょう。

gist.github.com

実行後に、PSGallery が復活することを確認します。

Find-Module も使えますね。

まとめ

単純な設定なのですが、面倒なものです。

OneGet や PowerShellGet の更新は、結構ほそぼそしているのですが、開発は続いています。今後がどうなることか、というところですが、明らかにサーバー展開時のモジュールやパッケージの導入が楽になります。

ぜひ WMF5 や PowerShellGetを試して貰えるといいですね。

PowerShell でディスクの初期化からフォーマットを行う

Windows のディスクを管理したいとき、古の時代から diskpart コマンドがあります。

DiskPart Command-Line Options | Microsoft Learn

www.atmarkit.co.jp

しかし現在これらのdiskpart 操作をを使うことはありません。PowerShell でより高度、安全に操作できるからです。

こういったディスクの初期化処理は、AWS や Azure においてディスク追加するときにも必要になることがあります。

そこで今回はその例を少し見てみましょう。

目次

GUI のディスク管理

Windows 10 を例にします。 Win +x > k によって ディスクの管理 を起動できます。

例えば以下のディスク構成があります。

この中のディスク3 は、現在は プライマリパーティション1つの 単一パーティション構成です。こういった場合は GUI でボリュームの削除を行って綺麗に構成可能です。

しかし、以前OSをインストールしていたディスクの場合は GUI で行えることに制限があります。*1

ディスクの管理では、OSインストール時に自動構成された回復パーティションを削除できません。

そこで古代においては、diskpart を使って操作を行うことがありました。

今回は、OSが入ったりした ディスク3 を「完全に消去 > 単一パーティション > NTFSフォーマット」まで構成してみましょう。

diskpart

コマンドプロンプトから diskpart を入力すると、管理者へのUAC昇格と共に diskpart の対話モードが開始します。

まずは list diskでディスク一覧を呼び出して対象を把握します。

次に操作対象ディスクを select disk 3 で選択します。

操作内容として、回復パーティションも含めて綺麗にするので、clean コマンドでディスクを消去します。

という感じです。

PowerShell で初期化する

PowerShell には、Disk や Partition、Volume 操作用の Cmdlet があります。これらをパイプラインで繋げれば、初期化からフォーマットまでワンライナーで綺麗に完了します。*2

gist.github.com

どうでしょうか?先のディスク3の初期化も一発です。かつ、ディスクの製品名から指定もできたり、BootDisk を除外してまとめて行ったりも可能です。

操作も Cmdlet が明確に意図を示しています。

Cmdlet やっていること
Get-Disk ディスク一覧の取得
where FriendlyName -Match ST3500 ST3500という製品名に該当するディスクにフィルタ
Clear-Disk -RemoveData -RemoveOEM -PassThru ディスクを消去
Initialize-Disk -PartitionStyle MBR -PassThru ディスクを MBRで初期化
New-Partition -UseMaximumSize -AssignDriveLetter 新規パーティションをディスク全体を単一として、ドライブレターを自動付与で作成
Format-Volume -FileSystem NTFS -Force 作成したボリュームを NTFS でフォーマット

簡単ですね。

まとめ

diskpart しかできないという操作はそうそうありません。回復コンソールなど、極めて限定的なシーンでの利用となるでしょう。

こういったWindowsシステム系の操作は PowerShell だと本当に楽に書けるのでぜひ試してみるといいと思います。

*1:OSインストール時やパーティション操作ソフトを除きます

*2:PowerShellを管理者権限で起動する必要があります

Azure Functions - C# で Windowsにインストールされている.NET Framework のレジストリバージョンを適切に返してみよう

現在、5/26 -27 で、ニューヨークにて Serverless Conf が行われています。コンテンツホルダーが信じられないぐらい豪華、かつホットな人ばかりなので、動向に注目です。

https://serverlessconf.io/serverlessconf.io

さて、Azure Functions の PMも参加しているように、Azure Functions はServerless という意味で着々とよくなってきています。*1

例えば、Github などとの Continuous Integration。

tech.guitarrapc.com

以前は、新しく push した結果が反映するときに、コンパイルが走ってもコンソールに結果出てこないため、実はコンパイルエラーでした!ということもありました。これが現在は、デプロイした後に Functionのコンソール を開くと、コンパイル結果が表示されるようになっています。とても良いです。CI を組むと、コンソールが Read only になるのはいいのですが、コンパイルも走ってくれなくて困った状況からついに開放です。

また、別の Function に移動して、戻ってきても前回の実行ログが保持されるようにもなっています。

https://github.com/projectkudu/AzureFunctionsPortal/issues/377github.com

1つバグが発生していて、Clear を押してもログが復活してしまいますが、すでにデプロイ待ちのステータスです。

https://github.com/projectkudu/AzureFunctionsPortal/issues/384github.com

こういう更新が、毎日あります。結構面白いので Issue などを眺めているとどう改善していっているかの参考になって個人的に楽しみにしています。

github.com

さて、今回の記事は、.NET Framework のバージョンを確認したいけど、レジストリではわかりにくいケースがあるのでそれを判別するAPIを AzureFunctions に作成してみるというものです。

目次

.NET Framework のバージョン確認

MSDN に記載があります。

To get an accurate list of the .NET Framework versions installed on a computer, you can view the registry or query the registry in code: インストールされている .NET Framework バージョンを確認する - .NET Framework | Microsoft Learn

レジストリです。悪くはないのですが面倒です。

ようはこの関係を取れればいいわけです。

合わせてサンプルも紹介されています。

gist.github.com

ちなみに、このH_KEY_LOCALMACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\ は以下の構成になっています。

{
  "Hive": "LocalMachine",
  "View": "Registry64",
  "SubKeyPath": "SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full\\",
  "IsRegistryKeyExists": true,
  "SubKeyNames": [
    "1033",
    "1041"
  ],
  "EntryNames": [
    "CBS",
    "Install",
    "InstallPath",
    "Release",
    "Servicing",
    "TargetVersion",
    "Version"
  ]
}

リファレンスでは Release の値を取得しておりint が返ってきています。

しかし、実は、Version を取れば 4.6.01038 といったほしいバージョンが取れたりします。なので、int での変換が面倒な場合は、H_KEY_LOCALMACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\FullRelease エントリの値を見るのが早いでしょう。おわり。

gist.github.com

AzureFunctions でレジストリの intからバージョンを返す

というのはさみしいので(?)、Release で取得した int を AzureFunctions に投げて、バージョン情報を返すようにします。面倒なので Dictionary で定義を書いていますが、いろいろなサービスから参照することを思うと、手動でいったんok、直すのは AzureFunctions だけというのもいいかと思います。*2

ということで、Function は次の通りです。

gist.github.com

あとはデプロイして、結果を試すと {"Version":"4.6.1"} が返ってきました。

まとめ

今回のサンプルも Github にあげておいたので、よろしければどうぞ。

github.com

あまり自前でこのような変換を作るのは好きではないのですが、この程度なら悪くないでしょう。いろいろな設定や変換を AzureFunctions に閉じ込めるのも手段としてはいいと思って採用してたりします。このような External Configuration store Pattern は、スケーラビリティに大きく寄与するので、採用できる箇所で採用すると嬉しいことが多いですね。*3

*1:成熟には程遠いですが!そこがいいということで

*2:どうしても面倒なら、適当にスクレイピングしましょう。

*3:ただしリトライ処理は大事です