参数数组中的Azure资源管理器模板网站应用程序设置

时间:2017-12-19 13:21:59

标签: parameters azure-resource-manager appsettings

我正在尝试获取一系列参数 - 特别是电子邮件收件人列表 - 并将这些参数写入我的网络应用程序的应用程序设置。

以下模板可以工作但显然只能从数组中写入第一个和第二个项目。

我研究过复制功能,但这只能处理对象的创建,但我需要添加到现有的键值对列表中。

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  ... 
  "parameters": {
    "Email:Recipients": {
    "type": "array"
  },
  ...
  "resources": [
    {
      "apiVersion": "2015-08-01",
      "type": "Microsoft.Web/sites",
      ...
      "resources": [
        {
          "apiVersion": "2015-08-01",
          "name": "appsettings",
          "type": "config",
          "properties": {
            ...
            "Email:Recipients:0": "[parameters('Email:Recipients')[0]]",
            "Email:Recipients:1": "[parameters('Email:Recipients')[1]]",
            ...
          }
        }
  ]
}

2 个答案:

答案 0 :(得分:0)

您可以在属性对象中使用复制功能 - 请参阅:https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-multiple#property-iteration

在部署模板之前,将其视为复制/粘贴操作类型。

答案 1 :(得分:0)

我有一个与您非常相似的用例:我希望用户指定用于分片的存储帐户数,并为模板创建所有存储帐户,然后为每个帐户添加一个连接字符串作为Web应用程序的应用程序设置以及我所有其他应用程序设置,格式为:

<other app settings>
...
"STORAGE_CONNECTION_STRING_00": "<connectionString00>",
"STORAGE_CONNECTION_STRING_01": "<connectionString01>",
...

我意识到我可能为时已晚,无法为您提供帮助,但是希望这会对其他人有所帮助。

该解决方案的关键在于,与其将应用程序设置指定为Microsoft.Web/sites资源的子资源,而应在属性部分内联指定它们。至关重要的是,这使您可以将它们指定为对象数组,每个对象具有namevalue属性,而不是问题中的一个大对象:

{
  "apiVersion": "2015-08-01",
  "type": "Microsoft.Web/sites",
  ...
  "properties": {
    ...
    "siteConfig": {
      "appSettings": [
        { 
          name: "STORAGE_CONNECTION_STRING_00",
          value: "<connectionString00>"
        },
        ...
      ]
  },
  ...
}

我第一次尝试使用copy将我所有的连接字符串添加到此列表中:

{
  "apiVersion": "2015-08-01",
  "type": "Microsoft.Web/sites",
  ...
  "properties": {
    "siteConfig": {
      "copy": [
        {
          "name": "appSettings",
          "count": "[parameters('resultsShardCount')]",
          "input": {
            "name": "[concat('STORAGE_CONNECTION_STRING_', padLeft(copyIndex('appSettings'), 2, '0'))]",
            "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageDataAccountNames')[copyIndex('appSettings')],';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageDataAccountNames')[copyIndex('appSettings')]), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).keys[0].value)]"
          }
        }
      ]
    }
  },
  ...
}

这可行,但是不允许我在存储连接字符串旁边添加其他任何应用程序设置。

我尝试使用单独的appsettings子资源添加其他应用设置,如最初的问题一样,希望将它们合并,但这只是覆盖了连接字符串。

接下来,我尝试定义两个变量,一个使用copy定义连接字符串对象数组,另一个定义所有其他应用程序设置对象的静态数组。我想然后可以使用union函数将它们组合起来:

  "apiVersion": "2015-08-01",
  "type": "Microsoft.Web/sites",
  ...
  "properties": {
    "siteConfig": {
      "appSettings": "[union(variables('additionalAppSettings), variables('storageAppSettings'))]"
    }
  },
  ...
}

不幸的是,模板变量被急切地求值,这意味着您不能引用任何资源属性。这对于评估连接字符串和其他一些我的应用程序设置都是一个问题,其中包含对我正在模板中部署的其他资源的引用。

研究该问题将导致查看嵌套模板,尤其是this Stack Overflow answer,以便在嵌套模板上使用copy在数组中建立动态求值对象的列表。

在我碰到another limitation的ARM模板之前,这种方法看起来非常有前途:

  

对于嵌套模板,不能使用嵌套模板中定义的参数或变量。

解决方案是改用链接模板,但这对于应该是一个小问题来说是一个巨大的矫kill过正。

在挠头之后,我最终想出了一种方法,使其仅使用输出参数,使其能够使用嵌套模板工作:

...
{
  "name": "reference0",
  "type": "Microsoft.Resources/deployments",
  "apiVersion": "2015-01-01",
  "dependsOn": [
    "storageDataAccountCopy"
  ],
  "properties": {
    "mode": "Incremental",
    "template": {
      "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
      "contentVersion": "1.0.0.0",
      "resources": [],
      "outputs": {
        "storageAppSettings": {
          "type": "array",
          "value": []
        },
        "storageAppSetting": {
          "type": "object",
          "condition": "[greater(parameters('resultsShardCount'), 0)]",
          "value": {
            "name": "[concat('STORAGE_CONNECTION_STRING_', padLeft(0, 2, '0'))]",
            "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageDataAccountNames')[0],';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageDataAccountNames')[0]), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).keys[0].value)]"
          }
        },
        "additionalAppSettings": {
          "type": "array",
          "value": [
            {
              "name": "APPLICATION_INSIGHTS_KEY",
              "value": "[reference(concat('Microsoft.Insights/components/', variables('applicationInsightsName'))).InstrumentationKey]"
            }
            ...
          ]
        } 
      }
    }
  }
},
{
  "name": "[concat('reference', copyIndex(1))]",
  "type": "Microsoft.Resources/deployments",
  "apiVersion": "2015-01-01",
  "copy": {
    "name": "storageAppSettings",
    "count": "[parameters('resultsShardCount')]"
  },
  "properties": {
    "mode": "Incremental",
    "template": {
      "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
      "contentVersion": "1.0.0.0",
      "resources": [],
      "outputs": {
        "storageAppSettings": {
          "type": "array",
          "value": "[concat(reference(concat('reference', copyIndex())).outputs.storageAppSettings.value, array(reference(concat('reference', copyIndex())).outputs.storageAppSetting.value))]"
        },
        "storageAppSetting": {
          "type": "object",
          "condition": "[less(copyIndex(1), parameters('resultsShardCount'))]",
          "value": {
            "name": "[concat('STORAGE_CONNECTION_STRING_', padLeft(copyIndex(1), 2, '0'))]",
            "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageDataAccountNames')[min(variables('maximumShardIndex'), copyIndex(1))],';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageDataAccountNames')[min(variables('maximumShardIndex'), copyIndex(1))]), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).keys[0].value)]"
          }
        }
      }
    }
  }
},
...

解释其工作原理:

reference0部署用于:

  • 将初始storageAppSettings数组输出为空数组。
  • 输出第一个连接字符串应用设置对象。
  • 输出网络应用所需的所有其他应用设置对象的数组。

然后使用referenceN遍历copy部署,对于我要部署的每个分片,一个循环(或者在您的情况下,对每个电子邮件接收者)。每个人都执行以下操作:

  • 输出新的storageAppSettings数组作为storageAppSettings数组和上一次迭代storageAppSetting中生成的referenceN-1对象的串联。
  • 输出Nth storageAppSetting对象。

请注意,在最后一个referenceN上,我们不需要输出storageAppSetting,因为第一个是在reference0中创建的,因此为了简洁起见,我们有一个condition要停止那。不幸的是,即使存在conditionvalue仍会被求值,否则会导致索引超出范围,除非您使用min(variables('maximumShardIndex'), copyIndex(1))之类的变量maximumShardIndex来防止它出现,被定义为[sub(parameters('resultsShardCount'), 1)]。另一个解决方法,但是在适当的地方它可以正常工作。

因此,最后一个storageAppSettings输出的reference数组是我们连接字符串应用程序设置对象的完整数组,而其他所有additionalAppSettings输出的reference0数组应用程序设置对象以及连接字符串。

最后,您可以在网络应用程序中将这两个数组的并集创建appSettings数组:

{
  "apiVersion": "2015-08-01",
  "type": "Microsoft.Web/sites",
  ...
  "properties": {
    "siteConfig": {
      "appSettings": "[union(reference('reference0').outputs.additionalAppSettings.value, reference(concat('reference', parameters('resultsShardCount'))).outputs.storageAppSettings.value)]"
    }
  },
  ...
}

我已经对此进行了测试,它已经成功部署了一个Web应用程序,该应用程序可以根据resultsShardCount模板参数的指定,将数据分片到N个存储帐户中。

我认为为您提供的解决方案大致相同,不同之处在于,除了建立连接字符串name / value对象的数组之外,您还将通过传递的收件人列表建立相似的数组进入您的模板。