Search code examples
azureazure-resource-manager

How to combine copy and singular properties in ARM template


How can I create an array in my Azure ARM template, consisting of both singular objects and dynamically created objects using the copy mechanism.

In my ARM template I want to create a list that is combined of 'static' elements and ones that are dynamically created based on values in an array.

For instance I have the following resource:

{
    "type": "Microsoft.KeyVault/vaults",
    "apiVersion": "2019-09-01",
    "name": "[parameters('keyVaultName')]",
     ...
    "properties": {
         ...
         // Option 1: I can do a singular accessPolicty
         "accessPolicies": [
                {
                    //  here I reference a static object
                      "objectId": "[parameters('configuredResourceId')]",
                    ...
                }
         ],
         // Option 2: Or iterate a list
         "copy": [
           {  
            // List of all properties per project. This will dynamically set the event processor (including secrets) and event hub.
            "name": "accessPolicies",
            "count": "[length(parameters('serviceNames'))]",
            "input": {
                "objectId": "[reference(resourceId('Microsoft.Web/sites', parameters('serviceNames')[copyIndex('accessPolicies')]), '2019-08-01', 'Full').identity.principalId]",
                "tenantId": "[subscription().tenantId]",
                "permissions": {
                    "keys": "[parameters('keysAppPermissions')]",
                    "secrets": "[parameters('secretsAppPermissions')]"
                }
            }
        ]
     }
 } 

When I only include option 1 or option 2 it works. However inclduing both (not ertirely unexpected) this gives the error that I can't define the same property twice:

 'Can not add property accessPolicies to Newtonsoft.Json.Linq.JObject. Property with the same name already exists on object.'.
 
 

In some cases I can probably work around this, by using the copy in the variables and then adding elements to an array. But that doesn't work when I want to use different types of functions based on the resource state (such as fethcing Ids).


Solution

  • I had the same problem when adding Key-Vault Policies with the 'copy' method and also adding static Key-Vault Policies.

    I solved it by adding the static policies as you described in the ARM. Next I defined another/child resource which is called: Microsoft.KeyVault/vaults/accessPolicies

    The last step I had to do was adding the dependsOn property and refer to the belonging Key-Vault.

    The show you in JSON close to your code:

    {
        "type": "Microsoft.KeyVault/vaults",
        "apiVersion": "2019-09-01",
        "name": "[parameters('keyVaultName')]",
         ...
        "properties": {
             ...
             // Step 1: add static policies:
            "accessPolicies": [
                {
                    //  here I reference a static object
                    "objectId": "[parameters('configuredResourceId')]",
                    ...
                }
            ],
    
        }
    },
    ...
    {
        "type": "Microsoft.KeyVault/vaults/accessPolicies",
        "name": "[concat(parameters('keyVaultName'), '/add')]",
        "apiVersion": "2019-09-01",
        // Step 2: add depending key-vault (parent resource)
        "dependsOn": [ 
            "[resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName'))]"
        ],
        "properties": {
            // Step 3: add the copy method to iterate policies
            "copy": [
                {
                    // List of all properties per project. This will dynamically set the event processor (including secrets) and event hub.
                    "name": "accessPolicies",
                    "count": "[length(parameters('serviceNames'))]",
                    "input": {
                        "objectId": "[reference(resourceId('Microsoft.Web/sites', parameters('serviceNames')[copyIndex('accessPolicies')]), '2019-08-01', 'Full').identity.principalId]",
                        "tenantId": "[subscription().tenantId]",
                        "permissions": {
                            "keys": "[parameters('keysAppPermissions')]",
                            "secrets": "[parameters('secretsAppPermissions')]"
                        }
                    }
                }
            ]
        }  
    },
    ...
    

    Please try the above ARM flow.