銀の光と碧い空

クラウドなインフラとC#なアプリ開発の狭間にいるエンジニアの日々

AWS Management Console でクロスアカウントアクセスするための IAM Role をPowerShellで生成する

複数のAWSアカウントを管理している人には待望の機能が来ました。

さて、この機能を作るためには、Switch Roleする先のAWSアカウントで、クロスアカウントを許可するIAM Roleが必要です。Management Consoleでぽちぽち作ってもいいですが、PowerShellで一発で*1作ってみました。

$assumeRolePolicyDocument の Principalですが、今回は指定したAWSアカウントの指定したユーザーに対して許可するようにしました。AWSアカウント全体に対して許可する場合は、user/<IAM_USER_NAME> のところを ROOTにすればOKです。また、ここユーザーだと個別指定になってめんどいよね...というのでGroupとかで指定したいところですが、それはできない仕様になっています。

また、付与する権限そのものはすべて許可するようにしていますが、$policy を編集すれば付与する権限を設定することも可能です。

*1:正確にはコマンド2回

PowerShell Cmdlet の中でRxを使いたい

PowerShell Advent Calendarの4日目の記事です。

PowerShell Advent Calendar 2014 : ATND

去年もPowerShell Advent Calendarなのに C# な話だったんですが、今年も性懲りもなくC# コード満載のCmdletネタになります。

PowerShell を C# から実行する - 銀の光と碧い空

最近、マルチプラットフォーム対応のポータブルクラスライブラリが話題ですが、PowerShell Cmdlet として実装するC# プロジェクトでも中身のロジックをポータブルにしておくといろいろ便利です。といっても、AndroidとかiOSに対応するところまでいかなくとも、ロジックのクラスをクラスライブラリとして分離して Cmdlet のクラスから参照してPowerShell から呼び出すほかに、たとえばそのクラスライブラリをWPFツールから参照してGUIから実行する、といったことができます。

さて、そんな状況で、わりと長い(数分くらい)の処理を実行する Cmdlet とその中身のロジックを作ることになりました。また、この処理はWPFのツールからも実行したいので、上に書いたようにクラスライブラリとして分離させることにしました。当然数分かかるため、進捗状況を知りたいので、処理の途中経過でメッセージで表示することにしました。単純に処理の結果だけ取得するなら、Task な返り値でいいのですが、進捗状況を知りたいとなると。。。と悩んだ結果、Rxを使って IObservable で進捗状況を返すことにしました(Tは独自定義の進捗状況を表すクラス)。で、IObservable なAPIをCmdletクラスで呼びだそうとして、問題はここで起きたのです。

問題を再現するためのコードを紹介します。まず、IObservable なクラスライブラリのメソッドはこんなものです*1

で、これを Cmdlet で呼ぶために、まずこんな書き方をしてみました。

Select メソッド内で飛んできた進捗を Write-Object しつつ、処理の完了を待つために、 FirstAsync したものを Wait しています。 これを実行するとこんなことになります。

Invoke-BadHelloRx : WriteObject メソッドと WriteError メソッドは、BeginProcessing メソッド、ProcessRecord メソッド、
および EndProcessing メソッドの上書きの外側から呼び出すことはできず、同じスレッド内からだけ呼び出すことができます。
コマンドレットで呼び出しが正しく作成されていることを確認するか、または Microsoft カスタマー サポート サービスにお問い合わせください。
発生場所 行:3 文字:1
+ Invoke-BadHelloRx
+ ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Invoke-BadHelloRx]、PSInvalidOperationException
    + FullyQualifiedErrorId : InvalidOperation,ObservableCmdletSample.BadRxCmdlet

ぐぬぬ... Write-Object は呼びだされたスレッドの中から実行しないといけないのですね...((ちなみに、async void にして、Waitメソッドの代わりにawaitすると、PowerShellが強制終了し、ほとんど情報のないイベントログがイベントID:1000 として記録され、さらに訳が分からなくなります)) では、ということで、SynchronizationContext.Current を取得して、 ObserveOn してみましょう。

実行すると...

Invoke-BadHelloRx : 値を Null にすることはできません。
パラメーター名:context
発生場所 行:3 文字:1
+ Invoke-BadHelloRx
+ ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Invoke-BadHelloRx], ArgumentNullException
    + FullyQualifiedErrorId : System.ArgumentNullException,ObservableCmdletSample.BadRxCmdlet

おお、ぬるぽ!!!111 Cmdletの中は SynchronizationContext.Current が null でした。

対処法を検索したところ*2*3、このCmdletが呼ばれる環境(コマンドプロンプトの中なのか、WPFの中なのか...など)がわかれば、それに対応した SynchronizationContext を作ればいいようなんですが、そもそもいろんな環境で再利用したいので、そういうこともしたくない...

と悩んでいたところ、CTO に教えてもらったのがこの方法です。

BlockingCollection<Action> を使って、RxのSubscribeの中でQueueに処理を追加し、後から逐次実行していくイメージです。

実行してみると...

f:id:tanaka733:20141201115100p:plain

無事に実行できました。この書き方がベストかどうかはわからないのですが、PowerShell Cmdlet から RxなAPIを叩くときに困ったときの助けになれば幸いです。

*1:この程度なら、Subject使わずに Observable クラスのメソッドチェーンで書けそうですが、実際は様々な非同期処理をawaitしている処理になっています。

*2:c# - Powershell custom cmdlet Calling WriteVerbose outside of implementation of standard methods - Stack Overflow

*3:powershell-for-developers/WPFJob.cs at 437aab5821e8de698168f245413d3ad725b40f7f · dfinke/powershell-for-developers · GitHub

CLR/H in Tokyo 1 で 「C# で作る PowerShell Cmdlets のすゝめ」というLTをしてきました

土曜日に、CLR/H in Tokyo 1 に参加してLTをしてきました。当日のつぶやきはこちらでどうぞ。

CLR/H in Tokyo #clrhtky1 - Togetterまとめ

日程変更があったわけですが、当初の日程だと弊社新タイトルのリリース間近で参加が厳しかったところ、日程変更があったおかげで私は参加できました。結果的には参加できて非常によかったです。

私のLTの資料はこちら。

続きを読む