LoginSignup
2
2

More than 5 years have passed since last update.

ARM Templateで「ネストしたリソースでcopyを使いたいけど使えなくて困った」というときの対応

Last updated at Posted at 2018-05-24

tl;dr

※コードは例であり、実際の動作確認はしていません

例として、SQL Database for SQL Serverを作り、そこに複数のIP制限をかけたい場合を考える。

ベースはこれ。

{
    "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
    },
    "variables": {
        "sqlServerName": "mysqlserver",
        "sqlServerAdminName": "superadmin",
        "sqlServerAdminPassword": "5ecRet!"
    },
    "resources": [
        {
            "comments": "SQL Database for SQL Server",
            "type": "Microsoft.Sql/servers",
            "name": "[variables('sqlServerName')]",
            "apiVersion": "2015-05-01-preview",
            "location": "[resourceGroup().location]",
            "properties": {
                "administratorLogin": "[variables('sqlServerAdminName')]",
                "administratorLoginPassword": "[variables('sqlServerAdminPassword')]",
            },
            "resources": [
                {
                    "type": "firewallRules",
                    "apiVersion": "2014-04-01-preview",
                    "dependsOn": [
                        "[concat('Microsoft.Sql/servers/', variables('sqlServerName'))]"
                    ],
                    "name": "AllowAllWindowsAzureIps",
                    "properties": {
                        "endIpAddress": "0.0.0.0",
                        "startIpAddress": "0.0.0.0"
                    }
                }
            ]
        }
    ]
}

ここで、複数のグローバルIPを与えて制限を行いたいと考え、以下のような配列を用意してVariablesを設定する。

"sqlServerAllowIps": ["1.1.1.1", "2.2.2.2"]

この時、書き方としては以下のように「配列の数だけ繰り返してリソースを用意すればよい」と思いついた。が、これはうまくいかない。

    "resources": [
        {
            "comments": "SQL Database for SQL Server",
            "type": "Microsoft.Sql/servers",
            "name": "[variables('sqlServerName')]",
            "apiVersion": "2015-05-01-preview",
            "location": "[resourceGroup().location]",
            "properties": {
                "administratorLogin": "[variables('sqlServerAdminName')]",
                "administratorLoginPassword": "[variables('sqlServerAdminPassword')]",
            },
            "resources": [
                {
                    "type": "firewallRules",
                    "apiVersion": "2014-04-01-preview",
                    "dependsOn": [
                        "[concat('Microsoft.Sql/servers/', variables('sqlServerName'))]"
                    ],
                    "name": "AllowAllWindowsAzureIps",
                    "properties": {
                        "startIpAddress": "0.0.0.0",
                        "endIpAddress": "0.0.0.0"
                    }
-                }
+                },
+                {
+                    "type": "firewallRules",
+                    "apiVersion": "2014-04-01-preview",
+                    "dependsOn": [
+                        "[concat('Microsoft.Sql/servers/', variables('sqlServerName'))]"
+                    ],
+                    "name": "[concat('AllowIp-', copyIndex())]",
+                    "properties": {
+                        "startIpAddress": "[variables('sqlServerAllowIps')[copyIndex()]]",
+                        "endIpAddress": "[variables('sqlServerAllowIps')[copyIndex()]]"
+                    },
+                    "copy": {
+                        "name": "dummy",
+                        "count": "[variables('sqlServerAllowIps')]"
+                    }
+                }
            ]
        }
    ]

これを実行すると以下のようなエラーになる。

Deployment template validation failed: 'The template resource '[concat('AllowIp-', copyIndex())]' at line '1' column '123' is not valid. Copying nested resources is not supported. Please see https://aka.ms/arm-copy/#looping-on-a-nested-resource for usage details.'.

ざっくりいえば、ネストしたリソースに対してcopyを使うことはできない、という意味。

エラー文に含まれているURLを見ろ、ということだが、これは少しわかりにくいと感じたので今回補足する。

先に成功例を貼ると、以下のように修正する。ネストさせない形で書けば良い。

    "resources": [
        {
            "comments": "SQL Database for SQL Server",
            "type": "Microsoft.Sql/servers",
            "name": "[variables('sqlServerName')]",
            "apiVersion": "2015-05-01-preview",
            "location": "[resourceGroup().location]",
            "properties": {
                "administratorLogin": "[variables('sqlServerAdminName')]",
                "administratorLoginPassword": "[variables('sqlServerAdminPassword')]",
            },
            "resources": [
                {
                    "type": "firewallRules",
                    "apiVersion": "2014-04-01-preview",
                    "dependsOn": [
                        "[concat('Microsoft.Sql/servers/', variables('sqlServerName'))]"
                    ],
                    "name": "AllowAllWindowsAzureIps",
                    "properties": {
                        "startIpAddress": "0.0.0.0",
                        "endIpAddress": "0.0.0.0"
                    }
                }
            ]
-        }
+        },
+        {
+            "type": "Microsoft.Sql/servers/firewallRules",
+            "apiVersion": "2014-04-01-preview",
+            "dependsOn": [
+                "[concat('Microsoft.Sql/servers/', variables('sqlServerName'))]"
+            ],
+            "name": "[concat(variables('sqlServerName'), '/', 'AllowIp', '-', copyIndex())]",
+            "properties": {
+                "startIpAddress": "[variables('sqlServerAllowIps')[copyIndex()]]",
+                "endIpAddress": "[variables('sqlServerAllowIps')[copyIndex()]]"
+            },
+            "copy": {
+                 "name": "dummy",
+                 "count": "[variables('sqlServerAllowIps')]"
+            }
+        }
+    ]

要点は以下

  • ネストにならないように変更する
  • type を変更する指定する(ネストではなくなったので。)
  • name"引数1/引数2"というイメージで設定する必要がある。
    • firewallRulesの例でいうと、nameには"{serverName}/{firewallRuleName}" となるようにする必要がある。
    • {serverName}は実際に存在するSQL Serverのリソースの名前を指定する。 variables('sqlServerName') になる。
    • {firewallRuleName} はユニークな値を選ぶ必要がある。(ここはユニークになれば自由な名前でよい)

分かりにくい解説

少し話はズレるが、Rest APIのURLを示すと以下のような形となっている。

https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Sql/servers/{serverName}/firewallRules/{firewallRuleName}?api-version=2014-04-01

(引用元: https://docs.microsoft.com/en-us/rest/api/sql/firewallrules/createorupdate)

この時、providers以降の文字列を見てほしい。

Microsoft.Sql/servers/{serverName}/firewallRules/{firewallRuleName}

この形はリソーステンプレートのtypeの部分に似ている。しかし、間に{serverName}を書く形となっており、少し違う。typeにはこのようには書けない。

ARMTemplateではnameに必要なパラメータを書く決まりとなっているようだ。なので、自由な名前を付けるだけではなく、このRestAPIの可変値の部分を満たすように設定する必要がある。
書き方も決まっており、可変値を順番に/で区切りながら設定しなければならない。今回の場合は"{serverName}/{firewallRuleName}"となる。

なので"[concat(variables('sqlServerName'), '/', 'AllowIp-', copyIndex())]"のように指定し、作成した際のserverNameを与えつつ、ループ毎にユニークなfirewallRuleNameを与える形にする必要があった。

ARM Template辛い。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2