Azure Cloud Services - ACLによるIP制限からNetworkSecurityGroupへの移行

######ネクストスケープ配信事業本部AdventCalendar2017の二日目。

```
2017/12/18
下記件ですが対応されたようです。

2017/12/10
一つ注意点を記載します。
現在、CloudServicesにNSGを設定するとオートスケールが失敗するという現象があります。
オートスケールはAzure Monitorを使用して実行されますが、Azure Monitorで使用している
スケール処理用のREST APIのバージョンが古いため、NSG側で弾かれてしまうというのが原因のようです。
このバグについては、年内に対応予定とのことです。
```
#はじめに
AzureCloudServicesでIP制限を行う場合は、CSCFGのNetworkConfigurationにAccessControlList(ACL)の設定を記述する方法が一般的かと思われます。
ただ、既にご存知かもしれませんが、ACLの上限は「50個」となりますので、レンジ指定なしとかでIPを設定していると割とすぐ上限に達してしまいます。

[エンドポイント アクセス制御リスト (ACL)の上限](https://docs.microsoft.com/en-us/azure/azure-subscription-service-limits?toc=%2fazure%2fvirtual-network%2ftoc.json#networking-limits)

ACLの上限を変更することは不可能なので、もし50個を超えて設定したい場合は、**CloudServiceを仮想ネットワーク(VNet)に移行し、NetworkSecurityGroup(NSG)を設定**するしか方法がありません。
この記事では私がCloudServicesをVNetへ移行しNSGの設定をした際に気づいたことなどをつらつらと記述していきます。

#VNetの作成
CloudServicesはクラシックリソースですのでVNetを作成する際のデプロイメントモデルは「Classic」を選択しましょう。
（でなければCloudServicesに設定した際に「見つからねーよ」となります。）

![01_VNet作成.png](https://qiita-image-store.s3.amazonaws.com/0/150006/a9bea1e0-7945-8e49-2ece-82bbd93df68c.png)

ここではあまりVNetの細かい話はしませんが、以下だけ注意してください。
・VNetに載せられるリソースは同じサブスクリプション＆同じリージョン内のものに限られます
　（以下は同じサブスクリプション内のリソースである前提で話します）
・VNetを作成する際は、特にどのリージョンにCloudServicesを配置しているかを確認してください
・東日本、西日本にそれぞれCloudServicesがある場合は、VNetも東日本、西日本にそれぞれ作成する必要があります
・AddressSpaceの範囲がかぶったVNetは同じリージョン内では作成できませんが、リージョンが異なれば作成できます

![02_VNet作成.png](https://qiita-image-store.s3.amazonaws.com/0/150006/c7b66444-5542-e8b9-aca8-8684bedac01a.png)

#NSGの作成
NSG作成の際もデプロイメントモデルは「Classic」を選択しましょう。
（でなければ（ry　）

![10_NSG作成.png](https://qiita-image-store.s3.amazonaws.com/0/150006/9b5d7adc-6055-1da9-3671-a31321913ff3.png)

NSGについてもCloudServicesを配置しているリージョンに作成するようにしてください。
（ResourceGroupがCreate newとなってますがそこは察してください）

![11_NSG作成.png](https://qiita-image-store.s3.amazonaws.com/0/150006/28ffc209-8355-9061-1726-3a6a725c2ead.png)

以下はNSGにいろんなルールを設定するための画面になります。
InboundSecurityRulesとOutboundSecurityRulesとありますが、前者がVNetの外からのアクセスに対して設けるルール、
後者がVNetの中から外へのアクセスに対して設けるルールとなります。

![12_NSG画面.png](https://qiita-image-store.s3.amazonaws.com/0/150006/99c8e56f-4e6a-3155-5413-c51e57c4227e.png)

両者ともデフォルトのルールが設定されており（編集・削除不可）、InboundSecurityRulesの方は全拒否がデフォルトになっています。
（OutboundSecurityRulesの方は去る者は追わずのスタンス）

![13_NSG画面.png](https://qiita-image-store.s3.amazonaws.com/0/150006/8575ce55-daed-d674-4110-c54dcc288568.png)

ちなみに、デフォルトルールの表示・非表示は上の方にある真実の目みたいなボタンで切り替えることができます。

![14_NSGDefaultRules.png](https://qiita-image-store.s3.amazonaws.com/0/150006/164f8154-62e3-d031-882f-fa470d34609f.png)

ルールを追加してみましょう。

|設定名|内容|
|:-----------------|:------------------|
|Name| ルール名。同じNSG内でかぶっちゃだめ |
|Priority|優先度。アクセスが来た際に優先度順でルールが判定される|
|Source系の設定|アクセス元についての設定。IP指定やらポート指定やら|
|Destination系の設定|アクセス先についての設定|
|Action|許可(Allow)するか、拒否(Deny)するか|

![20_NSG_add.png](https://qiita-image-store.s3.amazonaws.com/0/150006/0b1d8f09-a3e9-5a98-3a24-e927242ca14b.png)

何点か注意点を。
・Name、あるいは、PriorityはNSG内でかぶってはいけません
・SourcePortRangeをどう設定するかは最終的に本人次第ですが、セキュリティホールになられても嫌なので特定のポートを指定するか、レンジで指定した方がよいかと。
　ただ、HTTPとHTTPSを許可する場合はポート「80」と「443」をそれぞれ個別のルールとして追加しなければならないので若干面倒です。
・ルールをたくさん追加する場合はPowerShellスクリプトを作成したほうがよいです。
　ただ、一つのルールの作成や削除は数十秒かかったりするので、シーケンシャルにやるとかなり時間がかかります。
　パラレルにできるように工夫しましょう。
・RDP接続する場合は、**ポート「3839」だけでなく、ポート「20000」(2万)**も許可してください。
　CloudServicesの場合はLoadBalancerを挟むため後者のがないと接続できません。

下の図は追加後のもの。
（DefaultRulesは非表示にしてます）
（ついでにRDP接続に必要なルールも追加しました）

![15_NSG画面.png](https://qiita-image-store.s3.amazonaws.com/0/150006/97d9093e-2ee3-c1ed-08d7-8cf140fd50d8.png)

以上でVNetとNSGの準備ができたので次からは既存のCloudServicesをVNet上に載せてNSGを設定する方法を記述します。

#CloudServicesをVNet上に載せる
CloudServicesをVNetに載せてNSGを設定するにはCSCFGに定義を追加します。

既存のCloudServicesの設定（ACLを使用）

```xml
<NetworkConfiguration>
  <AccessControls>
    <AccessControl name="acl">
      <!--許可したいIPアドレス-->
      <Rule action="permit" description="allow-ip-001" order="100" remoteSubnet="198.198.198.1/32" />
    </AccessControl>
  </AccessControls>
  <EndpointAcls>
    <EndpointAcl role="WidevineLicenseProxy.FrontWeb" endPoint="EndpointHttp" accessControl="acl" />
    <EndpointAcl role="WidevineLicenseProxy.FrontWeb" endPoint="EndpointHttps" accessControl="acl" />
  </EndpointAcls>
</NetworkConfiguration>
```

上記のNetworkConfigurationを以下にあるように**書き換えます**。
（ACLとVNet/NSGの設定は共存できません）

```xml
<NetworkConfiguration>
  <!--載せる先のVNet名-->
  <VirtualNetworkSite name="Group cs-vnet-migration cs-vnet" />
  <AddressAssignments>
    <!--設定対象のロール-->
    <InstanceAddress roleName="CloudService.FrontWebRole">
      <!--VNet内のSubnetを設定-->
      <Subnets>
        <Subnet name="default" />
      </Subnets>
    </InstanceAddress>
  </AddressAssignments>
  <NetworkSecurityGroupRefs>
    <!--設定対象のロール-->
    <NetworkSecurityGroupRef roleName="CloudService.FrontWebRole">
      <!--使用するNSG設定-->
      <NetworkSecurityGroup name="Group cs-vnet-migration cs-nsg" />
    </NetworkSecurityGroupRef>
  </NetworkSecurityGroupRefs>
</NetworkConfiguration>
```

また、VNet名やNSG名ですがクラシックなリソースなので以下の書式で記述する必要があることに留意ください。
> Group {ResourceGroup Name} {VNet/NSG Name}

やることは以上のようにCSCFGを書き換えてあげるだけの簡単な作業です。
既存のCloudServicesをVNet上に移行する場合はポータル上でCSCFGを編集するか、Stagingスロットにデプロイしスワップするかの2通りありますが、前者だとダウンタイムが発生するため、後者の方がよいでしょう。

#まとめ
最初のうちは許可IPが少ないためACL使用してても問題にならないでしょうが、急に許可IPが増えてきた場合にVNet用意して～、NSG設定して～とやるのも面倒なので、今後は新規にCloudServicesを作成する際はもうVNet+NSG前提の設計としていた方がよいですね。

以上です。