2

我有以下二头肌脚本来执行以下步骤:

  • 基于代码创建存储帐户
  • 创建密钥
  • 使用正确的密钥加密存储帐户
  • 将秘密连接字符串(存储帐户)生成到密钥保管库中

基础设施分为 2 个资源组:

rg-shared => where the 2 key vault are (1 keyvault for key and GUID, and 1 keyvault for secrets (connection string)

rg-storage-account => where all the storage account get created 

在 azure bicep 中,我有以下脚本:

存储.二头肌

param ManagedIdentityid string
param uri string 
param kvname string
param keyvaultrg string = 'XXXX' //<== SHARED Resource Group
var keyVaultKeyPrefix = 'Key-Data-'
var storagePrefix = 'sthritesteur'
param tenantCodes array = [
  'aabb'
  'bbcc'
  'ccdd'
]


// Create storage accounts
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-04-01' = [for tenantCode in tenantCodes: {
  name: '${storagePrefix}${tenantCode}'
  location: resourceGroup().location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_RAGRS'
  }
  // Assign the identity
  identity: {
    type: 'UserAssigned'
    userAssignedIdentities: {
        '${ManagedIdentityid}':{}
    }
  }
  properties: {
    allowCrossTenantReplication: true
    minimumTlsVersion: 'TLS1_2'
    allowBlobPublicAccess: false
    allowSharedKeyAccess: true
    networkAcls: {
      bypass: 'AzureServices'
      virtualNetworkRules: []
      ipRules: []
      defaultAction: 'Allow'
    }
    supportsHttpsTrafficOnly: true
    encryption: {
      identity: {
        // specify which identity to use
        userAssignedIdentity: ManagedIdentityid
      }
      keySource: 'Microsoft.Keyvault'
      keyvaultproperties: {
        keyname: '${kvname}-${keyVaultKeyPrefix}${toUpper(tenantCode)}'
        keyvaulturi:uri
      }
      services: {
        file: {
          keyType: 'Account'
          enabled: true
        }
        blob: {
          keyType: 'Account'
          enabled: true
        }
      }
    }
    accessTier: 'Hot'
  }

}]



resource storage_Accounts_name_default 'Microsoft.Storage/storageAccounts/blobServices@2021-04-01' = [ for (storageName, i) in tenantCodes :{
  parent: storageAccount[i]
  name: 'default'
  properties: {
    changeFeed: {
      enabled: false
    }
    restorePolicy: {
      enabled: false
    }
    containerDeleteRetentionPolicy: {
      enabled: true
      days: 7
    }
    cors: {
      corsRules: []
    }
    deleteRetentionPolicy: {
      enabled: true
      days: 30
    }
    isVersioningEnabled: true
  }
}]

module connectionString 'shared.bicep' = [for (storageName, i) in tenantCodes :{
  scope: resourceGroup(keyvaultrg)
  name: storageName
  params: {
    storageAccountString: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount[i].name};AccountKey=${listKeys(storageAccount[i].id, storageAccount[i].apiVersion).keys[0].value};EndpointSuffix=${environment().suffixes.storage}'
  }
}]

keyvaultclient.bicep

param deploymentIdOne string = newGuid()
param deploymentIdTwo string = newGuid()
output deploymentIdOne string = '${deploymentIdOne}-${deploymentIdTwo}'
output deploymentIdTwo string = deploymentIdTwo

param storagerg string = 'XXXX' //<=== Storage Accounts Resource Groups
param sharedManagedIdentity string = 'mgn-identity-shared'
param keyvaultmain string = 'XXXX' //<=== KeyVault Name where to create GUID AND Keys
param tenantCodes array = [
  'aabb'
  'bbcc'
  'ccdd'
]
var clientDataKeyPrefix = 'Key-Data-'

resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
  name: sharedManagedIdentity
  location: resourceGroup().location
}

resource keyVaultClients 'Microsoft.KeyVault/vaults@2021-06-01-preview' existing = {
  name: keyvaultmain
}
resource kvClientsKey 'Microsoft.KeyVault/vaults/keys@2021-06-01-preview' = [for code in tenantCodes: {
  parent:keyVaultClients
  name: '${keyVaultClients.name}-${clientDataKeyPrefix}${toUpper(code)}'
  properties: {
    keySize: 2048
    kty: 'RSA'
    // Assign the least permission
    keyOps: [
      'unwrapKey'
      'wrapKey'
    ]
  }
}]
resource accessPolicy 'Microsoft.KeyVault/vaults/accessPolicies@2021-06-01-preview' = {
  parent:keyVaultClients
  name: 'add'
  properties: {
    accessPolicies: [
      {
        tenantId: subscription().tenantId
        objectId: managedIdentity.properties.principalId
        permissions: {
          // minimum required permission
          keys: [
            'get'
            'unwrapKey'
            'wrapKey'
          ]
        } 
      }
    ]
  }
}
resource clientLearnersGuid 'Microsoft.KeyVault/vaults/secrets@2021-06-01-preview' = [for tenant in tenantCodes: {
  parent:keyVaultClients
  name: '${keyVaultClients.name}${tenant}'
  properties: {
    contentType: 'GUID Key'
    value: '${deploymentIdOne}-${deploymentIdTwo}'
  }
  dependsOn:kvClientsKey
}]
module StorageAccount 'storage.bicep' = [for (storageName, i) in tenantCodes :{
  scope: resourceGroup(storagerg)
  name: storageName
  params: {
    ManagedIdentityid:managedIdentity.id
    kvname:keyVaultClients.name
    uri:keyVaultClients.properties.vaultUri
  }
  dependsOn:clientLearnersGuid
}]


共享的二头肌

param keyvaultshared string = 'XXXX' //<=== Key Vault Where to Store the Storage Connection String Secret
param storageAccountString string
param tenantCodes array = [
  'aabb'
  'bbcc'
  'ccdd'
]
resource keyVaultShared 'Microsoft.KeyVault/vaults@2021-06-01-preview' existing = {
  name: keyvaultshared
}
resource storageAccountConnectionString 'Microsoft.KeyVault/vaults/secrets@2021-06-01-preview' = [for tenant in tenantCodes: {
  parent:keyVaultShared
  name: '${keyVaultShared.name}-test${tenant}'
  properties:{
    contentType: '${tenant} Storage Account Connection String'
    value: storageAccountString
  }
}]

这些脚本基于tenantCode. 这是完美的。如果我只有 1 个tenantCode声明,一切都会顺利和完美,但是当我尝试声明超过 1 个时,我面临的问题就会出现。这就是细节问题。

当我声明超过 1 个代码时,脚本仍然创建了我需要的所有资源:存储帐户、密钥、加密、GUID 和 ConnectionStrings Secrets。但无论如何它在ConnectionStrings Secret.

它失败的原因是在这段代码的那些文件中:

共享二头肌

resource storageAccountConnectionString 'Microsoft.KeyVault/vaults/secrets@2021-06-01-preview' = [for tenant in tenantCodes: {
  parent:keyVaultShared
  name: '${keyVaultShared.name}-test${tenant}'
  properties:{
    contentType: '${tenant} Storage Account Connection String'
    value: storageAccountString
  }
}]

keuvaultsclient.bicep *

module StorageAccount 'storage.bicep' = [for (storageName, i) in tenantCodes :{
  scope: resourceGroup(storagerg)
  name: storageName
  params: {
    ManagedIdentityid:managedIdentity.id
    kvname:keyVaultClients.name
    uri:keyVaultClients.properties.vaultUri
  }
  dependsOn:clientLearnersGuid
}]

我有一个多重循环,我意识到在秘密下的共享密钥库中,我有正确数量的秘密(有 3 个租户代码,我有 3 个秘密),并且在每个秘密下我有 3 个版本(对于它生成的每个租户代码每个秘密都有一个新版本)。此循环错误导致二头肌脚本fail出现以下消息:

{"status":"Failed","error":{"code":"DeploymentFailed","message":"At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/DeployOperations for usage details.","details":[{"code":"Conflict","message":"{\r\n  \"status\": \"Failed\",\r\n  \"error\": {\r\n    \"code\": \"ResourceDeploymentFailure\",\r\n    \"message\": \"The resource operation completed with terminal provisioning state 'Failed'.\",\r\n    \"details\": [\r\n      {\r\n        \"code\": \"DeploymentFailed\",\r\n        \"message\": \"At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/DeployOperations for usage details.\",\r\n        \"details\": [\r\n          {\r\n            \"code\": \"Conflict\",\r\n            \"message\": \"{\\r\\n  \\\"error\\\": {\\r\\n    \\\"code\\\": \\\"StorageAccountOperationInProgress\\\",\\r\\n    \\\"message\\\": \\\"An operation is currently performing on this storage account that requires exclusive access.\\\"\\r\\n  }\\r\\n}\"\r\n          },\r\n          {\r\n            \"code\": \"Conflict\",\r\n            \"message\": \"{\\r\\n  \\\"error\\\": {\\r\\n    \\\"code\\\": \\\"StorageAccountOperationInProgress\\\",\\r\\n    \\\"message\\\": \\\"An operation is currently performing on this storage account that requires exclusive access.\\\"\\r\\n  }\\r\\n}\"\r\n          }\r\n        ]\r\n      }\r\n    ]\r\n  }\r\n}"}]}}

在这个阶段我完全被困住了,因为我是二头肌初学者,我用尽了所有选项并进行了测试以尝试解决这个问题。

如何重现

  • 创建 2 个资源组(一个用于密钥保管库,一个用于存储帐户)
  • 使用正确的 kv 名称更新参数
  • 在所有文件中,在每个文件上添加至少 2 个tenantCode
  • 执行命令az deployment group create -f ./keyvaultsclient.bicep -g <rg-where-keyvaults-are>

我希望我足够清楚地解释了我面临的问题,如果您需要更多详细信息,请告诉我。

非常感谢您的时间和帮助

4

1 回答 1

2

我通过进行一些更改测试了相同的代码,请尝试在.bicep文件中进行相同的更改:

keyvaultclient.bicep:

删除了模块循环,因为它为同一事物创建了 2 个模块。

module StorageAccount './storage.bicep' = {
  scope: resourceGroup(storagerg)
  name: 'NestedStorage'
  params: {
    ManagedIdentityid:managedIdentity.id
    kvname:keyVaultClients.name
    uri:keyVaultClients.properties.vaultUri
  }
  dependsOn:clientLearnersGuid
}

storage.bicep:

删除了模块的循环并添加了仅存储连接字符串的循环,它将将输出存储在数组中并将其传递给下一个模块。

module connectionString './shared.bicep'={
  scope: resourceGroup(keyvaultrg)
  name: 'KeyvaultNested'
  params: {
    storageAccountString: [for (tenant,i) in tenantCodes :{
    id:'DefaultEndpointsProtocol=https;AccountName=${storageAccount[i].name};AccountKey=${listKeys(storageAccount[i].id, storageAccount[i].apiVersion).keys[0].value};EndpointSuffix=${environment().suffixes.storage}'
  }]
}
}

shared.bicep:

将参数类型StorageAccountString从字符串更改为数组并添加[for (tenant,i) in tenantCodes到秘密部分,以便我可以将值作为storageAccountString[i].id .

param storageAccountString array

resource storageAccountConnectionString 'Microsoft.KeyVault/vaults/secrets@2021-06-01-preview' = [for (tenant,i) in tenantCodes: {
  parent:keyVaultShared
  name: '${keyVaultShared.name}-test${tenant}'
  properties:{
    contentType: '${tenant} Storage Account Connection String'
    value: storageAccountString[i].id
  }
}]

输出

在此处输入图像描述

在此处输入图像描述

在此处输入图像描述

在此处输入图像描述

于 2021-10-27T11:25:28.030 回答