Как создать идемпотентные повторно развертываемые шаблоны ARM, использующие Key Vault? Циклические зависимости создают проблемы

Я пытаюсь включить в свои ресурсы некоторые дополнительные функции безопасности, такие как шифрование с использованием ключей, управляемых заказчиком. Для служебной шины это требует, чтобы в служебной шине было создано управляемое удостоверение и предоставлен доступ к хранилищу ключей. Однако управляемое удостоверение становится известным только после того, как служебная шина существует.

В моем шаблоне ARM мне нужно инициализировать служебную шину без шифрования, чтобы получить управляемую идентификацию, предоставить этому удостоверению доступ к хранилищу ключей, а затем обновить служебную шину с шифрованием. Однако этот процесс развертывания не повторяется. При последующих повторных развертываниях произойдет сбой, так как шифрование не может быть удалено из служебной шины после того, как оно будет предоставлено. Даже если бы это сработало, я бы выполнял ненужные шаги, чтобы удалить шифрование и снова добавлять его при каждом развертывании. Кажется, что шаблон IAC, который описывает ожидаемое конечное состояние ресурса, не может использоваться как для поддержки, так и для начальной загрузки новой среды.

Та же проблема существует для управления API: я хочу добавить пользовательские домены, но для них требуется доступ к Key Vault. Это означает, что я не могу повторно развернуть свой шаблон ARM без удаления пользовательских доменов при повторении шага инициализации (или сохранить отдельный набор шаблонов для «инициализации» по сравнению с «настоящим развертыванием».

Есть ли лучшее решение для этого? Я изучил назначенные пользователем идентификаторы, которые, похоже, могут решить проблему, но они не поддерживаются в шаблонах ARM. Я проверил, есть ли способ сделать шаг init условным, проверив, существует ли уже ресурс, и это также не поддерживается с помощью шаблона ARM.




Ответы (1)


Если вы хотите сохранить один шаблон ARM, вы можете использовать вложенные развертывания, чтобы определить ресурс, а затем снова сослаться на него, чтобы обновить его.

В следующем примере я создаю служебную шину с назначенным системой управляемым удостоверением, хранилищем ключей и ключом RSA. Во вложенном развертывании в рамках одного и того же шаблона ARM, который зависит от генерируемого ключа, я затем обновляю служебную шину, чтобы включить шифрование. Преимущество нахождения всех в одном шаблоне заключается в том, что resourceId и reference можно использовать с сокращенным синтаксисом (т. Е. Только с именем ресурса).

{
    "$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
person Heath    schedule 25.03.2021
comment
Независимо от того, является ли шаблон вложенным или нет, разделение развертывания служебной шины на два этапа приводит к проблеме, о которой я упоминал в исходном сообщении. Последующие повторные развертывания завершатся сбоем, поскольку шифрование нельзя удалить из служебной шины после того, как оно будет предоставлено. Даже если бы это сработало, я бы выполнял ненужные шаги, чтобы удалить шифрование и снова добавлять его при каждом развертывании. Та же проблема существует для управления API: я хочу добавить пользовательские домены, но для них требуется Key Vault. Я не могу повторно развернуть свой шаблон ARM, не удалив пользовательские домены, когда шаг инициализации повторяется. - person siegelc; 27.03.2021
comment
Если вам нужно повторно развернуть шаблон ARM, наличие отдельных шаблонов для ресурсов, не связанных с служебной шиной, может быть более безопасным вариантом, если инкрементное развертывание с включенным, но не указанным шифрованием завершится неудачно. Возможно, вы могли бы добавить condition к ресурсу верхнего уровня, чтобы избежать повторного развертывания, если он уже существует. - person Heath; 28.03.2021
comment
Нет условия проверить, существует ли ресурс в шаблоне ARM - feedback.azure.com/forums/281804-azure-resource-manager/ Кроме того, все компоненты, которые имеют интеграцию с хранилищем ключей, имеют это проблема, а не только служебная шина. Управление API, концентратор событий, служебная шина, учетная запись хранения - ни один из них нельзя безопасно повторно развернуть с помощью шаблона ARM, если они интегрированы с хранилищем ключей из-за этой циклической зависимости. Так что это действительно нежизнеспособно, как IAC, если я не пропущу здесь решение. - person siegelc; 29.03.2021
comment
Единственная ссылка, которую я смог найти об использовании развертываний ARM для подобных ситуаций на docs.microsoft.com, рекомендовала использовать два отдельных шаблона, но даже тогда, похоже, не предполагалось, что поддерживаются последующие инкрементные обновления. Решение сценариев, если вам нужно повторно развернуть, может быть единственным вариантом. Это проблема курицы или яйца. Если вас интересует только первоначальное развертывание с использованием одного файла шаблона ARM, вышеуказанное решение будет работать. Подобного решения может не быть, если вы хотите постепенно применять тот же шаблон. - person Heath; 31.03.2021
comment
Назначенные пользователем управляемые удостоверения решают проблему, поскольку их можно создать и предоставить доступ к хранилищу ключей до развертывания самого ресурса. К сожалению, еще не поддерживается для служебной шины, хотя решает ту же проблему, что и я, с управлением API. - person siegelc; 13.05.2021