AzureのARMテンプレートを理解するpart2 (ネットワークリソース編)


はじめに

前回の記事でARMの基本的な概念及び構造を説明したので、
今回は自分流にテンプレートをカスタマイズしていく方々のために筆を進めたいと思います。

いきなりすべてを一つの記事に書いてしまうとずいぶん長くなってしまいそうなので、まずは仮想マシン等のIaaSを使用する上で必ず必要になってくるネットワークリソースに関して書いていきます。

本記事で取り上げるのは、仮想ネットワーク、サブネット、NSG、NIC、パブリックIPになります。

それでは行ってみましょー!

仮想ネットワーク

仮想ネットワークはネットワークリソースの中心となるリソースになります。
依存関係の中心にいる感じになります。
基本的なテンプレート例は以下になります。

template.json
{
     "type": "Microsoft.Network/virtualNetworks",
     "name": "[parameters('virtualMachineName')]",
     "apiVersion": "2016-03-30",
     "location": "[resourceGroup().location]",
     "properties": {
         "addressSpace": {
              "addressPrefixes": [
                 "[parameters('addressPrefix')]"
             ]
          },
         "subnets": [
             {
                 "name": "[parameters('subnetName')]",
                 "properties": {
                     "addressPrefix": "[parameters('subnetPrefix')]",
                     }
                 }
             ]
         },
     }

気を付けるべき点としては、仮想ネットワークの中の子リソースとしてサブネットの定義が必要であることです。
またNetwork Security Groupをサブネットに適用する場合はサブネット内で指定する必要があります。
他には特に難しい点はなく、比較的テンプレートの書きやすいリソースになっています。
名前やアドレス帯はすべてパラメーターから引っ張ってきていますが、ハードコーディングでリソースの定義内に書き込んでしまっても大丈夫です。

サブネット

正直ネットワークリソースで私が一番引っかかったのがサブネットです。
先ほど仮想ネットワーク内でサブネットも定義したじゃん!と思って進めていたら、エラーの応酬に見舞われました。
どうやらARMテンプレートから作成する際はサブネットを一つのリソースとして定義しないとだめだそうです。
この点はもう仕様としか言いようがないので受け入れてください。
で、たどり着いたテンプレート例が以下になります。

template.json
{
       "type": "Microsoft.Network/virtualNetworks/subnets",
       "name": "virtualNetworkname/subnetName",
       "apiVersion": "2017-06-01",
       "properties": {
           "provisioningState": "Succeeded",
           "addressPrefix": "[parameters('subnetPrefix')]"
       },
       "dependsOn": [
           "[resourceId('Microsoft.Network/virtualNetworks', parameters('VirtualNetworkName'))]"
       ]

   }

気を付けるべき点としてはnameの値は仮想ネットワーク名/サブネット名にする必要がある点です。
また仮想ネットワークがないとサブネットは作成できないので、dependsOnに仮想ネットワークを記載する必要があります。

NSG

NSGはセキュリティを向上させるファイアーウォールリソースになります。
サブネットとNICに適用できるのですが、今回はNICに紐づける想定で進めます。
また、NSGのルールを同時に定義する必要がありますが、細かくやっているとそれもまた長くなってしまうので、一つだけルールを参考に書いていきたいと思います。
以下がテンプレート例です。

template.json
{
       "type": "Microsoft.Network/networkSecurityGroups",
       "name": "[parameters('NetworkSecurityGroupName')]",
       "apiVersion": "2016-03-30",
       "location": "[resourceGroup().location]",
       "properties": {
           "securityRules": [
               {
                   "name": "RDP-allow",
                   "properties": {
                       "protocol": "*",
                       "sourcePortRange": "*",
                       "destinationPortRange": "3389",
                       "sourceAddressPrefix": "*",
                       "destinationAddressPrefix": "*",
                       "access":"Allow",
                       "priority": 100,
                       "direction":"Inbound"
                   }
               }
           ]
       }

上記の様にプロパティ内でルールを一つ一つ書いていく形になります。今回記述したのはRDPを全開放にするルールです。
※RDPをAnyで開けてしまうとセキュリティ的にリスクが大きいので必ずsourceAddressPrefixで範囲を絞ってください。

パブリックIP

パブリックIPは必須のリソースではございませんが、今回は参考に書いていきます。
以下がテンプレート例です。

template.json
{
    "type": "Microsoft.Network/publicIPAddresses",
    "name": "[parameters('PublicIPAddressName')]",
    "apiVersion": "2016-03-30",
    "location": "[resourceGroup().location]",
    "properties": {
        "publicIPAllocationMethod": "Dynamic"
    }
},

特に気を付けるべき点がないくらいにすっきりとしたリソースです。
publicIPAllocationMethodはIPアドレスの動的、静的のパラメーターになります。静的化したい場合はStaticとしてください。

NIC

最後はNICになります。
NICはここまで作成してきたもののほとんどに依存関係を持つために、それぞれをdependsOnに記載していく必要があります。
以下がテンプレート例です。

template.json
{
    "type": "Microsoft.Network/networkInterfaces",
    "name": "[parameters('NicName')]",
    "apiVersion": "2016-03-30",
    "location": "[resourceGroup().location]",
    "properties": {
        "networkSecurityGroup": {
            "id":"[resourceId('Microsoft.Network/networkSecurityGroups', parameters('NetworkSecurityGroupName'))]"
        },
        "ipConfigurations": [
            {
                "name": "ipconfig1",
                "properties": {
                    "privateIPAllocationMethod":"Dynamic",
                    "publicIPAddress": {
                        "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('PublicIPAddressName'))]"
                    },
                    "subnet": {
                        "id": "[variables('SubnetRef')]"
                    }
                }
            }
        ]
    },
    "dependsOn": [
        "[resourceId('Microsoft.Network/publicIPAddresses', variables('PublicIPAddressName'))]",
        "[resourceId('Microsoft.Network/virtualNetworks', variables('VirtualNetworkName'))]",
        "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('NetworkSecurityGroupName'))]"
    ]
}

ここで気を付けなければならないのがサブネットの指定方法です。サブネットが毎回付きまとってきますね。
ポータルで確認できない情報になりますが、ここではリソースIDでサブネットを指定する必要があります。
上記の例ではvariablesから引っ張ってきていますが、そのvariablesは以下のようになっています。

variables.json
"variables": {
     "VnetID": "[resourceId('Microsoft.Network/virtualNetworks',parameters('virtualNetworkName'))]",
     "SubnetRef": "[concat(variables('vnetID'),'/subnets/',parameters('subnetName'))]"
}

上記をかみ砕くと、サブネットのリソースIDは
「仮想ネットワークのリソースID/subnets/サブネット名」
となります。
また、他がdependsOnに書いているような形式ではリソースID呼び出せないものとなっています(というよりも呼び出し方がどこにも書いていなく、詰みました)。

おわりに

テンプレートのサンプル自体はネット上に結構転がって入るのですが、自分で一から作るとなると依存関係であったりパラメータの書き方であったり考慮する点がたくさんありますね。
特にサブネットの扱いなんてのは、二回定義しなくては作成中にエラーが出てしまうという何とも不思議な仕様になっています。。。
恐らく今後改修されるとは思いますが、現状は仕様に従って書いていくしかないですね。
ただリソースの定義を一度書いてみると大体の感覚が掴めてきて、どの要素が必要なのかと見当がつきやすくなっていきます。
(一からすべてを書いていく必要はありませんが)一度一部分だけでも試しに書いてみると面白いです。
ではまた次回は別の系統のリソースについて書いていきたいと思います。
(^^)/