tech.guitarrapc.cóm

Technical updates

2018年目標

オフラインで抱負を毎年やっているのですが、こういうのは外に書いておくことに意味があって自分で後日読み返せるので書いておきます。

個人のプライベートにおける目標です。

目次

テーマ

挑戦。

知らない、なんとなく後回しにしていたことを回さない。今年とこの先を見て優先度をつけて選択する。という普通のことを続ける。

CSharp

.NETCore を前提に。Full .NET からなるべく離れる。

コンテナ動作を前提に。ランタイムをどこまで気にせずに実行できるようにできるかが今後のカギ。アプリケーション実行までの到達フローとスピンアップが大きな課題かな。

Unity は、2017.3 以降の動向が結構気になる。,NETCore どうなるのかもあるけど、いくつか使ってない機能があるので手元に取り込んでおきたい。ライブラリちょっと作ってるの出す。

言語機能は引き続き積極的に使っていくが、動向は注意。

言語勉強会は NETCore ベースで参加したいが、IL や CPP 化も少し興味ある。

Swift

課題のView は、Storyboard への View分離、Routerでのナビゲーションをきっちり組めるように。Autolayout 自体は苦ではないので、あとは数感。

裏は随時変化が必要なので、Rx必要なら引き続き RxSwift 。他ライブラリは、言語、Xcode標準で苦しいかどうかで考える。

基本的にサーバーレスにどこまでできるのか、サーバーサイドをどう組み合わせてパイプライン化できるのか。

アプリと並行して、いくつか非同期系処理はライブラリとして作っておきたい。

Serverside Swift は様子見。

えり好みなしで時間を作って頻度高めに参加したい。

Firebase + FireStore + CloudFunctions

クライアントの要。サーバーサイドをイベント、API を HttptriggerでできないならGAE前提で。ここできついならちょっと設計まずいと思うようにするほうがよさげ。*1

TypeScript でのデータフロー処理自体は慣れてきたが、いまいちもうちょっといいやり方がありそうなのと、エコシステムにキャッチアップできてないのでもう少し見る機会は増やす。

勉強会も機会見て参加したいが、優先度はまだ低い。

PowerShell

体系的にまとめる。早々にまとめる方がメリット高そうなので、個人の時間で一定の時間を毎日割く。昨年後半から挑戦していて、なんとか時間の工面は立つめどできたので、今年は個人の最優先でやり切る。

勉強会はその後に開催しておきたい。参加でもいいけど、いずれにしてもやっておきたい。

他言語

昨年から引き続き、Kotlin の優先度は上がってる。

もう少し低いレイヤというかネイティブに C/C++との絡みと増えているので、いい加減C++ か Rust は手を付ける説あるが不透明。ちょっと考えるが、優先度を高める可能性ある。

コンテナ

k8s がいいけど微妙に悩ましいので、GAE ぐらいの緩さがいい感じ。今年はここが主戦場の1つ。サーバーレス効かないものってまだ多くて、そのあたりはまぁさくっと封じ込めつつパイプライン組んでしまう。

動画配信

去年末にすこし触っていたが想像以上に楽しい。でも1sec未満を目指すとめちゃめちゃハードル高い。そもそもミドルウェア自作となると、かなり苦しい。

面白いのでこのあたりはアンテナはるが、趣味なので優先度は低め。

AWS / GCP / Azure

流れを作るのが AWS なのはもうしばらく続きそう。その意味で更に使っていくが、IoT は完全に蚊帳の外にしてるがたぶん手を付ける余裕はひねり出せない予感。AI も今年も手を付けるのが苦しそう。

個人開発のGCP比重は極めて高く。DNS 含めて移してもいいぐらいには完全に移行しても良い。

Azure は、Azurefunctions と AppService の動向が少し悩ましいのでアンテナ感度は高めに。AzureAD も停滞を感じるので、そろそろ次も選択肢に。

スマートホーム

去年から使ってる Google Home が Google Apps 対応したらちょっと使い方を真剣に考える。それまでは今のゆるふわでok。

ツール

個人 Google Apps もあるので Googleに集約しているが、このままでいい気がする。

MS 系ツールは、Outlook がわずかに使っていたがこれもなくしてもいい感じ。というかなくす。メイン製品で使うのはVisual Studio ぐらいかな?

Adobe も 今の利用なら Sketch + inVision で十分。

Slack は維持でよさげ。

esa はkibela のログインがGoogle 連携なさげで微妙だけど個人利用ならこれでいいかも。5人まで無料は大きい。ちょっと悩ましいけど考える。

Wunderlist が終わるとなった場合の代替見つけられてなくてつらい。Todo はあれは違う..。

アウトプット

ブログメインで。Qiita からは離れる気がする。dev.to はありだけど。

ブログも、はてなブログの https化と画像の目途次第で変更するかも。

*1:低レイテンシや常時接続だと話は別

2017年を振り返って

どうやら2年に一度振り返り記事を書いているようです。

前回は 2015年でした。

tech.guitarrapc.com

例年も挑戦だらけですが、今年は踏み込みにくかったことへも挑戦の連続だったのでメモします。

目次

総合

2015年の終わりにこう書きました。

個人的には、もう少しインフラやそれ以外に Unity を取り入れたりしたいですね。C# に完全に技術基盤が移行しているので、PowerShell というより、C# を中心とした動きが強くなると思います。HoloLens に胸を躍らせる日々です。

2017年は、これが完全にシフトしてUnity やクライアントにシフトしていました。2016-17 にかけてはC# を中心としています。HoloLens もさんざん触っていました。アウトプット忘れてた。ここにメイン言語としては Swift / TypeScript が入ってきました。家も実はずいぶん前から MBP だったり。

引き続きやることは多岐にわたっていて、さらに頑張らないとなのですが、どう楽しむかも方も幅が広がったので引き続き楽しく過ごします。

プログラミング

2016年から続く流れとして、2017年も Unity + C# での開発が大きなウェイトを占めました。後半にSwift と TypeScript がメインの武器に入ってきたのは本当に良かったです。2018年は、C# / Swift / TypeScript / ShellScript / PowerShell あたりがよく使いそうと思いつつ。

ひたすらコード書いていた気もしますが、チームビルディング、マネージネントにも時間を割きました。まだまだ課題は多いですが、プロジェクトを楽しく完遂させることを主眼にどうやるといいか、毎日模索しています。

CSharp

お仕事として、黒騎士と白の魔王がリリースできたのは良かったです。

メインの武器としてこれまでもサーバーサイド、サーバーレス、ライブラリをよくやっていたのですが、クライアントも武器として働くようになりました。Unity でアプリを複数作りきったこともそうですが、サーバーサイド、サーバーレス、クライアントのいずれもをクライアントに比重を置きつつやっていました。表にだしたもので、Project Sonata黒騎士VRミュージアム を作ってました。黒騎士VRミュージアムは、Google VR SDK と Unity 5.6 の組み合わせですが、はじめから最後まで作りきったので、黒騎士のファンボックス的にも楽しんでもらえると嬉しいもので。Unity での開発基盤からチューニングまでやっていて、今までよりもパフォーマンスに気を配りつつUIや言語対応フローまで網羅していったのですが、考える事がまだまだ多くUnity 開発まだまだ楽にすることが多くていい感じですね。やることがなくなったと思ったらそこで進化がオワリなので、継続して向上していくのは重要だし、マインドとして失わないようにしたいです。

AzureFunctions や Lambda に代表される C# でのサーバーレス処理ですが、AzureFunctions が特に複雑度を増していてそろそろ苦しいです。仕方ないのですが。Lambda も何年もやっているとそろそろ API Gateway のめんどくささがあります。イベントベースとはいえ、このあたりの手間のかかりようから、AzureFunctions への完全シフトがなされました。Lambda いいんですが、やっぱり苦しい。後述する Cloudfunctions が颯爽と登場して、私の中では CloudFunctions 激熱です。

Unity といえば、Shader もごりごり触っていました。C# Script からの操作はあめぇ、と思いつつパフォーマンス気にしないでいい場面では便利なわけで。この辺りは、Shader まだまだ勉強足りないためつい逃げてしまって、ぐぬぬです。スクリプト処理よりも、ビジュアルへの影響が大きいものへのアクションは強化しないと苦しいです。ライティングはさすがに怖さはなくなってなれてますが、ビジュアル関連はどうも苦手ですね。物理演算の数学も使うものをしっかり学びなおさないと武器にできてない感じがあるので、課題です。画像のフォーマット、サイズも当然入ります。コンシューマの知識って大事。

サウンド周りも手を付けました。主にCRI ですが、なるほどこうやって制御するといい。というのを参考に一から組んでみましたが、うまく制御できてよかったです。自作のサウンドエンジンは別ですが、サウンドミドルウェアはものによって思想が違い、それがC# スクリプトのAPIにもよく現れていて、なるほどムズカシイ感あります。サウンドデザイナー、サウンドエンジニアと呼ばれる方々に学びっぱなしで、尊敬の念が深まるばかりです。サウンド、波、便利だけど習熟までのハードルは高い。それがVR や AR だとさらにやばい。サウンドヤバイ。

積極的な相談とそこから確実に学んでいくことは大事です。学べることは私にとって何よりも幸せと感じます。

.NET Core は可及的速やかにスタンダードになってほしいし、するべくと考えています。次の記事で少しそのあたりに踏み込みたいです。ビルドも、MSBuild きっついので、Gradleのようなスクリプトベースでの制御がほしいです。正直ちょっとやばい。

Swift

今年からの武器になりました。ネイティブをずっとやろうと思ってもにょもにょ触ってはうーんという感じでしたが、Swift4 できっちりやろうと思って始めました。Unity でもネイティブになる瞬間にしり込みする悪い傾向があったのも理由です。*1

Swift どんなのを何をしているかは別機会にまとめるとして、感触としてはやっていて楽しい/よく考え抜かれていて素晴らしい言語と感じます。どんな言語もそうですが、1つの言語をやると構文的な違いはあっても、考え方の根幹を学んで沿えば言語スタック、フレームワークの意図しているであろう良さを享受できると信じています。*2

Swift に関しては、幸いにしてコミュニティの熱気、先達の素晴らしい情報の共有があるので、これまでやってきた言語の中でも飛びぬけて入りやすい。楽しいを感じるまでのスパンが短いです。Xcode や Storyboard も使い方も知らないからはじまります。できないのではなく知らないという前提で、調べて学んでいくスタイルを徹底するように気を付けていますが、幸いにして多くを楽しく学ぶことができています。このまま学ぶ姿勢を絶やさず、今年は学んだことを記事にアウトプットしていきたいです。

主に@_monoさんから学んでいます。そろそろ Wishlist をみようとおもいます。Swift ですが、コミュニティ活動が国内海外を問わずすごいです。それが一番楽しい言語界隈の1つと感じるのかと思っています。TOKTYO - try! Swift が 2018年3月1日1 からありますが、早々に予約しています。心から楽しみです。

www.tryswift.co

なお、おうちでは Swiftを毎日書いていたり。

TypeScript

Swift + Firebase が私のなかで鉄板のクライアント構成です。クライアントアプリ作るときにサーバーサイドを考えるなら、まず Firebase でできるか考えるのが多くのシーンでは適切だと思っています。

さて、これまでもサーバーレスの処理にNode.js をたびたび触っていましたが、Firebase となると Google Cloud Platform なので CloudFunctions です。言語対応の薄さから、あんまりと思っていましたが、Swift を機会にさくさく TypeScriptに入ってほとんど詰まらずに楽しめています。あれ? と思うぐらいに、楽しく楽です。型のおかげな感じもありますが、VSCode + TypeScript が相当書きやすく macOS 上で普段書いています。デプロイも楽ですし。

2018年も引き続きメインに据えてやっていくので、これもアウトプットできるといいですね。

Python

今年はあまり触る機会を作れませんでしたが、ちょろちょろデータ処理をするときに使っていました。Rの代わりじゃないですが、Jupyter Notebookつくときに触っています。言語自体は好みですが、書く機会の少なさが習熟の遅さにつながっている気がします。

しかし、Google Cloud 触っていると Python 2.7 なのが本当に苦しいのでいい加減 3.x にしたい..。

PowerShell

発信を少なくしていましたが、他の言語もガッツリ腰を入れて触っていることもあり基礎から考えてみることにする機会になりました。APIデザインやコーディングスタイルはその1つで、共有意識として何かしらの形でチームとしてもっておきたいものです。で、どうだったかというと、なるほど結構あるようでない。Verb-Noun のような、ずっと言われていることはともかく、このデザインはどういう意図なのかの、背骨となる考えが不透明な感じがします。その場の対応が続いたのかな? という印象が拭えないのは、(PowerShell を使っていて慣れたころに出会う) 直感に反する動きからも、ふむと思います。

このあたりは、伝え方というか、体系としてまとめることが必要と感じており何かしら結果をだしたいと思うので、2018年の課題にします。

PowerShell Core が最もいいニュースであり、今後の進む道です。これはまちがいなく、PowerShell Team がオープンな場で常に挑戦に向かっていることが素晴らしいと思います。

ShellScript

Docker をmac上で使っていましたが、Windows でも使っています。さすがに勝手が違って戸惑いが多いのでいまいちmacほど使いやすくないのですが、だいぶんWindows上でのDockerも慣れました。

さて、Docker といえばなにぶんイメージを軽くするのに最低限しかいれないこともあり、DockerFile で ShellScript はサーバー運用でごりごり書いていたころほどでなくてもかなり使います。あんまり頑張りすぎないのが、PoweShell/ShellScript に共通する普遍的なお約束だと思っているのですが、そういう意味ではほどほどに、でも効果を強くできそうでいいですね。

言語的なスキキライが、あまりなくなってきたこともあり楽しくやってます。

インフラ

私が一番力を発揮できることは? と考えると、インフラというか基盤のように思います。そういう意味で部署を横断するインフラ的な考えはかなり好みなわけで。

Serverless

インフラ設計の基本であり、真っ先にどうやるとサーバーレスでシンプルに組めるか考えるようになりました。プラットフォームや処理によりますが、全般的なことはAzureFunctions でやっています。AWS リソースのイベントなら Lambdaです。

そのなかで クライアント主軸の開発にできる場合は、Firebaseとの相性から CloudFunctions を選ぶ選択が自分のなかで当然になったのは興味深く面白いです。C# 、サーバーレスでいいと思いきやスピンアップの遅さやデプロイのめんどくささがあり、なるほどマルチプラットフォーム、エコシステムについて考えるきっかけになりました。

インフラって、めんどくさいことを極限まで消すといらない存在なのですが、そこがインフラの良さでサーバーレスの良さなので、まだまだやることは多いです。

Docker

シェルスクリプトもそうですが、Serverless を用いれない場合のインフラの最小構成単位がコンテナです。どうやってやるかが設計の要ですがアウトプットを早めにしたいです。この辺りはまたいずれ。

前述したC# ですが、Docker 対応を前提に .NETCore は基本になるのが望ましいと思っています。マルチプラットフォームのエントリーポイントが Docker なので、Docker でどこででも(ポータビリティ) 動くことを前提に組むのは今後の最低要件となるでしょう。すくなくとも C# が大きく飛躍して生きていくには。Docker はいくつかの利用者視点での側面がありますが、アプリケーションの実行単位としてのDocker は、いろいろ便利というか言語が隠ぺいされる意味ではもっとも身近でもっとも導入しやすいでしょう。これは C# にとって、非常にアドバンテージとなりえます。

GCP

GCPへの傾倒と利用がとりわけ激しかったです。AWS に一番強い自覚がありますが、今年は GCP の利用頻度が半端なかったです。GCP 最高ではないし、課題も多いですが、クライントアプリ開発者としての視点からいくと、ほかのクラウドプラットフォームを大きく突き放して使いやすいです。正直AWS がつけ入るスキが今のところほぼない。Azureも AppCenter はいいですが、ほぼない。GCP でのエコシステムに乗れるなら乗るのが私は楽と感じます。

インフラとしてみても GCP は比較的隙がなくて、AWS -> GCP も GCP -> AWS も AWS -- GCP もいいと思います。個人でも GCP をメインにしています。

GAE / Firebase / CloufFunctions / Google Cloud Datastore は、引き続き最も有力な選択肢です。Spanner や GKE は少し重いですが、まぁ初めから前提にするにはいいです。GCE Container もそういう意味では便利です。

Fastly

Fastly は、CDN の姿を変えました。いくつかの利用ケースを考えてきましたが、Fastly Yamagoya Meetup 2017 は過去に見たCDNが主の勉強会で最高に熱く、素晴らしかったです。参加できたこと、学びを得たことが本当に幸せで徹夜して資料書いて参加できてよかったです。ちょうどスケジュール的に追い詰められていたこともありますが、もう徹夜はいやです。

techplay.jp

CDN は今後のありかたは変わります。自分のプロダクトにて変えられるかが力量ですが、同時に最も楽しい挑戦でしょう。もっと Fastly の利用者増えてほしいです。国内で1位じゃないのが意味わからないです。*3

Datadog

黒騎士といえば、開発が2年続いた、終盤のクローズドベータテスト中に、「NewRelic を中心としてきた、これまでのモニタリングの仕組みでは監視がまともにできない」ことに気づいて、一気にDatadog での監視の仕組みに変えました。このあたりは 黒騎士と白の魔王を支えるDatadogを使ったモニタリング にも詳細を書きました。

engineering.grani.jp

何を使うのか、どう使うのか、どのようにモニタリングをみせるのかを今のモニタリングの考えから改めてゼロベースで考えて構築し、その後も継続的に更新できていてよかったです。すでに会社から New Relic は一掃し、Datadog に集約することで、今までよりも一歩踏み込んだモニタリングに到達できました。

C# 的に gRPC的にどのようにやるといいのかを、@neuecc とやり取りするなかで、neuecc/DatadogSharp とかも爆誕しました。.NET でも Datadog APM 使えるのでぜひ、本番でも使ってます。@t_tetsuzin とも ASP.NET MVC でどう組むか、gRPC の時と違う挑戦をしましたが、これもキレものと一緒にがっつりやりきれて良かったです。

github.com

さらに一歩先にすすめるための布石は積み重ねているので、また一歩いい感じにしたいと思います。そのあたりは2018年早々にさくさく実現します。

コミュニティ

コミュニティ活動は、アウトプットが要だと思っています。この意味でFastly Yamagoya Meetup 2017 の1本だけしか出てないのは、相当ダメです。

techplay.jp

ここは2018年の課題です。

勉強会に参加する中で、今年は「参加することに楽しむかどうか」が重要と感じました。特に、Google Cloud INSIDE Games and Apps と Serverless Conf Tokyo 2017 ではそれを強く感じ、効果的に感じました。だいたいなんでも「楽しむ」ことをモットーとしていますが、改めてなんでも、どんな自分の体調でもうまくセルフマネージメントできるかは自分自身のハンドリングなのでしょう。

記事

なんとまさかの14本。書いた数は最低です。内容もそこまで濃くもないので、これは単純にインプットの消化が間に合わず、記事を書く理由を自分で作れなかったことにあります。学びが多いのでアウトプットは本来多いのですが、まぁしかたないので今年書きましょう。

ライフスタイル

記事の要因ですが、深夜にちょっと時間をとるだけの体力がなくなってきた気がするのは、おやおやまぁまぁ。ライフスタイルも少し修正しないとなんでしょう。必ずしも時間が取れないわけではないので、すこし自分に甘えているようなのでこれは失笑ものです。

2018年は?

流れは年末から変わらず。C# / Swift / TypeScript / Serverless / Container がメインです。やること多いので、いっこずつ片付けます。

やりこんでできないより、やりきるは何より大事です。できるを最低条件に過程をよくするのは力量です。自分でハードルを挙げておいて、工夫してよじ登るのが続くのでしょう。

インフラ、サーバーサイド、クライアントサイドの視点からどうやるといいのかを考えられるようになったのが2017年の最大の獲得であり、今後試されます。ハードルあがった...。

*1:これは本当にダメとしかいいようないので、ネイティブはまちがいなくやった方がいいです。いやとかわからないと思ったら、なるべく早く。それが多少しか必要なくても、です。

*2:それが本当にそれかは常に学び続ける必要はあるし絶対的に正しいかは別ですが

*3:1位になるには帯域.....

AzureAD ログインと Docker for Windows Shared Drive によるボリュームマウント

Docker は普段 macOS で使っているのですが、そういえば Docker for Windows がGAされて久しいです。ふつうに Linux コンテナが動かせるのは WSL とは違うコンテナとしての良さに満ちてていいものです。

docker run -it centos /bin/bash

とはいえ Docker for Windows は、触るたびにHyper-V をはじめとしていくつかの特徴的な挙動で難しく感じます。

さて、今回は AzureAD でログインしている Windows において、Docker の Shared Drive が上手く使えない症状に遭遇したので、解消方法をメモしておきます。

見つけるまで結構悩んでしまったので、もしほかの方が同様の症状にあった時にうまくいくヒントとなることを祈ります。

更新

2020年6月 時点で、最新 Docker Desktop for Windows にて修正されています。やったね!

github.com

目次

概要

Docker はホストのドライブをコンテナで共有する機能があります。Docker for Windows の場合は、SMB (TCP445) を使います。(SMB/CIFS)

これにより、ホストのファイル更新をコンテナと共有が可能だったり、ログ処理したりが可能になります

環境

Docker for Windows 17.09.1-ce-win42

Share Drive 方法

Docker for Windows > Settings > Shared Drives に行きます。ここで共有したいドライブを有効にします。

  • シェアしていない状態

  • シェアした状態

うまくいきましたか? やりましたね!完璧です。そのまま docker run -v を楽しみましょう。

docker run --rm -v c:/Users:/data alpine ls /data

単純にこれで済む場合は、local User でログインしている Administrators 権限を持っているケースか、AD でログインしていて適切に権限が付与されているケースが経験あります。

注意点

Network Connection Profile

もし自ネットワークプロファイルが private でない場合は、Private にしておきます。ただ、Hyper-V のプロファイルはパブリックネットワークで構いません。

自宅や職場で guest or public netrowk は、相応の理由がない場合はちょっと困ったことを引き起こしやすいです。(Firewall ルールからしてセキュアに守られる)

Hyper-V ではない、自分のイーサネットやWifi のネットワークコネクションのプロファイルを プライベートネットワークに変更する場合は、PowerShell 上で以下でさくっとできます。*1

# 対象のId を確認しましょう。
Get-NetConnectionProfile
$id = "対象のConnectionIdをどうぞ"
Set-NetConnectionProfile -NetworkCategory Private -InterfaceIndex $id

うまくホストのネットワークが プライベートになりましたか?

Firewall

Firewall の警告が出た場合はこのケースがあり得ます。

Firewall で弾かれる場合は TCP 445 のコンテナip 10.0.75.1からのアクセスになっている DockerSmbMount のエントリが許可になっていますか?

このコンテナIP 10.0.75.1 は、Docker for Windows > Settings > Network にあるデフォルトの値です。自分でIPを設定されている場合はよきように解釈してください

もしアンチマルウェアを使っていらしゃる場合は、そのソフトのブロックを解除してあげるか White list に追加するといいでしょう。

これで共有ができましたか?

AzureAD の場合

さて、本題です。

AzureAD でログインしたUserにおいては、そのままでは Shared Drives が失敗します。*2Driveのシェアを実行 > 認証を入力 > 数秒で Share がはずれる。という状態です。

ログを見ると原因がわかります。

[11:19:49.606][NamedPipeClient][Info   ] Sending Version()...
[11:19:49.607][NamedPipeClient][Info   ] Received response for Version
[11:19:49.607][NamedPipeServer][Info   ] Version()
[11:19:49.608][NamedPipeClient][Info   ] Sending Mount(C, AzureAD\UserName:**********, Docker.Core.Settings)...
[11:19:49.607][NamedPipeServer][Info   ] Version done in 00:00:00.
[11:19:49.609][NamedPipeServer][Info   ] Mount(C, AzureAD\UserName:**********, Docker.Core.Settings)
[11:19:49.629][SambaShare     ][Info   ] Mount C
[11:19:49.679][Cmd            ][Info   ] この共有リソースは存在しません。
[11:19:49.679][Cmd            ][Info   ] NET HELPMSG 2310 と入力すると、より詳しい説明が得られます。
[11:19:49.681][SambaShare     ][Info   ] "C" is not shared
[11:19:49.681][SambaShare     ][Info   ] Creating share "C:\" as "C" with Full Control to "AzureAD\UserName"
[11:19:49.740][Cmd            ][Info   ] C が共有されました。
[11:19:49.777][Cmd            ][Info   ] 共有名             C
[11:19:49.777][Cmd            ][Info   ] パス               C:\
[11:19:49.777][Cmd            ][Info   ] 注釈
[11:19:49.777][Cmd            ][Info   ] 最大ユーザー数     制限なし
[11:19:49.777][Cmd            ][Info   ] ユーザー
[11:19:49.777][Cmd            ][Info   ] キャッシュ         キャッシュは無効
[11:19:49.778][Cmd            ][Info   ] アクセス許可       AzureAD\UserName, FULL
[11:19:49.778][Cmd            ][Info   ] コマンドは正常に終了しました。
[11:19:49.780][SambaShare     ][Info   ] "C" is shared
[11:19:50.700][SambaShare     ][Info   ] Username: UserName
[11:19:50.700][SambaShare     ][Info   ] Host IP: 10.0.75.1
[11:19:50.700][SambaShare     ][Info   ] Cifs options: noperm,iocharset=utf8,nobrl,mfsymlinks,vers=3.02,domain=AzureAD
---- 中略 ----
[11:19:52.190][SambaShare     ][Error  ] Unable to mount C drive: 10.0.75.1 (10.0.75.1:445) open
umount: can't unmount /c: No such file or directory
umount: can't unmount /C: No such file or directory
rmdir: '/c': No such file or directory
rmdir: '/C': No such file or directory
mount error(13): Permission denied
Refer to the mount.cifs(8) manual page (e.g. man mount.cifs)
mount: mounting //10.0.75.1/C on /c failed: Invalid argument

[11:19:52.192][SambaShare     ][Info   ] Removing share C
[11:19:52.244][SambaShare     ][Info   ] Mount C
[11:19:52.273][Cmd            ][Info   ] この共有リソースは存在しません。
[11:19:52.273][Cmd            ][Info   ] NET HELPMSG 2310 と入力すると、より詳しい説明が得られます。
[11:19:52.275][SambaShare     ][Info   ] "C" is not shared
[11:19:52.275][SambaShare     ][Info   ] Creating share "C:\" as "C" with Full Control to "UserName"
[11:19:52.300][ApiProxy       ][Info   ] proxy >> GET /images/sha256:9e4f13a0901e7cdc0c16babf4ebec822828ecae42947c79b69c51e2e22e0470e/json
[11:19:52.300][ApiProxy       ][Info   ] Dial Hyper-V socket 45ef270d-7ba5-4a0c-9633-f0d79d3b0f30:23a432c2-537a-4291-bcb5-d62504644739
[11:19:52.300][ApiProxy       ][Info   ] Successfully dialed Hyper-V socket 45ef270d-7ba5-4a0c-9633-f0d79d3b0f30:23a432c2-537a-4291-bcb5-d62504644739
[11:19:52.302][ApiProxy       ][Info   ] proxy << GET /images/sha256:9e4f13a0901e7cdc0c16babf4ebec822828ecae42947c79b69c51e2e22e0470e/json
[11:19:52.309][Cmd            ][Info   ] システム エラー 1332 が発生しました。
[11:19:52.309][Cmd            ][Info   ] アカウント名とセキュリティ ID の間のマッピングは実行されませんでした。
[11:19:52.311][SambaShare     ][Error  ] Failed to create share "C:\" as "C" with Full Control to "UserName" with code: 2
[11:19:52.341][Cmd            ][Info   ] この共有リソースは存在しません。
[11:19:52.343][NamedPipeClient][Info   ] Received response for Mount
[11:19:52.341][Cmd            ][Info   ] NET HELPMSG 2310 と入力すると、より詳しい説明が得られます。
[11:19:52.343][SambaShare     ][Info   ] "C" is not shared
[11:19:52.343][NamedPipeServer][Info   ] Mount done in 00:00:02.7347295.
[11:19:52.449][ApiProxy       ][Info   ] proxy >> GET /images/sha256:9e4f13a0901e7cdc0c16babf4ebec822828ecae42947c79b69c51e2e22e0470e/json
[11:19:52.449][ApiProxy       ][Info   ] Dial Hyper-V socket 45ef270d-7ba5-4a0c-9633-f0d79d3b0f30:23a432c2-537a-4291-bcb5-d62504644739
[11:19:52.449][ApiProxy       ][Info   ] Successfully dialed Hyper-V socket 45ef270d-7ba5-4a0c-9633-f0d79d3b0f30:23a432c2-537a-4291-bcb5-d62504644739
[11:19:52.450][ApiProxy       ][Info   ] proxy << GET /images/sha256:9e4f13a0901e7cdc0c16babf4ebec822828ecae42947c79b69c51e2e22e0470e/json

原因は アカウント名とセキュリティ ID の間のマッピングは実行されませんでした。 がまさにそれです。要は no mapping between security id ですが、Windows においてこれは Security上の アクセスIdが一致していないことを示します。一度 C: とドライブ共有できているにもかかわらず、なのがなかなか厄介です。 何がセキュリティIdと違うのかというと、 認証時に AzureAD ログインしているときに入れないといけない AzureAD\ が原因です。では、ということで AzureAD\ を除いてユーザー名のみにすると、そんなIdはないので共有自体が失敗します。セキュリティIdが一致していない、厄介。AzureAD で Windows ログインすることはかなり快適なのですが、こういう問題はたびたびあります。

問題がわかれば対処方法が思いつきます。

対処方法は、AzureAD\UserName の UserName だけの ローカルユーザーを作りましょう。ユーザー作成後、Administrators を与えます。これで、AzureAD\UserName で共有後に、UserName も見つかるので正常にドライブ共有が可能になります。

PowerShell ならこうです。

# ここでポップアップがでるので、追加したいユーザーのUsername と Password をいれます。
$credential = Get-Credential
New-LocalUser -AccountNeverExpires -PasswordNeverExpires -UserMayNotChangePassword -Name $credential.UserName -Password $credential.Password
Add-LocalGroupMember -Group Administrators -Member $credential.UserName

もしGUI でやりたい場合、UserName をご自身のユーザー名に置き換えてください

参考

*1:管理者権限がいります。ADならDomainAuthenticated なので問題ないはずですが Firewall ルールが問題になる可能性はあります

*2:私は5度やって5度失敗したのでそういうものでしょう

PowerShell の配列表現と生成処理時間

面白い記事があります。

blog.shibata.tech

PowerShell において配列生成は 言語仕様上にある通りカンマ演算子 , によって表現されるものであり、ASTでも満たされている。しかし、そこに言及がなく ()$()@() で生成しているような表現を見かけるけど実は違うんだよ。ということが説明されています。とはいえ、@() で囲むことに意味はあるので注意なのですが。

tech.guitarrapc.com

さて、結果としてみるとどの表現でも配列が生成されます。が、AST を見てもそれぞれ違うことから「表現可能な方法が複数ある場合にどれを使うのがいいのか」を考えてみましょう。

目次

PowerShell の構文木 AST を見る

もし PowerShell の AST が見たい場合は、ShowPSAst でモジュールを入れておくといいでしょう。

# 今のユーザーにのみ導入する
Install-Module ShowPSAst -Scope CurrentUser

github.com

配列の生成

今回は、ベンチマークでは単純に配列評価の時間を測定したいため、配列の生成は事前に文字列を起こしておきましょう。

以下のようにすると配列が生成できます。

gist.github.com

これで2種類の配列文字列が取得できたので準備ok です。

1,2,3,4,5,....

1
,2
,3
,4
,5
....

ベンチマークの測定対象

それではベンチマークを測ってみましょう。

測定対象として選んだのは記事にあった表現とその派生です。

  1. ArrayLiteralAst : カンマ演算子, による配列の生成 = 最速の予定
    • 1,2,3
  2. ParenExpressionAst + ArrayLiteralAst : () で カンマ演算子 , による配列の生成のラップ
    • (1,2,3)
  3. ArrayExpressionAst + ArrayLiteralAst : @() でカンマ演算子 , による配列の生成のラップ
    • @(1,2,3)
  4. SubExpressionAst + ArrayLiteralAst : $() でカンマ演算子 , による配列の生成のラップ
    • $(1,2,3)
  5. (ArrayExpression + ArrayLiteralAst) * PipelineOutput : @() でカンマ演算子 , による配列の生成のラップした結果をパイプラインでマップ
    • @(1,2,3) | % {$_}
  6. Constraints + ArrayLiteralAst : @() で生成した中身を前置カンマにしました
@(
1
,2
,3)

ベンチマーク結果

1000回実行したっけの平均/最大/最小を見ます。単位は ms です。

PowerShell でのベンチマークは、今回簡易に Measure-Command を用いました。

Code Target Count Average Maximum Minimum
1,2,3 ArrayLiteralAst 1000 6.72703 76.897 1.109
(1,2,3) ParenExpressionAst + ArrayLiteralAst 1000 6.70472 77.452 1.0702
@(1,2,3) ArrayExpressionAst + ArrayLiteralAst 1000 7.020254 185.7868 1.0828
$(1,2,3) SubExpressionAst + ArrayLiteralAst 1000 7.59060 85.0647 1.4674
前述参照 (ArrayExpression + ArrayLiteralAst) * PipelineOutput 1000 75.666 234.0299 52.0301
前述参照 Constraints + ArrayLiteralAst 1000 8.67313 195.6095 6.1331

いかがでしょうか? 予想通りですか?

ArrayLiteralAst

さすがに Average / Maximum / Minimum のいずれにおいても安定して最速です。

ArrayLiteralAst だけの場合、次のAST評価となっています。

# AST  : {1,2,3} | Show-Ast
# Eval : ScriptBlockAst > NameBlockAst > PipelineAst > CommandExpressionAst > [ArrayLiteralAst] > ConstantExpressionAst(s)

ParenExpressionAst + ArrayLiteralAst

こちらも ArrayLiteralAst のみと比較して、ParenExpressionAst + ArrayLiteralAst では、() で括った分一段要素が増えます。一方で実行速度にはほとんど差がなく、() は評価の軽い要素であるのが明確です。

# AST  : {(1,2,3)} | Show-Ast
# Eval : ScriptBlockAst > NameBlockAst > PipelineAst > CommandExpressionAst > [ParenExpressionAst] > PipelineAst > CommandExpressionAst > [ArrayLiteralAst] > ConstantExpressionAst(s)

ArrayExpressionAst + ArrayLiteralAst

Maximum 測定誤差がでたと考えられます。次のAST評価となっています。 ArrayExpressionAst + StatementBlock + CommandExpressionAst が増えていることからもそこそこ評価が増えてきました。が誤差レベルですね。

# AST  : {@(1,2,3)} | Show-Ast
# Eval : ScriptBlockAst > NameBlockAst > PipelineAst > CommandExpressionAst > [ArrayExpressionAst] > StatementBlockAst > PipelineAst > CommandExpressionAst > ArrayLiteralAst > ConstantExpressionAst(s)

SubExpressionAst + ArrayLiteralAst

こちらは、Minimum が少し大きいですが同様に誤差でしょう。

部分式は多用するのですが、AST評価を見ても [SubExpressionAst] > StatementBlockAst > PipelineAst > CommandExpressionAst となっており、ArrayExpressionAst とだいたい同様ですね。こちらも気にしなくてよさそうです。

# AST  : {$(1,2,3)} | Show-Ast
# Eval : ScriptBlockAst > NameBlockAst > PipelineAst > CommandExpressionAst > [SubExpressionAst] > StatementBlockAst > PipelineAst > CommandExpressionAst > ArrayLiteralAst > ConstantExpressionAst(s)

(ArrayExpression + ArrayLiteralAst) * PipelineOutput

原因は明らかで パイプラインです。ASTを見ても明らかに要素数が多くなることが分かります。パイプラインほんと重いんですよね。配列を生成するためにこの利用は避けましょう。

# AST  : {@(1,2,3) | % {$_}} | Show-Ast
# Eval : ScriptBlockAst > NameBlockAst > PipelineAst > CommandExpressionAst > [ArrayExpressionAst] > StatementBlockAst > PipelineAst > CommandExpressionAst > ArrayLiteralAst > ConstantExpressionAst(s)
#                                                             | > CommandAst 
#                                                                         | > StringConstantExpressionAst
#                                                                         | > ScriptBlockExpressionAst > ScriptBlockAst > NamedBlockAst > PipelineAst > CommandExpressionAst > VariableExpressionAst

Constraints + ArrayLiteralAst

最初の要素 1 のみ 速やかに ConstantExpressionAst として評価されています。しかし後続は前置のカンマによってシングル要素の配列 とAST評価されてしまいArrayLiteralAst とついています。AST評価を見てみると明らかですね。

# AST  : {@(
# 1
# ,2
# ,3)
# } | Show-Ast
# Eval : ScriptBlockAst > NameBlockAst > PipelineAst > CommandExpressionAst > [ArrayExpressionAst] > StatementBlockAst > PipelineAst > CommandExpressionAst > [ConstantExpressionAst]
#                                                                                                                    | > PipelineAst(s) > CommandExpressionAst > [ArrayLiteralAst] > ConstantExpressionAst
#                                                                                                                    | > PipelineAst(s) > CommandExpressionAst > [ArrayLiteralAst] > ConstantExpressionAst

まとめ

特に制約がない時に書くなら、すなおに , でくくるのみにするか () で括るのが良さそうです。

$a = 1,2,3
$b = (1,2,3)

String Interporation のような文字列埋め込みに使う 表現も悪くはなさそうです。

$c = "$(1,2,3)" // "1 2 3" となる

良く紹介される形も明らかな齟齬はなさそうです。

$d = @(1,2,3)

ただしパイプライン、お前はだめだ。

$e = @(1,2,3) | % {$_}

ベンチマークコード全体

コードを置いておきます。参考になれば幸いです。

https://gist.github.com/guitarrapc/8a89dc9438673871a71649ab8315e0e8

PowerShell のAPIデザインガイドライン

この記事は、PowerShell Advent Calendar 2017 5日目の記事です。

qiita.com

昨日は @atworks さんの PSRemoting を用いたリモートプロセス実行でした。 qiita.com

3日目の前回 PowerShell のコーディングスタイルについて触れました。

tech.guitarrapc.com

次は、残りのAPI デザインを見てみましょう。

※ 2回に分けて書きましたが個人的には個人/チームが書きやすいようにすればいい話だと思っています。しかし、「曖昧だった基本的な指針がわからず困ってた方」にとって、ベストプラクティスはいい材料となると思います。PowerShell コミュニティは結構活発なので、コミュニティの中で皆様がよい PowerShell 生活を送られることを祈っています。

目次

C#のデザインガイド

Required Development Guidelines

https://msdn.microsoft.com/en-us/library/dd878238.aspx

Design Guidelines と Coding Guidelines がありますが、Design Guidelinesのみ触れます。

Design Guidelines Use Only Approved Verbs (RD01)

  • PowerShell の Cmdlet 規則である Verb-Nown 形式で公開する時に、あらかじめ定義されてある 動詞(Verb) を用いましょう
  • 動詞は用途ごとにクラス分離されているので、適切なものを使うといいです
    • VerbsCommon
    • VerbsCommunications
    • VerbsData
    • VerbsDiagnostic
    • T:System.Management.Automation.VerbsLifeCycle
    • VerbsSecurity
    • VerbsOther
  • どの動詞をいつ使うかのガイドラインも公開されています

(Design Guidelines) Cmdlet Names: Characters that cannot be Used (RD02)

  • Cmdlet には用いることができない特殊文字があります。その一覧を示しています
  • 私は基本的にアルファベットのみ用いるようにすることで沿えるので単純にそう捉えています
  • Parameters Names that cannot be Used (RD03)
  • PowerShell Cmdlet のパラメータには予約語があります。それを避けるようにしましょう
  • Confirm, Debug, ErrorAction, ErrorVariable, OutBuffer, OutVariable, WarningAction, WarningVariable, WhatIf, UseTransaction, and Verbose

(Design Guidelines) Support Confirmation Requests (RD04)

  • もしシステム変更を伴う操作を提供する場合は、PowerShell が持っている 確認機構を用いることをさしています
    • いわゆる ShouldProcess を指しています

(Design Guidelines) Support Force Parameter for Interactive Sessions (RD05)

  • 対話的実行を提供する場合に、Force パラメータは提供しましょう
  • これは PowerShell が自動化を念頭に置かれた言語なため、対話実行でそれを妨害することを防ぐためです
    • 以下のような操作が特に注意です
    • Prompt
    • PSHostUserInterface.PromptForChoice
    • IHostUISupportsMultipleChoiceSelection.PromptForChoice
    • Overload:System.Management.Automation.Host.PSHostUserInterface.PromptForCredential
    • ReadLine
    • ReadLineAsSecureString

(Design Guidelines) Document Output Objects (RD06)

  • 出力の記述です
  • C# のドキュメントXML で説明を公開するといいです

Strongly Encouraged Development Guidelines

次は強く要請するガイドラインです。

https://msdn.microsoft.com/en-us/library/dd878270.aspx

Design Guidelines と Coding Guidelines がありますが、Design Guidelinesのみ触れます。

(Design Guidelines) Use a Specific Noun for a Cmdlet Name (SD01)

Server のような汎用的な言葉ではなく、操作対象を明示した Process のような命名が好まれます。

(Design Guidelines) Use Pascal Case for Cmdlet Names (SD02)

  • Cmdlet 名は、PascalCase で表現しましょう。Clear-ItemProperty のほうが clear-itemProperty より好ましいです

(Design Guidelines) Parameter Design Guidelines (SD03)

  • 標準的なパラメータ名が公開されているのでここにあるものはそれを利用しましょう
    • たとえば、名前ならName、出力なら Output といった具合です
    • ただ、処理によっては ServiceName のようなより明示的な名前も提供したい場合があるでしょう。その場合は、パラメータ用プロパティにAlias属性 を用いることで [Alias("ServiceName")]のように表現できます
  • 単一要素を受けるパラメータには単数名を用いて表現しましょう
    • もし-es のような複数名を用いる場合は、そのbパラメータがいつでも複数要素を受け入れる場合にしましょう
  • パラメータはPascalCaseで。C# であれば Property を用いるので、C# デザインガイドと同じで違和感はないかと思います
    • errorAction や erroraction より、ErrorAction が好ましいです
  • パラメータの組み合わせで操作が変わる Cmdlet を提供する場合2つの方法があります
    • enum を用いて、enumごとに操作を分岐し パラメータを処理する方法
    • ValudateSet属性.aspx) を用いて、パラメータの入力を制約する方法
    • 私の経験上、複数の操作を提供する Cmdlet は作りたくなります。パラメータ1つだけが特定のパラメータ時に用いないようなの「単純な組み合わせ」であれば、ValidateSet が楽でしょう。が、複数の パラメータの組み合わせを ParameterSet で表現するのはおススメしません。Cmdlet を分離することを検討するといいでしょう
  • パラメータ名にはStandard Type を用いましょう
    • あらかじめ、どんな用途(Activity) にどんなパラメータが期待されるのかリストされています
    • Append など利用者が直感的に利用しやすい API デザインとして一貫性を保つため、該当するものを利用するといいでしょう
  • 強く型付けされた .NET Framework 型を利用する
    • Object を利用するということは、型に対する意識が強いということです
    • .NET Framework の型を意識して利用すると適切な型が入ることがほしょうされるため、よいでしょう
    • たとえば、URI には Uri 型を用いれば、String 型が入ってこないことを保証できます
  • パラメータの型に首尾一貫性をもたせる
    • たとえば、Process パラメータというのにInt16 型を当てた場合、他のCmdlet の Process パラメータで Uint16 を用いるのは避けましょう
    • 利用者の直感に反するので触り心地に大きく影響します
  • true/false をとるパラメータは避けて、Switch Parameter を用いましょう
  • Switch Parameter は、もし利用していれば true、なければ false とみなします
  • もし 3値 (true, false, Unspecified) が必要な場合は、Nullable<bool> が適切でしょう
    • 個人的に、Unspecified と null を合わせるのが適切なのかは一考の余地があります
  • 可能であれば、パラメータに配列を許容しましょう
    • 例えば Get-Process は Name に String配列を許容します
    • 利用者の使い心地として、複数回 Cmdlet を実行するより、1回で済む方がうれしいことは多いでしょう
  • PassThru パラメータのサポートを検討しましょう
    • Stop-Process のような値を返さない Cmdlet (Void型) であっても、時に結果オブジェクトが必要です
    • こういった場合に、PassThru パラメータを与えることで、結果オブジェクトを返すオプションを提供しましょう
    • Add, Set, New といった Verb の Cmdlet はサポートしているものが多いです
  • ParameterSet のサポート
    • Cmdlet は1つの目的のために作ります
    • が、時に1つの操作を複数の表現で呼べることがあるでしょう。つまり、パラメータの組み合わせということです
    • このパラメータの組み合わせの表現に、ParameterSet を用いることが多いです
    • ParameterSet を用いる場合、DefaultParameterSetCmdlet 属性に指定しましょう

(Design Guidelines) Provide Feedback to the User (SD04)

ユーザーは実行中ただ待つのは苦痛です。実行に対して何かしらのフィードバックを返しましょう。

  • WriteWarning, WriteVerbose, WriteDebug メソッドのサポート

    • もし意図しない結果が起こった場合は、WriteWarning メソッドで結果をユーザーに伝えましょう
    • もしユーザーがさらなる詳細情報を求める場合、WriteVerbose で結果を返しましょう。例えば、実行シナリオが意図した状態になっているかを伝えることもいいでしょう
    • 開発者がプロダクトサポートのために必要とする情報は、WriteDebug メソッドで返すといいでしょう
  • 長時間実行時の WriteProgress サポート

    • 長時間実行する場合、進捗を WriteProgress メソッドで表示するといいでしょう
  • Host Interface を用いた対話実行

    • 時に ShouldProcess 以外に、Host を通してユーザーとやり取りをする必要に迫られます。そんなときに Host プロパティを用いましょう
    • たとえば、PromptForChoiceWriteLine/ReadLine などです
    • もしCmdlet が GUI を生成しないなら、Out-GridView Cmdlet の利用も検討できます
    • また Cmdlet は、Console API は利用すべきではありません
  • Cmdlet ヘルプファイルの生成

    • Help.xml ファイルで、Cmdlet のヘルプを伝えることができます

(Design Guidelines) Advisory Development Guidelines

アドバイスとしてのガイドラインです。

https://msdn.microsoft.com/en-us/library/dd878291.aspx

Design Guidelines と Coding Guidelines がありますが、Design Guidelinesのみ触れます。

適用時は、Code Guideline も参考にしてください。

(Design Guidelines) Support an InputObject Parameter (AD01)

  • 特定の操作で良く用いられる名前があります。InputObject です
  • パイプラインからの入力をサポートしてプロセッシングするパラメータ名によく用いられ、.NET Framework のオブジェクトを取り扱います

(Design Guidelines) Support the Force Parameter (AD02)

  • Force パラメータを用いたユーザーの権限処理や対話を操作できるようにしましょう
  • Remove-Item Cmdlet の場合、通常は readonlyファイルを消せません。しかしForce パラメータを用いることで消すことができます
    • しかし、もしユーザーがそもそもそのファイルにアクセスする権限がない場合、Force をつけても何ら変わらず「失敗」します

(Design Guidelines) Handle Credentials Through Windows PowerShell (AD03)

  • Credential パラメータをサポートし魔装。このパラメータは PSCredential型を受け認証を処理することが期待されます
  • このサポートにより、ユーザーに対して自動的にポップアップを表示し、ユーザー名やパスワード入力ができるようになります
  • Credential パラメータには、Credential属性をあてます

Support Encoding Parameters (AD04)

  • テキストやバイナリを扱うときは、Encoding パラメータをサポートします

Test Cmdlets Should Return a Boolean (AD05)

  • Test- とつく Cmdlet は Boolean を返すことが期待されます

PowerShell のデザインガイド

実は、コーディングスタイルに含まれてしまっている部分が強いので、API デザインとしては存在しません。

ただし、Best Practice が存在します。

github.com

一度目を通してみると面白いのではないでしょうか?

  • Naming Conventions
  • Building Reusable Tools
  • Output and Formatting
  • Error Handling
  • Performance
  • Security
  • Language, Interop and .Net
  • Metadata, Versioning, and Packaging

ざくっと上げます。PURE とあるものは、議論の余地があるため記載しません。

Building Reusable Tools

再利用性に注目しています。

TOOL-01 Decide whether you're coding a 'tool' or a 'controller' script

  • 自分がツールを作ろうとしているのか、ツールの操作を作ろうとしているのか意識しましょう
    • なにかをするためのツールとして書かれている場合、re-usable でしょう
    • ツールをビジネスロジックに合わせて「操作」するために書かれている場合、re-usable ではないと考えられます

TOOL-02 Make your code modular

  • 処理を、関数にすることで、re-usable になります

TOOL-03 Make tools as re-usable as possible

  • 入力をパラメータで受け取り、パイプラインに出力する
  • この仕組みは re-usable さが最大限高まります

TOOL-04 Use PowerShell standard cmdlet naming

  • PowerShell の標準のネーミングをしましょう
  • Verb-Noun大事。Get-Verb Cmdlet で標準の Verb 一覧が見れます

TOOL-05 Use PowerShell standard parameter naming

  • 標準のパラメータ名を用いましょう

TOOL-06 Tools should output raw data

  • ツールの場合、Cmdlet の処理中に、データをなるべく触らず生で出力することをコミュニティとしては期待することが多いです
  • もし出力データを操作する場合でも、最小限にとどめましょう。そうすることで、多くのシーンで re-usable になります

TOOL-07 Controllers should typically output formatted data

  • 操作する場合、re-usable は主眼ではないので適切にわかるデータにフォーマットして返しましょう

WAST-01 Don't re-invent the wheel

  • 車輪の再発明だめ
  • 下の例は、Test-Connection $computername -Quiet で表現できます
function Ping-Computer ($computername) {
    $ping = Get-WmiObject Win32_PingStatus -filter "Address='$computername'"
    if ($ping.StatusCode -eq 0) {
        return $true
    } else {
        return $false
    }
}

WAST-02 Report bugs to Microsoft

  • バグは共有しよう

Output and Formatting

出力に関してです。

Don't use Write-Host unless you really mean it

  • Write-Host だめ。良く言われますね。Host にしか出力しないので、「見せるためだけ」「フォーマットするだけ」に利用しましょう
  • 特に Show Verb を使っていたり、Format Verb を使っている関数を書いた時にしか、使わないぐらいがいいです
  • なるべく他のWrite-* Cmdlet の利用を検討してください

Use Write-Progress to give progress information to someone running your script

  • ユーザーに何かしら進捗を示すとき Write-Progress が最適です
  • ただし、パイプライン上のなんでも流せばいいというものではありません。伝えたいことにしぼりましょう

Use Write-Debug to give information to someone maintaining your script

  • スクリプトのメンテナンスをする人に向けて、Write-Debug でメッセージを送ってください
  • $DebugPreference = "Continue" とすることで、Breakpoint で止まらず結果をみることもできます

Use CmdletBinding if you are using output streams

  • [CmdletBinding()] を使うだけで、出力ストリームを操作する -Verbose などが利用できるようになります

Use Format Files for your custom objects

  • カスタム型を使う場合は、modulename.format.ps1xml を使ってフォーマットを検討してください

Only output one "kind" of thing at a time

  • 1つの関数で、複数の型を返すことを避けてください
  • [OutputType()] で伝える型とのずれが生じるのは相当なコストをユーザーに強いることになります

Two important exceptions to the single-type rule

  • もし内部関数の場合は、複数の型を返すのはありです
    • $user, $group, $org = Get-UserGroupOrg のように分けて受け取れます
  • もし複数の型を返す場合、個別に Out-Default に包んで返すことでフォーマットが混在することを避けられます

Error Handling

エラー処理です。

ERR-01 Use -ErrorAction Stop when calling cmdlets

  • Cmdlet の呼び出し時は、-ErrorAction Stop をつけてエラー時に捕まえましょう

ERR-02 Use $ErrorActionPreference='Stop' or 'Continue' when calling non-cmdlets

  • Cmdlet ではない場合、呼び出し前に $ErrorActionPreference='Stop' を実行し、呼び出し後に$ErrorActionPreference='Continue' に戻しましょう
  • 特に自動化時に適切にエラーで止めることは重要です

ERR-03 Avoid using flags to handle errors

  • フラグで失敗制御はしないでください
try {
    $continue = $true
    Do-Something -ErrorAction Stop
} catch {
    $continue = $false
}

if ($continue) {
    Do-This
    Set-That
    Get-Those
}
  • try, catch で制御しましょう
    Do-Something -ErrorAction Stop
    Do-This
    Set-That
    Get-Those
} catch {
    Handle-Error
}

ERR-04 Avoid using $?

  • $? の利用は避けましょう
  • これはエラーが前回のコマンドで発生したか示すものではなく、前回のコマンドが成功したかみるだけです。この結果に関しては、ほぼ意味がないでしょう

ERR-05 Avoid testing for a null variable as an error condition

  • null チェックを全部いれるとかやめましょう

ERR-06 Copy $Error[0] to your own variable

  • 直前のエラーが $Error[0] に収められています。catch 句の $_ も同様です
  • ただ、次のエラーですぐに上書きされるので必要なら変数にいれてください
    • $Error 配列に過去のものは入っています

Performance

PERF-01 If performance matters, test it

  • PowerShell のパフォーマンスは、妙なくせだらけです
  • パフォーマンスかな、とおもったらテストしましょう
  • たとえば、以下の例なら2つ目が早いです
[void]Do-Something
Do-Something | Out-Null
  • いくつか方法が思いつく場合、計測しましょう

PERF-02 Consider trade-offs between performance and readability

  • パフォーマンスと読みやすさはトレードオフな場合があることを考慮してください
  • 例えば、式で表現とパイプラインで表現でも変わります
$content = Get-Content file.txt

ForEach ($line in $content) {
  Do-Something -input $line
}
Get-Content file.txt |
ForEach-Object -Process {
  Do-Something -input $\_
}

あるいは、.NET Framework を直接触ることでも変わります。

$sr = New-Object -Type System.IO.StreamReader -Arg file.txt

while ($sr.Peek() -ge 0) {
   $line = $sr.ReadLine()
   Do-Something -input $line
}

さらにこんな書き方もあるでしょう。

$handle = Open-TextFile file.txt

while (-not Test-TextFile -handle $handle) {
    Do-Something -input (Read-TextFile -handle $handle)
}
  • どれがいいかといえば、なるべく PowerShell に沿った書き方が読みやすいでしょう。が、基本的には .NET Framework のラッパーにすぎません
  • いくつものパターンがある中から、パフォーマンスとご自身の美学に沿って選択してください

Security

Always use PSCredential for credentials/passwords

  • Credential や パスワードには、PSCredentail を使います
  • SecureString でパスワードが保持されるため、基本的にこれを使いましょう
param (
    [System.Management.Automation.PSCredential]
    [System.Management.Automation.Credential()]
    $Credentials
)
  • どうしても生パスワードを、そこから拾う必要がある場合、メソッドから取得しましょう。なるべくさけてください
$Credentials.GetNetworkCredential().Password

Other Secure Strings

  • 他にも、Read-Host -AsSecureString でも SecureString を受け取ることgあできます
  • 万が一 SecureString を String にする必要があるなら、ZeroFreeBSTRE を用いてメモリリークを抑えてください
    # Decrypt a secure string.
    $BSTR = [System.Runtime.InteropServices.marshal]::SecureStringToBSTR($this);
    $plaintext = [System.Runtime.InteropServices.marshal]::PtrToStringAuto($BSTR);
    [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR);
    return $plaintext
  • もしディスクに認証を保持する必要がある場合、Export-CliXml を使ってパスワードを守ってください
    # Save a credential to disk
    Get-Credential | Export-CliXml -Path c:\creds\credential.xml

    # Import the previously saved credential
    $Credential = Import-CliXml -Path c:\creds\credential.xml
  • さらにもし、String がセンシティブでディスクに保持する必要がある場合は、ConvertFrom-SecureString で暗号化してください。ConvertTo-SecureString で戻すことができます。Windows Data Protection API (DPAPI)をつかっているため、同一Windowsマシンの同一ユーザーでのみ Decrypt できるので注意です。処理として、AES 共通鍵での暗号化もサポートしています
   # Prompt for a Secure String (in automation, just accept it as a parameter)
    $Secure = Read-Host -Prompt "Enter the Secure String" -AsSecureString

    # Encrypt to Standard String and store on disk
    ConvertFrom-SecureString -SecureString $Secure | Out-File -Path "${Env:AppData}\Sec.bin"

    # Read the Standard String from disk and convert to a SecureString
    $Secure = Get-Content -Path "${Env:AppData}\Sec.bin" | ConvertTo-SecureString

Language, Interop and .Net

VER-01 Write for the lowest version of PowerShell that you can

  • サポートする、もっとも低い PowerShell バージョンのために書いてください
  • ただし、新しいほどパフォーマンスメリットがあったりします
  • たとえば、PoweShell v3 では2番目の書き方のほうがかなり高速化されます
Get-Service | Where-Object -FilterScript { $\_.Status -eq 'Running' }
Get-Service | Where Status -eq Running

VER-02 Document the version of PowerShell the script was written for

  • #requires -version 3.0 といった形でサポートしているバージョンを明記してください
  • Module の場合、PowerShellVersion = '3.0' とマニフェストの psd1 に設定することで表明できます

余談 : 個人的に注意していること

私が特に多くの人から苦しいと耳にすることで、個人的に気を付けているものは次のものです。だいたい記事にしていたので参考にしていただけると幸いです。

  • [Object]型デフォルトに起因する型をつかった操作が影響受けやすいこと

tech.guitarrapc.com

  • $null の扱い

tech.guitarrapc.com

winscript.jp

  • パイプラインを通したときの実行速度と式の違い

tech.guitarrapc.com

tech.guitarrapc.com

  • 型の明示をしない場合の暗黙の型変換 (左辺合わせ)

tech.guitarrapc.com

  • 単一要素配列が返却時に自動的なアンラップがかかる

tech.guitarrapc.com

  • より安全に書くためには StrictMode の利用がいいでしょう

blog.shibata.tech

まとめ

PowerShell Script で書く場合も、C# で書く場合と同じように気を付ければ問題なさそうです。

特に、パラメータ入力、パイプラインが最も入り組んでいる印象が強いです。独自の構文$? はコンソールでの入力以外は使わないんですよねぇ。実際、私はほぼ使わないです。

PowerShell も .NET に限らず、一般的なプログラミング言語のやり方が生きます。言語自体の構文サポートの弱さやなど癖がありますが、ゆるく付き合うといいでしょう。