如何创建使用 Key Vault 的幂等、可重新部署的 ARM 模板?循环依赖存在问题

How to create idempotent, re-deployable ARM templates that utilize Key Vault? Circular dependencies present issues

我正在尝试在我的资源中加入一些额外的安全功能,例如使用客户管理的密钥进行加密。对于服务总线,这需要服务总线创建托管标识并授予对 Key Vault 的访问权限。但是,在服务总线存在之前,托管标识是未知的。

在我的 ARM 模板中,我需要在不加密的情况下初始化服务总线,以便获得托管标识,授予该标识对密钥保管库的访问权限,然后使用加密更新服务总线。但是,此部署过程不可重复。在随后的重新部署中,它将失败,因为加密一旦被授予就无法从服务总线中删除。即使它确实有效,我也会执行不必​​要的步骤来删除加密并将其重新添加到每个部署中。似乎描述资源预期最终状态的 IAC 模板不能用于维护和 bootstrap 新环境。

API 管理存在同样的问题,我想添加自定义域,但它们需要 Key Vault 访问权限。这意味着当初始化步骤重复时,我无法在不删除自定义域的情况下重新部署我的 ARM 模板(或者为 'initialization' 与 'real deployment'.

保留一组单独的模板

有更好的解决办法吗?我调查了用户分配的身份,这似乎可以解决问题,但 ARM 模板不支持它们。我检查了是否有一种方法可以通过检查资源是否已经存在来使 'init' 步骤成为条件,这也不支持通过 ARM 模板。

如果要维护单个 ARM 模板,可以使用 nested deployments 定义资源然后再次引用它以更新它。

在下面的示例中,我使用系统分配的托管标识、Key Vault 和 RSA 密钥创建服务总线。在取决于所生成的密钥的同一 ARM 模板内的嵌套部署中,我随后更新服务总线以启用加密。全部在同一个模板中的一个优点是 resourceIdreference 可以使用缩写语法(即只是资源名称)。

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "name": {
            "type": "string",
            "defaultValue": "[resourceGroup().name]"
        },
        "location": {
            "type": "string",
            "defaultValue": "[resourceGroup().location]"
        },
        "tenantId": {
            "type": "string",
            "defaultValue": "[subscription().tenantId]"
        }
    },
    "variables": {
        "kv_name": "[concat(parameters('name'), 'kv')]",
        "kv_version": "2019-09-01",
        "sb_name": "[concat(parameters('name'), 'sb')]",
        "sb_version": "2018-01-01-preview",
        "sb_keyname": "sbkey"
    },
    "resources": [
        {
            "type": "Microsoft.ServiceBus/namespaces",
            "apiVersion": "[variables('sb_version')]",
            "name": "[variables('sb_name')]",
            "location": "[parameters('location')]",
            "sku": {
                "name": "Premium",
                "tier": "Premium",
                "capacity": 1
            },
            "identity": {
                "type": "SystemAssigned"
            },
            "properties": {
                "zoneRedundant": false
            }
        },
        {
            "type": "Microsoft.KeyVault/vaults",
            "apiVersion": "[variables('kv_version')]",
            "name": "[variables('kv_name')]",
            "location": "[parameters('location')]",
            "dependsOn": [
                "[variables('sb_name')]"
            ],
            "properties": {
                "sku": {
                    "family": "A",
                    "name": "Standard"
                },
                "tenantId": "[parameters('tenantId')]",
                "accessPolicies": [
                    {
                        "tenantId": "[reference(variables('sb_name'), variables('sb_version'), 'Full').identity.tenantId]",
                        "objectId": "[reference(variables('sb_name'), variables('sb_version'), 'Full').identity.principalId]",
                        "permissions": {
                            "keys": [
                                "get",
                                "wrapKey",
                                "unwrapKey"
                            ]
                        }
                    }
                ],
                // Both must be enabled to encrypt Service Bus at rest.
                "enableSoftDelete": true,
                "enablePurgeProtection": true
            }
        },
        {
            "type": "Microsoft.KeyVault/vaults/keys",
            "apiVersion": "[variables('kv_version')]",
            "name": "[concat(variables('kv_name'), '/', variables('sb_keyname'))]",
            "location": "[parameters('location')]",
            "dependsOn": [
                "[variables('kv_name')]"
            ],
            "properties": {
                "kty": "RSA",
                "keySize": 2048,
                "keyOps": [
                    "wrapKey",
                    "unwrapKey"
                ],
                "attributes": {
                    "enabled": true
                }
            }
        },
        {
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2020-10-01",
            "name": "sb_deployment",
            "dependsOn": [
                "[resourceId('Microsoft.KeyVault/vaults/keys', variables('kv_name'), variables('sb_keyname'))]"
            ],
            "properties": {
                "mode": "Incremental",
                "template": {
                    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
                    "contentVersion": "1.0.0.0",
                    "resources": [
                        {
                            "type": "Microsoft.ServiceBus/namespaces",
                            "apiVersion": "[variables('sb_version')]",
                            "name": "[variables('sb_name')]",
                            "location": "[parameters('location')]",
                            "sku": {
                                "name": "Premium",
                                "tier": "Premium",
                                "capacity": 1
                            },
                            "identity": {
                                "type": "SystemAssigned"
                            },
                            "properties": {
                                "zoneRedundant": false,
                                "encryption": {
                                    "keySource": "Microsoft.KeyVault",
                                    "keyVaultProperties": [
                                        {
                                            // Ideally should specify a specific version, but no ARM template function to get this currently.
                                            "keyVaultUri": "[reference(variables('kv_name')).vaultUri]",
                                            "keyName": "[variables('sb_keyname')]"
                                        }
                                    ]
                                }
                            }
                        }
                    ]
                }
            }
        }
    ]
}

要使用 Azure CLI 进行部署:

az group create -g rg-example -l westus2
az deployment group create -g rg-example -f template.json --parameters name=example