Windows Azure Compute Emulator + IIS Expressで外部IPからの要求を受信可能にする

Visual StudioでWindows AzureのWebロール開発をする場合、プロジェクトテンプレートから生成すると、普通にIIS Express+Compute Emulatorでの開発・デバッグを行うと思います。

この時、Webプロジェクトのデバッグ設定でIIS Expressを使い、”localhost”+ランダムなポート番号を割り当て、Webプロジェクトそのものでデバッグを開始すると、その通りのバインディングで要求を受け付けるのに、Cloudプロジェクトからデバッグ実行すると、何故かポート番号が「81」とかに勝手に強制されたり、localhost(127.0.0.1)以外のIPアドレス・インターフェイスからの要求を受け付けない等で困ったことが無いでしょうか?

私の中でも長らく謎でしたが、業を煮やして調べることにしました。
(なお、現在のWindows Azure SDKは2.2です)

というのも、Windows PhoneからIIS Expressに接続させるようなアプリをデバッグする場合に、WP7までのエミュレーターは、WP内ネットワークのシミュレートを、ホストマシンのネットワークに直接バイパスする機能があったため、WPから「localhost」の指定でIIS Expressに問題なく接続できていました。

WPEmulatorNetwork

ところが、WP8になってエミュレーターがHYPER-Vベースになった関係で、ネットワークシミュレートは単にHYPER-Vの仮想スイッチへの接続となってしまい、ホストマシンのIPアドレス(又はホスト名)を指定しないと接続出来なくなったのです。すると、そもそもIIS Expressはデフォルトでlocalhost以外のインターフェイスをバインドしていないため、全く要求を受け付けず、デバッグ出来ません。

また、Cloudプロジェクトでデバッグを開始すると、ポート番号が勝手に変更されるだけでなく、何で「80」じゃなくて「81」(又はその付近のポート番号)に強制されるのか? ですが、WindowsのIIS(IIS Expressではなく)が80を使用している事によって引き起こされるようです。

これは想像出来たものの、ややこしすぎる orz

で、環境の都合などどうでもいいので、ちゃんと指定された通りに動いてほしい。元々ランダムなポート番号を使うのだから、そのまま素直に使ってくれれば良いでしょ?というのが動機です。

具体的には、以下の対応を行います。

  1. Visual Studioは「管理者権限」で起動する
    正確には、IIS Expressを管理者権限で起動すれば良いのですが、デバッグ時はVSから起動するので、VSを管理者権限で起動します。その理由は3へ。
  2. ファイアーウォールに穴をあける。HYPER-Vからは仮想スイッチ経由で接続されるので、穴があいている必要があります。セオリー通り、最初はファイアーウォールをオフで試して、全て確認できたら明示的に穴を開けるという手順で行きましょう。
  3. IIS Expressの構成ファイルのサイトバインディング情報を「手動修正」して、すべてのネットワークインターフェイスでリッスンさせる。localhost(ループバックインターフェイス)だけではなく、全てのインターフェイスでリッスンを行うためには、「管理者権限」が必要です。これが1の理由となります。そして、IIS Expressの構成ファイルを修正しますが、パスは「マイドキュメントIISExpressconfigapplicationhost.config」です。sitesタグ内のbindingInformation属性を修正します。デバッグ中のサイトに対応するsiteエレメントを探してください。
<site name="AzureServiceSample" id="23">
   <application path="/" applicationPool="Clr4IntegratedAppPool">
      <virtualDirectory path="/" physicalPath="C:PROJECTAzureServiceSampleAzureServiceSample" />
   </application>
   <bindings>
      <binding protocol="http" bindingInformation="*:45934:" />   <!-- ココ -->
   </bindings>
</site>

CloudProjectProperty

上の例の「bindingInformation」属性に、「*:45934:」のように、コロン2個で区切られた文字列を指定します。左側はバインドするインターフェイスに対応するIPアドレスを指定(今回は全てのインターフェイスでリッスンさせるので、アスタリスク)、中央はポート番号、右側はホスト名(HTTPヘッダのホスト名に対応し、マルチホーム識別に使うもの)です。上記の例は単純に、すべてのインターフェイスをリッスン、ポート番号として45934、マルチホームは使わない、と言う事になります。

そして、CloudプロジェクトでCompute Emulatorとセットでデバッグする場合は、このように「Webプロジェクトポートの使用」をTrueに変更します。これで、Cloudプロジェクトデバッグ時も同じ構成(ポート番号)でデバッグ出来ます。

所で、Cloudプロジェクトでデバッグを開始すると、「applicationhost.config」ファイルは上記のパスではなく、テンポラリフォルダに生成されたファイルを読みに行きます。そのため、このファイルを手でカスタマイズしていると、Cloudプロジェクトのデバッグ実行では全く反映されず、これまた悩みます。XMLなので手で簡単にいじれます、が、実際には触れないと言う事です。

WindowsFirewallInboundUnblockRule

ファイアーウォールに穴をあける場合、Windowsファイアーウォールの「受信の規則」で、このような感じでリモートIPアドレスを普段の社内・宅内ネットワーク・あるいは固定IPならそのアドレスに絞り込んでおくと良いでしょう。本当はポート番号も絞りたいところですが、ランダム番号なので、プロジェクト生成時に毎回メンテナンスしなければならず、不便です。もちろん、これは開発時かつ安全な環境である事が前提です。

これで、以下のようにクリーンな開発が出来ます。

  1. 空のWebプロジェクトを作って、一から実装する(ウィザードが作る余計なコードを除外)。
  2. このWebプロジェクトで普通にIIS Expressで普通にデバッグして完成させる。
  3. ここで初めてCloudプロジェクトを追加。
  4. Cloudプロジェクトからデバッグ実行して、Compute Emulator+IIS Expressでも全く問題なく動作する事を確認。
  5. デプローイ!!!

ちなみに、今回はクライアントをWindows Phoneエミュレーターとしましたが、例えばWPの実機や、WCFを使うWPFやWinFormsのクライアントでも、全く同じ手順で問題を回避出来ます。

自分の覚書を兼ねて書きましたが、正直もうちょっとフレンドリーであって欲しいと思います(次回また忘れそうだ)>VS