Provision Access Policy with DependsOn

Viewed 892

I'm trying to do the following:

  • Create a keyvault (works)
  • Create an AZ function (works)
  • Use the keyvault's vaultURI as an App Setting to the AZ function (works)
  • Give the AZ function managed identity (works)
  • Create an Access Policy whereby the AZ function has access to the keyvault (whoopsie!)

Originally I was creating the Access Policy within the Keyvault, but I had to declare the AZ function as depending on the Key Vault (such that I could fetch its URI). Obviously, I was then unable to set Key Vault to depend on the AZ Function (since this would create a cyclic dependency). I then tried creating the AccessPolicy as another step, and marking it as depends on the KeyVault, and the AZ Function (thinking it would be provisioned last).

But for some reason, when looking at the deployment logs, it always seems like he's trying to deploy it first! Any help would be appreciated.

ARM Template truncated for brevity:


  "resources": [
    {
      "type": "Microsoft.KeyVault/vaults",
      "apiVersion": "2016-10-01",
      "name": "[variables('keyVaultName')]",
      "location": "[ResourceGroup().location]",
      "properties": {
        "sku": {
          "family": "A",
          "name": "Standard"
        },
        "tenantId": "[subscription().tenantId]",
        "accessPolicies": [
          {
            "tenantId": "[subscription().tenantId]",
            "objectId": "[parameters('userId')]",
            "permissions": {
              "keys": [
                "Get",
                "List",
                "Update",
                "Create",
                "Import",
                "Delete",
                "Recover",
                "Backup",
                "Restore"
              ],
              "secrets": [
                "Get",
                "List",
                "Set",
                "Delete",
                "Recover",
                "Backup",
                "Restore"
              ],
              "certificates": [
                "Get",
                "List",
                "Update",
                "Create",
                "Import",
                "Delete",
                "Recover",
                "Backup",
                "Restore",
                "ManageContacts",
                "ManageIssuers",
                "GetIssuers",
                "ListIssuers",
                "SetIssuers",
                "DeleteIssuers"
              ]
            }
          }
        ],
        "enabledForDeployment": false,
        "enabledForDiskEncryption": false,
        "enabledForTemplateDeployment": false
      }
    },
    {
      "type": "Microsoft.KeyVault/vaults/accessPolicies",
      "name": "[concat(variables('keyVaultName'),'/add')]",
      "apiVersion": "2018-02-14",
      "dependsOn": [
        "[resourceId('Microsoft.Web/sites', variables('functionName'))]",
        "[resourceId('Microsoft.KeyVault/vaults', variables('keyVaultName'))]"
      ],
      //tried both the above and the below
      "dependsOn": [
        "[variables('keyVaultName')]",
        "[variables('functionName')]"
      ],
      "properties": {
        "accessPolicies": [
          {
            "tenantId": "[subscription().tenantId]",
            "objectId": "[reference(concat(resourceId('Microsoft.Web/sites', variables('functionName')), '/providers/Microsoft.ManagedIdentity/Identities/default'), '2015-08-31-PREVIEW').principalId]",
            "permissions": {
              "keys": [
              ],
              "secrets": [
                "Get",
                "Set",
                "Delete"
              ],
              "certificates": [
              ]
            }
          }
        ]
      }
    },
.
.
.
.
.
.
.
.
    {
      "type": "Microsoft.Web/sites",
      "apiVersion": "2016-08-01",
      "name": "[variables('functionName')]",
      "location": "[ResourceGroup().location]",
      "dependsOn": [
        "[variables('planName')]",
        "[variables('appInsightsName')]",
        "[variables('storageAccName')]",
        "[variables('keyVaultName')]",
        "[variables('databaseName')]"
      ],
      "kind": "functionapp",
      "identity": {
        "type": "SystemAssigned"
      },
      "properties": {
        "serverFarmId": "[variables('planName')]",
        "enabled": true,
        "reserved": false
      },
      "resources": [
        {
          "apiVersion": "2015-08-01",
          "name": "connectionstrings",
          "type": "config",
          "dependsOn": [
            "[variables('functionName')]",
            "[variables('databaseName')]"
          ],
          "properties": {
          }
        },
        {
          "apiVersion": "2015-08-01",
          "name": "appsettings",
          "type": "config",
          "dependsOn": [
            "[variables('functionName')]",
            "[variables('appInsightsName')]",
            "[variables('storageAccName')]",
            "[variables('keyVaultName')]"
          ],
          "properties": "[union(variables('completeAppSettings'),json(concat('{ AzureWebJobsStorage:\"', concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccName'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccName')), '2019-04-01').keys[0].value), '\", WEBSITE_CONTENTAZUREFILECONNECTIONSTRING:\"',\tconcat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccName'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccName')), '2019-04-01').keys[0].value), '\", WEBSITE_CONTENTSHARE:\"', variables('functionName'), '\", APPINSIGHTS_INSTRUMENTATIONKEY:\"', reference(concat('microsoft.insights/components/', variables('appInsightsName'))).InstrumentationKey, '\", KeyVaultUri:\"', reference(concat('Microsoft.KeyVault/vaults/', variables('keyVaultName'))).vaultUri, '\"}')))]"
        },
        {
          "type": "slots",
          "apiVersion": "2016-08-01",
          "name": "[variables('functionStagingName')]",
          "location": "[ResourceGroup().location]",
          "dependsOn": [
            "[variables('functionName')]",
            "[variables('keyVaultName')]"
          ],
          "kind": "functionapp",
          "properties": {
            "enabled": false,
            "serverFarmId": "[variables('planName')]"
          }
        }
      ]
    }
  ]

P.s. I know the way I'm creating my app settings is a work of art (sarcasm). Please don't judge me, just know that it works.

P.p.s Complete ARM Template: https://pastebin.com/mma4PyRu

2 Answers

your template is absolutely fine, so if this doesnt work it looks like a bug. having said that, you can always work around this by moving your accessPolicy assignment into a nested template:

{
   "type": "Microsoft.Resources/deployments",
   "apiVersion": "2018-05-01",
   "name": "linkedTemplate",
   "dependsOn": [
       "[variables('keyVaultName')]",
       "[variables('functionName')]"
   ],
   "properties": {
       "mode": "Incremental",
       "templateLink": {
       "uri":"https://mystorageaccount.blob.core.windows.net/AzureTemplates/newStorageAccount.json",
       "contentVersion":"1.0.0.0"
    },
    "parameters": {
        "managedIdentityId":{"value": "[reference(concat(resourceId('Microsoft.Web/sites', variables('functionName')), '/providers/Microsoft.ManagedIdentity/Identities/default'), '2015-08-31-PREVIEW').principalId]"}
    }
}

You'd need to upload the template somewhere (which should consist of just your accessPolicy assignment

Reading: https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-linked-templates#external-template

Try removing the apiVersion from the reference() function, that should delay the call until after the resource is provisioned. e.g.

[reference(concat(resourceId('Microsoft.Web/sites', variables('functionName')), '/providers/Microsoft.ManagedIdentity/Identities/default')).principalId]"
Related