tech.guitarrapc.cóm

Technical updates

AWS Windows 自動化ラウンドテーブルのセッション資料公開

2016/5/13 に、アマゾン ウェブ サービス(AWS) 様主催で、AWS で Windows を扱っている方を集めてのクローズドなラウンドテーブルの第1回が開催されました。

私も、AWS Solution Architect の@keisuke69さんにお誘いいただき登壇させていただきました。今回の資料を作るきっかけを与えていただき、本当にありがとうございます。当日参加して下さった方もありがとうございます。

今回は、ラウンドテーブルで用いた資料と、グラニのインフラの基本的な考え方を紹介します。

目次

Simple Windows Architecture on AWS

グラニは3年以上に渡りAWSでインフラ環境を構築しています。

神獄のヴァルハラゲートのリリース当初はPHPだったためAmazon Linux でしたが、2013年7月のある晩にサービス運営中のコードベースをC# にリプレースし、以降は Windows Server を中心としています。

当時と現在では、大きくインフラ構成が異なります。最も大きな変更が、インフラコンポーネントを Disposable *1 にしたことであり、よりシンプルにすることの追求です。

なぜ構成を公開するのか

今回の構成は、かなり踏み込んだ部分まで公開しています。グラニのインフラ構成を公開したのは、自分達の現状の整理と戒めを強く意識しています。

過去もそうですが、現在のインフラ構成にも多くの不満があり直したいことが山積みです。しかし直すにあたっても、そもそも私の考えが間違っていることが非常に多い自覚があります。

資料の補足と現状の理解を確認することを兼ねて少し現在の考えを書きますが、この資料や記事を読んでいただいた素敵な皆様がおかしいと感じられたことをご指導いただけると本当に嬉しいです。

Cloud Design Pattern

Cloud Design Pattern というと、AWS Cloud Design Patterns をぱっと思い浮かべることが多いと思います。しかし同時に Azure Design Pattern も浮かべる方も多いのではないでしょうか?私は、それぞれのデザインパターンを次のように大雑把に捉えています。

Cloud Design Pattern 大雑把な解釈
AWS-CloudDesignPattern インフラコンポーネント構成に関するデザインパターン
アンチパターンでないパターン集
Cloud Design Patterns: Prescriptive Architecture Guidance for Cloud Applications | Microsoft Learn サービス設計に関するデザインパターン
ベスト プラクティスパターン集

インフラ環境をDisposable(いつでも捨てられるよう)にする。これを実現するためにAWS上の構成変更を躊躇わず、マネージドサービスを積極的に利用し、インフラ全員がコードを書いています。しかし巷に溢れる優れたサービスを無差別に採用はできません、自分達にあったサービスを選択するための原則がないとすぐにカオスな状況に陥ります。

コード設計、サービス取捨選択の原則としている考え方が Azure の Cloud Design Pattern にある、Retry PatternExternal configuration store Pattern です。

Retry Pattern

AWS にかぎらず、パブリッククラウドにおける大原則であることが多いように思います。

そのための手法も多く公開されています。

docs.aws.amazon.com

azure.microsoft.com

グラニも同様に失敗しても再施行によって成功する構成を目指しています。*2

失敗時の処理方法を共有するのではなく、もう一回やればいい。

このシンプルさは失敗に対する心理的障壁を下げるだけでなく、チームに対しても単純で伝えやすいです。

External configuration store Pattern

グラニのインフラは、インスタンスを1台たてるのも100台立てるのも構成方法と処理時間に大きな違いがないようにしています。この担保をしている重要な要素が External configuration Pattern であり、スケーラビルティに大きく寄与すると思っています。

インフラは多くのミドルウェアが動きます。*3自分たちのコードもその1つです。そんな時に利用するのが External configuration store Patternです。

ミドルウェアをインスタンスに配置するにあたり、構成ファイルをデプロイ自体に含めるのではなく、S3 や Blob、場合によっては Github など外部に寄せるようにする。

もちろん自動デプロイ/再デプロイが高速、容易な場合はその限りではありません。またコードを書くにしても、AWS LambdaAzure Functions といった Serverless を利用できないかまず検討しています。*4

Github に push すれば、全サーバーの構成が速やかに自動的に変わるというのは楽なものです。

Stamp Pattern

さて、インフラ視点のクラウドデザインパターンとして採用しているのが Stamp Pattern です。*5

ウェブサーバーに限らず、事前構成に時間が掛かる役割のEC2は Stamp パターンを用いてることで、全て同一構成に揃うようにしています。

https://aws.clouddesignpattern.org/index.php/CDP:Stamp%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3

この辺は過去にも記事で書いたことがあります。

gihyo.jp

個人的には柔軟性、展開速度、シンプルさの観点からStamp Pattern はかなり嫌いです。*6Amazon AMI に Configuration Management を適用するだけで済む場合は、Stamp Pattern を利用せず CM を適用するだけで構成しています。

このあたりはWindows版 Docker が Windows Server 2016 で出るのを心から待っています。リリースされたらすぐに乗り換えて、全サーバーコンポーネントを一新します。

他の基本としている要素

Design Pattern とは少し違いますが、基本としている要素があります。

速度

先日のセッションと公開した資料の中で、「何度も、素早く繰り返すこと」(高速なイテレーション)を重視していることを述べました。

リトライするにしても、1回の処理に30min もかかっていては繰り返し自体が困難です。なるべく一回の施行は高速に、そして短いスパンで繰り返せることを目指しています。

もちろん何でも速さが正義ではありません。頻度との兼ね合いで、一日1回しか実行しないなら数分、数秒に大きなこだわりを出しても仕方ないと思っています。

副作用を伴う改善は極力選択しない

「課題A」を改善するために、「回避不可能な別の課題B」を生じる。

そんな改善は選択しないように気をつけています。*7

もちろん程度問題なので、課題B が限定的だったり無視できるようなデメリットなら採用することが多いです。また、短期的にデメリットがあっても、すぐに改善するとわかっているなら課題の大きさによっては待たずに即採用もあります。*8

大事にしているのは、「根本の問題がなんなのか、原因を分析」することです。その場ですぐに解決できなくても後日ヒントや解決策に気づけるように意識づけています。

コスト意識

AWS EC2 なら、Lambda や S3 など「実行時課金」の Serverless へ変更できないかいつも考えています。

もしEC2でやるしかなくても、Spot Instance を積極的に採用しています。EC2 オンデマンドだったり、他のリソースなら Reserved Instance を検討します。

グラニのインフラにおいて、AWS 利用コストは当初と比較して劇的に改善しました。今も、不定期に構成を見なおして改善できないか検討しています。

自動化を常に考慮する

マネージドサービスを導入する際は、自動化できることを強く評価しています。Webhook、APIを含めて、サービス間連携できることが自動化手段を提供しているかどうかは重要です。

逆に、.NET SDK がなくとも APIが公開されていれば問題とすることがあまりありません。*9

そういえば AWS でウェブコンソールを使って最後にEC2を立てたのは3年前です。ずっと SDK 経由で作成しかしていません。

デプロイ戦略

今回構成したインフラ構成において、アプリは C#6.0、ASP.NET MVC 5.3.2 です。

そのため、2013年当時は MSDeployを用いていましたが、内製ツールのRapidHouse へと一年以上前に切り替えました。

残念ながらRapidHouse の公開は今のところ予定にないのですが、当初の構想通りデプロイが 1秒で終わるようにできたのは体感できるインパクトが大きくよかったと思っています。

ずっと言っているのですが、Windows におけるシンボリックリンクの重要性は極めて高く、インフラ構成を「柔軟、簡素」にできる可能性が高いのでぜひ検討してください。

mamiya の発表を見た時に、ほとんど同じ発想だったのでどこの環境でも同じことを考えるのだなぁと思い、ブラッシュアップのため参考にさせていただきました。

github.com

資料も非常に分かりやすかったので、セッション資料でも参考にしています。

speakerdeck.com

インフラ基盤を C#へ

RapidHouse によって 各Windowsサーバーの操作がAPI公開されているので、サーバーの管理は PowerShell Remoting から HTTP(S)経由に完全移行し、C# がフル活用できるようになっています。

察しの良い方は気づかれた通り、昔のグラニは PowerShell をインフラの中心としていましたが、現在はC# が中心になっています。

もちろんPowerShell も利用しますが、ただのツールです。PowerShell DSC の Configuration や ワンライナー、補助スクリプトがメインで長大なコードは次々とC#にリプレースされています。

構成図の見直し

今回は、ヴァルハラゲートの構成図でしたが、最初期からみてどんどん変わっていっています。

変化は大事だと思っています。停滞はサービスの死に直結します。

そして変化の記録と公開も同様に大事にしています。弊社のサービス構成図は、CTOのスライド でも度々引用されています。

少し時系列で並べて見ます。構成図にない変化がかなりあるのですが、ここでは触れません。

時期 構成図
2013年3月
2013年9月
2014年1月
2015年12月
2016年5月

構成図自体も変化しています。従来は構成図を Visio で書いていましたが、現在は CloudCraft に移行しています。Visio、昔から使っていますが、ついにトドメを刺せました。

だれでも直感的に使えて再利用も共有も可能。しかも、コンポーネントの違いも3D で表現しやすい構成図。CloudCraft はそんなサービスだと思います。

cloudcraft.co

まとめ

どうでしょうか?目新しいことがない、ふつーの構成ではありませんか?Windows を AWS で使うのがツラい、Linux にしたいという声をよく聞きますが、私達は Unix/Linux でのふつーをWindows でも十分に構築できると考え実践する努力をしています。インフラの多くのコンポーネントは、実は Serverless だったりもします。

AWSにロックイン、ということもよく耳にしますが一切そんなことはありません。AWS じゃなくても、いつでも Azure や Google Cloud Platform に移行できる体制でもあります。それでも AWS を使うのは全てのバランスとAurora などの各リソースの強力、安定さからです。

グラニのインフラが目指しているのは、Windowsでうんぬんより、ユーザーにサービスをよりよく楽しんでもらえることです。そのためにできることをチーム全員が積極的に挑戦しています。Linux には、共有するという文化から始まり、多くの面でインフラのトレンドを突き進んでいます。新しい考え方、手法にはサービスがより良くなることが多く秘められています。それが Windows でできないということは誰も言っていません、だから採用する努力をしています。

あ、あと、グラニのこんなインフラを変えたい、もっと良くしたいとお考えの方をインフラ一同心からお待ちしています。

*1:Immutable Infrastructure というより Disposable Infrastructure という方がグラニには適しています

*2:失敗したらリトライ可能な状態にロールバックする、冪等性を担保するなど、多くのやり方があるでしょう

*3:なるべく動かしたくないですし、減らすことを常に図っていますが!

*4:AWS Lambda が C# に対応してくれれば、Azure Functions にある不満が API Gateway/Lambda で強く改善できるので変更するのですが...

*5:いわゆるゴールデンイメージですね

*6:正確にはEC2が嫌いです

*7:それでも失敗します……

*8:なるべく待ちますが

*9:特に Swagger に対応していると嬉しいです

Azure Functions - C# で外部アセンブリ読み込みとRoslynコンパイラに渡してみる

前回は、Azure Functions に C#コードを投げつけると Roslyn Scripting で評価して結果を返すところまでやりました。

tech.guitarrapc.com

次にやりたくなるのが、独自クラス、メソッドも Roslynに評価させることですね。自分用ヘルパーなどはみなさんお持ちだと思うので、うまく読みこませられないかやってみましょう。

目次

どうやって独自クラスなどを読みこませるか

Roslyn Scripting の Reference を見てみると、Add references つまり、参照の追加があります。これを使えば外部DLL の追加が可能そうです。

github.com

var result = await CSharpScript.EvaluateAsync("System.Net.Dns.GetHostName()", 
     ScriptOptions.Default.WithReferences(typeof(System.Net.Dns).Assembly));
外部DLL を AzureFunctions で参照する

Azure Functions では、#r キーワードを使うことで外部DLL を読み込むことができます。

github.com

azure.microsoft.com

外部DLL の配置は Function単位

1つ注意があります。#r で外部 DLLを読み込むときは Function単位で binフォルダを作ってそこに配置する必要があります。

If you need to reference a private assembly, you can upload the assembly file into a bin folder relative to your function and reference it by using the file name (e.g. #r "MyAssembly.dll").

つまりこうですね。

これは、↓図のような #loadのようにFunctionの外において参照することはできないことを意味します。

相対参照できるか試しても読み込めないことがエラーで示されます。

gist.github.com

2016-05-08T17:35:37.376 run.csx(2,1): error CS0006: Metadata file '../MyExtesnsions.dll' could not be found
2016-05-08T17:35:37.690 run.csx(7,7): error CS0246: The type or namespace name 'MyExtesnsions' could not be found (are you missing a using directive or an assembly reference?)
2016-05-08T17:35:37.690 run.csx(22,24): error CS0246: The type or namespace name 'EnumerableExtensions' could not be found (are you missing a using directive or an assembly reference?)

絶対参照なら一見読み込んでコンパイルが通るようにみえますが、今度はUnable to find assembly と、読み込んでいるはずのアセンブリが見つからないエラーがでます。

gist.github.com

2016-05-08T18:17:23.599 Function started (Id=478b950c-d69c-454a-a8c9-12de5f8f2fb5)
2016-05-08T18:17:23.770 Unable to find assembly 'MyExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Are you missing a private assembly file?
2016-05-08T18:17:23.833 Function completed (Failure, Id=478b950c-d69c-454a-a8c9-12de5f8f2fb5)
2016-05-08T18:17:23.942 Exception while executing function: Functions.Test. mscorlib: Exception has been thrown by the target of an invocation. ƒ-Test#ℛ*b2ec9bce-cf2c-4b67-bee1-e7188b743111#29-0: Could not load file or assembly 'MyExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

ふと シンボリックリンクで回避できるか試すも、特権がないので mklink も弾かれます。

グローバルに参照させる方法は打つ手にゃいのかにゃ。。。。 ということで、諦めて Function内に bin フォルダを作って配置しましょう。

外部アセンブリクラスを作成する

適当にクラスライブラリを作成します。

今回は、2つの Function CSharpCompilerWebhookCSharpCSharpCompilerSlackOuthookCSharp でこの外部アセンブリを参照します。そこで、MyExtensions.csproj のビルド後処理でコピーするようにしてみました。

gist.github.com

ポイントは、SolutionDir 変数の定義と、Targetによるdllコピー処理の記述、AfterBuildイベントのフックです。

特にVS2015 以降では、SolutionDir が .csproj で初期状態で記述されなくなったのでめんどくささ半端ありません。

  <PropertyGroup>
    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
  </PropertyGroup>
// 省略
  <Target Name="CopyDll">
    <Copy SourceFiles="$(SolutionDir)MyExtensions\bin\$(ConfigurationName)\MyExtensions.dll" DestinationFolder="$(SolutionDir)CSharpCompilerSlackOuthookCSharp\bin" ContinueOnError="true" />
    <Copy SourceFiles="$(SolutionDir)MyExtensions\bin\$(ConfigurationName)\MyExtensions.dll" DestinationFolder="$(SolutionDir)CSharpCompilerWebhookCSharp\bin" ContinueOnError="true" />
  </Target>
  <Target Name="AfterBuild">
    <CallTarget Condition="'$(Configuration)' == 'Debug'" Targets="CopyDll" />
    <CallTarget Condition="'$(Configuration)' == 'Release'" Targets="CopyDll" />
  </Target>

この MyExtensionsクラスをビルドすることで、MyExtensions.dllCSharpCompilerWebhookCSharp\binCSharpCompilerSlackOuthookCSharp\bin にコピーされればokです。

本当は CI を使うところですが、めんどうなので Github連携のまま行きます。ということで、せっかくコピーした dll が Githubリポジトリに入らないことが無いように .gitignore[Bb]in/ をコメントアウトして MyExtensions/[Bb]in/を追加します。

gist.github.com

Github上で dll が見えますね?

さぁ準備完了です。

.csx で外部アセンブリを参照してみる

さっそく参照してみましょう。まずは、CSharpCompilerWebhookCSharp Function でやります。

まず、呼び出し元の run.csx で dllを参照しusing <NameSpace>します。その上で、呼び出し先のCSharpScripting.csx でも、using <NameSpace>を行いします。

あとは、Roslyn Scripting API の通り、.WithImports() に using 対象の名前空間、.WithReferences() でアセンブリかアセンブリパスを渡します。

gist.github.com

これでコンパイルが無事に通れば、準備完了です。サンプル JSON を投げてみましょう。

gist.github.com

無事に結果が返りました。

Slackの Outgoting Webhook用の Function、CSharpCompilerSlackOuthookCSharp でも試してみましょう。

gist.github.com

無事に実行されました。

まとめ

今回の内容も Github に上げました。

github.com

これで任意の外部アセンブリを読み込ませていい感じで Roslyn Scripting API でも読みこませられますね!

かなりやりたいことは網羅できるはずです。

Azure Functions - C# のRoslyn コード評価環境を構築しよう

C# を書く環境として、Visual Studio や LinqPad、Visual Studio Code が良く話題になります。特にサクッとコード片を試す、インタラクティブにトライアンドエラーをしたいとなると、LinqPad (特に Premium) が素晴らしいです。

LinqPad に関しては、素晴らしいブログ記事があるのでぜひとも目を通されるといいと思います。

takeshik.org

takeshik.org

takeshik.org

takeshik.org

しかし、チャットでやり取りしていると LinqPad など他ツールを立ち上げること自体が億劫になります。ツールを切り替えるのは非常にコストが高く、ふとコード片を試したい、共有したい、あるいはチャット上でコードを実行したいとなると、チャット上でボットにお願いして評価してほしいでしょう。

そんな時に利用できるのが、Roslyn の Scripting API + AzureFunctions です。

とはいえ、Slack で Outgoing Webhooks を使ったチャットのコード評価ボットに関する素晴らしい記事がすでにあります。

qiita.com

今回はこの素晴らしい知見を基に、Line や 他チャットなど汎用的なWebhookをJSON経由で評価できるようにしてみましょう。

目次

Roslyn Scripting API の分離

Slack Outgoing Webhook と 汎用JSON Webhook の両方で C# Scripting を使うので、評価部分だけCSharpScripting.csx としてコードを分離します。利用したい run.csx は、#load コマンドで呼び出せるようになり二重管理から解放されます。

tech.guitarrapc.com

元記事に加えて、外部を叩いたりできるように参照をごそっと追加します。

gist.github.com

これで、評価したいコードを string として渡せば Roslyn が実行してくれるようになりました。

Webhook 用にFunction を作成する

続いて、application/json 形式で入力されたWebhookを受け入れる Azure Functions として CSharpCompilerWebhookCSharpを作成します。

gist.github.com

function.json に注意です。Slack の slashcommand や Outgoing Webhook と違い、GenericJson としてください。

Roslynコード評価を外に出したので、run.csx がとてもシンプルになりました。

検証

サンプルとして、Azure Functions 画面上でテストしてみます。google.com ページのソース文字数を数えてみましょう。

gist.github.com

うまくいきましたね。

new System.Net.Http.HttpClient().GetStringAsync("https://google.com").Result.Length

Slack Outgoing Webhook も修正してみる

とはいっても元記事そのままで、C# コード評価部分だけ外を参照するだけです。ただし、0.2 に上げた場合は、以下の設定に変わります。

gist.github.com

こちらも問題なく実行されましたね。

@C#: Enumerable.Range(10, 20).Aggregate((x, y) => x + y)

まとめ

今回のものも、Github に載せておきました。

github.com

Roslyn Scripting による C#コード評価は非常に簡単ですが、既存の何かに載せるにはサンドボックスだったり色々考慮が必要です。

こういった環境を気にしないといけないものこそ AzureFunctions に寄せると、不用意なリークも防げるため安全がある程度担保されるかと思います。

素晴らしい記事の数々に感謝です。

グラニはC#怖い人によって 数年前からすでに Chatwork上でコード実行が出来ているのですが、Azure Functions に実行基盤を移譲できそうでよかったです。

わたしが C# を学ぶにあたって教わっている先達のサイトをまとめてみる

書いていないネタは多いのですが、アンケートで C# についてと言われました。

そこで、私自身 C# を学ぶにあたって参考にしているものをまとめておくことことにします。*1

はじめに感謝と尊敬を。ここに載せていないサイト、書籍の多くからも学びも得ています。今現在もそうです。

私自身が何か恩返しをできればと思いつつ、同じように悩まれている方への参考となれば幸いです。

目次

個人ブログ

順番には大きな意味はありません。

サイト ブログ主 参考にしている分野 備考
++C++; // 未確認飛行 C ++C++; // 管理人: 岩永 (@ufcpp) / Twitter C#, プログラミング全般 C#は書籍よりもここから。
初めての C# から現在もずっと学び続けています。
実は初めての PowerShell もここでした。
ご本人とあまりしゃべらないけどセッションいつも楽しいです。
neue cc neuecc (@neuecc) / Twitter C#, Rx, ASP.NET, OWIN, Unity, プログラミング全般 C# をどういった時に、どのように使うか、なぜC#なのかを学んでいます。
C# じゃなくても言語を問わない実装の違い、理由にも詳しくてすごい。
実際は本人から直接学ぶ部分が非常に多いです。
kekyoの丼 – プログラムの話題とか Kouji Matsui (@kekyo2) / Twitter C#, 言語実装, 動的生成 なぜC# でその処理をそのように書くのかって疑問じゃないですか?
C#にかぎらずプログラムについて、Windowsについて学んでいます。
async/await や LINQ について度々読みなおしています。
COMとか全然理解が追いつかなくてぐぬぬ...
NyaRuRuが地球にいたころ NyaRuRu (@NyaRuRu) / Twitter C#, プログラミング全般 やばいです。とても影響を受けています。
内容に最も感動を受けたサイトの1つです。
https://t.co/pBQkOmDxij ぐらばく☪ (@Grabacr07) / Twitter C#, WPF WPF といえばダントツです。
図書館の如くネタにあふれています。
ぷろじぇくと、みすじら。 Mayuki Sawatari (@mayuki) / Twitter C#, プログラミング全般 全力ガチサイトです。
本当に勉強になります。すぎょい。
銀の光と碧い空 たなか (@tanaka_733) / Twitter C#, .NET Core, RHEL, IIS, CI 他のサイトにない一風変わった品揃えです。
.NET でサーバーサイドとか考えるならまさにです。
最近 .NET Core 中心で色々注目です
ぐるぐる~ ぐるぐる系SQL(@bleis)さん / Twitter C#, F# ぐるぐる~
TAKESHIK.ORG たけしけー (@takeshik) / Twitter C#, 式木, LinqPad, 変なこと いつも更新すると面白い内容です。
一通り見てると面白いです。
平々毎々(アーカイブ) Kentaro Inomata (@matarillo) / Twitter C#, F#, Java C# を一歩下がった目で冷静に評価されているという印象です。
中身かなり濃いのでとてもおもしろいです。
Threading in C# - Free E-book Joe Albahari C#, Threading C# で Thread や Task, Parallel って何を知りたかった時に@neuecc に教えてもらった神サイトです。
感動を覚えるぐらい素晴らしい内容で、日本語訳とかもあったりします。
しばやん雑記 Tatsuro Shibamura (@shibayan) / Twitter IIS, Azure, ASP.NET 色々なネタがあって面白いです。
Azure に強いですが、他の色々な情報があって好きです。
ASP.NET で困ったらだいたいここで助けられます。
miso_soup3 Blog みそ (@miso_soup3) / Twitter ASP.NET Web API, Azure Swagger とか OWIN とか、Web系の取っ掛かりはいつもここです。
xin9le.net じんぐる (Takaaki Suzuki) (@xin9le) / Twitter C#, LINQ, Rx 結構シリーズもの多いです。サラッと読めます。
かずきのBlog@hatena かずき(Kazuki Ota) (@okazuki) / Twitter C#, Rx, WPF, UWP ほぼ毎日更新があります。
毎回の記事が短いので読みやすいです。
シリーズものが多いです。
pierre3のブログ ピエール (@pierusan2010) / Twitter C#, PowerShell 何かに刺激を受けて、というサンプル記事が多く参考にさせていただくことが多いです。
鷲ノ巣 あえとす (@aetos382) / Twitter C#, PowerShell, ETW いいですよ、すごく。
他の人が書かない記事が多いですし、造詣が深く勉強になります。
http://hidori.jp/ Hidori (@hidori) / Twitter C#, PowerShell 初めのころに多くの記事で学びました。
更新お待ちしております。
テラシュールブログ 椿 (@tsubaki_t1) / Twitter C#, Unity Unity で困ったらここ
RyotaMurohoshi - Qiita むろほし(@RyotaMurohoshi)さん / Twitter C# デリゲート, Func と LINQ についての理解に助けられました。
ScottGu's Blog - ScottGu's Blog Scott Guthrie (@scottgu) / Twitter ASP.NET, Azure全般 Azure関連の大きな動向とか
Scott Hanselman - Scott Hanselman's Blog Scott Hanselman 🌮 (@shanselman) / Twitter ASP.NET, Microsoft全般 大体真っ先の情報が出たりします
Code, code and more code. Marc Gravell (@marcgravell) / Twitter C#, Redis 言わずと知れた StackOverflow の凄腕すごい人
グラニで利用しているフレームワークの開発者でもあるので超重要
You’ve Been Haacked | You’ve been Haacked is a blog about Technology, Software, Management, and Open Source. It’s full of good stuff. https://hachyderm.io/@haacked (@haacked) / Twitter C# 他の人が扱ってなくて困リ切った時に救われます。
Implementing the Singleton Pattern in C# Jon Skeet (@jonskeet) / Twitter C#, Singleton シングルトンの歴史すごい。Lazy<T> 便利!

Microsoft関連

サイト 備考
C# リファレンス | Microsoft Learn リファレンスです。C# でこれどうだっけ?をさくっと確認するときに
.NET API ブラウザー | Microsoft Learn このクラスって... を調べるならここです。
一日何度お世話になっていることか
Introduction - C# language specification | Microsoft Learn C# 言語仕様をみたくなったら
Reference Source .NET Framework のリファレンスソースが公開されているのです。中身みれるの最高
.NET Platform · GitHub .NET 関連の GitHub リポジトリです。Roslynm Coreclr から始まって色々みるのです
Microsoft · GitHub Microsoft 関連の GitHub リポジトリです。TypeScript, VSCode, MSBuild から docker のFork まで
GitHub - dotnet/reactive: The Reactive Extensions for .NET Reactive Extensions (Rx) ならここ
あと Home · ReactiveX/RxJava Wiki · GitHub はMicrosoft 関連じゃないですが、Rx の絵が親切

困ったときの

サイト 備考
Stack Overflow - Where Developers Learn, Share, & Build Careers 困った時の世界の先輩
Google Yahoo や Bing じゃないです。Google しか使ってません。
だいたいエラーメッセージで叩くと救われます。

まとめ

色々忘れている気がします。不定期に更新しよう。。。

*1:あまりこういう記事は書かないのですが思うところもあったので

Azure Functions の API Key を扱ってみる

Azure Functions を使っていて気になるのが認証制御です。

AWS API Gateway + Lamdba では、任意の Token をつけることができました。それでは Azure Functions はどうでしょうか?

App Service Authentication/Authorization のような、アカウント連携はあまりに重厚でしょう。単純にAPI Keyで済ませたいものです。

そこで今回は Azure Functions で API Keyを使った認証について見てみましょう。

目次

API Key認証の例

API Key認証といってもどんなものでしょうか。

AWS

たとえば API Gateway では API Keys としてトークンを発行できます。

このトークンを任意のAPIで有効化することで、APIごとに認証トークンがヘッダにないとアクセスできないようにできます。

Azure Functions

翻って Azure Functions です。

Azure Functions を触っていて気づくのが、Github WebhookだけURL が違い/Github Secretを持つということです。

なんともわかりやすいでしょう。

Function 種類 Function Url フォーマット Github Secret サンプル
Empty なし なし
Http Trigger https://{function app name}.azurewebsites.net/api/{function name}?code={api key} なし
Generic Webhook https://{function app name}.azurewebsites.net/api/{function name}?code={api key} なし
Github Webhook `https://{function app name}.azurewebsites.net/api/{function name} {api key}

API Key の詳細

ここ最近アップデートされたドキュメントに概要が説明されています。

azure.microsoft.com

You can use an HTTP or WebHook trigger to call a function in response to an HTTP request. The request must include an API key, which is currently only available in the Azure portal UI.

基本的には HTTP、WebHookトリガーのいずれもリクエストに API Key を含む必要があります。

さて API Key などの認証については、Function の設定に依ります。すなわち Function が HTTP Trigger なのか Webhook なのかということです。

Integrate タブから function.json を見てみましょう。

Advanced Editor から詳細の json を操作できます。例えば GenericWebhook なら次のとおりです。

{
  "bindings": [
    {
      "webHookType": "genericJson",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req"
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ],
  "disabled": false
}

プロパティのの説明は、ドキュメントにあるとおりです。

HTTP Request のプロパティです。

プロパティ Webhookトリガーの場合 HTTPトリガーの場合
name Function コード内でリクエストオブジェクトを示す変数名として利用されます。*1
res にしたならコードでも res として受けるということです。
同左
type 必ず httpTrigger とします。 同左
direction 必ずin とします。 同左
webHookType 正当な入力値は github, slack, genericJsonのいずれかです。 WebHook ではないのでこのプロパティを空文字"" とします。
authLevel Webhookトリガーの場合無視され適用されません。 API Keyについて制御できます。
function とすることでAPI Keyを必須とできます。
anonymousとすることで API Keyを無視します。
adminとすることで、master API Key を必須とします。

HTTP Response のプロパティです。

プロパティ 説明
name レスポンスオブジェクトを示す変数名です。
reqにしたならコードでも reqで返す必要があります。
type 必ず http とします。
direction 必ず out とします。

API Key の保存場所

API Key は、D:\home\data\Functions\secrets に保存されています。

Kudu で見てみましょう。

host.json

最も大事な master key は、host.json に書かれています。

host.json に書かれた2つのキーを説明します。

キー 説明
masterkey FunctionApp 内の全てのFunctionで利用できます。無効のFunctionもトリガー実行できません。
function.json で authLeveladminにすることで必須にできます。
functionkey FunctionApp 内の全てのFunctionで利用できます。無効のFunctionはトリガー実行できません。

しかしこの host.json の鍵は権限が強すぎます。Wehook のプロバイダと共有なんてもっての他です。

もっと対象の Function だけに働きかけられる鍵はないでしょうか。

Functionごとの鍵

実際の利用を考えた場合、host.json ではなく、各ファンクションを作った際に自動的に生成される<Function名>.jsonの鍵を使いましょう。例えば GenericWebhookCSharp1 というFunction を作成したらGenericWebhookCSharp1.json がその鍵をもつファイルです。

この鍵の key プロパティの値が、対象Function を叩くための API Key です。

またこの鍵は、Functionを作成した時の Function Url で、クエリストリングに埋め込まれています。

トリガーによるAPI Key 必須選択と渡し方

ドキュメントにある通りです。Wehook トリガーでは API Key は必須です。HTTP トリガーでは選択可能です。

To trigger a WebHook function the HTTP request must include an API key. For non-WebHook HTTP triggers, this requirement is optional.

API Key の埋め込み方

API Key は2つの埋め込み方があります。

  1. code という名前で、クエリストリングに含める
  2. x-functions-key という、HTTP リクエストヘッダに埋める*2

もちろんHTTPトリガーに関しては、function.jsonauthLevelanonymous にすることで API Key がなくても問題なくできます。

簡単なメソッドで検証してみましょう。1つはクエリストリングでキーを渡します。もう1つはx-functions-keyヘッダで渡します。

gist.github.com

Webhook トリガー

HTTP Request は、API Key をクエリストリングで含んでいる必要があります。

クエリストリングにAPI Key がなかったり、間違っていると BadRequest が返ります。

gist.github.com

gist.github.com

正しいAPI Key を含めると、jsonが判定されて応答が返ってきます。

gist.github.com

また、ヘッダにx-functions-key でキーを渡してもダメなのが特徴的です。

gist.github.com

HTTP トリガー

API Key はオプショナルです。この指定を行うのが、先ほどの function.json の HTTP Request にある authLevel プロパティです。

authLevel が省略 or function

もしfunction.json で authLevel が省略された場合はfunction相当、となるためAPI Key が必須となります。

クエリストリングにAPI Key がなかったり、間違っているとUnauthorized が返ります。

gist.github.com

gist.github.com

クエリストリングに正しいAPI Key を含めると、jsonが判定されて応答が返ってきます。

gist.github.com

Webhook と違い、ヘッダにx-functions-key でキーを渡することでも認証ができます。

gist.github.com

authLevelanonymous

匿名認証のためAPI Key は無視されます。URL さえ正しく叩ければいい状態です。

gist.github.com

authLeveladmin

host.json のmasterKey 以外の鍵では呼べなくなります。host.json のfunctionKey でも実行できません。

gist.github.com

鍵の変更

もし API Key を変更したくなったどうしましょうか?

ドキュメントに記述がありませんが、D:\home\data\Functions\secrets の鍵を書き換えれば更新されます。

変更前 :

変更後 :

変更が確認できましたね

gist.github.com

まとめ

これに気づかず、独自ヘッダ認証を組み込んでからドキュメントが発行されました。

Azure functions で作成するとデフォルトで API Key が必須です。HTTPS であることからも、安全がある程度担保されているので、いい感じに扱えますね。

API の更新も含めて、うまく使う目処がそろそろ立つのではないでしょうか?

*1:あるいは、Node.js の場合はリクエストボディとして扱われます。

*2:HTTP Trigger でのみ利用可能のようです