Bicep 中的迭代循环

本文演示如何使用 for 语法来循环访问集合中的项。 此功能在 v0.3.1 及更高版本中可用。 可以使用循环来定义资源、模块、变量、属性或输出的多个副本。 使用循环可以避免在 Bicep 文件中重复语法,并可以动态设置要在部署期间创建的副本数。 若要完成快速入门,请参阅快速入门:创建多个实例

若要使用循环创建多个资源或模块,每个实例必须具有唯一的名称属性值。 可以使用数组或集合中的索引值或唯一值来创建名称。

循环语法

循环可以使用以下方式声明:

  • 使用整数索引。 当场景为“我想创建这么多实例”时,此选项适用。range 函数创建整数数组,该数组从起始索引开始并包含指定元素的数量。 在循环中,可以使用整数索引来修改值。 有关详细信息,请参阅整数索引

    [for <index> in range(<startIndex>, <numberOfElements>): {
      ...
    }]
    
  • 使用数组中的项。 当场景为“我想要为数组中的每个元素创建实例”时,此选项适用。在循环中,可以使用当前数组元素值修改值。 有关详细信息,请参阅数组元素

    [for <item> in <collection>: {
      ...
    }]
    
  • 使用字典对象中的项。 当场景为“我想要为对象中的每个项创建实例”时,此选项适用。项函数可将对象转换为数组。 在循环中,可以使用对象中的属性来创建值。 有关详细信息,请参阅 字典对象

    [for <item> in items(<object>): {
      ...
    }]
    
  • 使用整数索引和数组中的项。 当场景为“我想要为数组中每个元素创建实例,但还需要当前索引来创建另一个值”时,此选项适用。有关详细信息,请参阅循环数组和索引

    [for (<item>, <index>) in <collection>: {
      ...
    }]
    
  • 添加条件部署。 当场景为“我想要创建多个实例,但对于每个实例,我希望仅在条件为真时进行部署”时,此选项适用。有关详细信息,请参阅带条件的循环

    [for <item> in <collection>: if(<condition>) {
      ...
    }]
    

循环限制

在 Bicep 中使用循环有以下限制:

  • Bicep 循环仅适用于可在部署开始时确定的值。
  • 循环迭代不能为负数,也不能超过 800 次迭代。
  • 不能循环带有嵌套子资源的资源。 必须将子资源更改为顶级资源。 请参阅子资源的迭代
  • 若要在多个属性级别上循环,请使用 lambda map 函数

整数索引

有关使用索引的简单示例,请创建一个包含字符串数组的变量。

param itemCount int = 5

var stringArray = [for i in range(0, itemCount): 'item${(i + 1)}']

output arrayResult array = stringArray

输出返回具有以下值的数组:

[
  "item1",
  "item2",
  "item3",
  "item4",
  "item5"
]

下一个示例将创建 storageCount 参数中指定的存储帐户数。 它将返回每个存储帐户的三个属性。

param location string = resourceGroup().location
param storageCount int = 2

resource storageAcct 'Microsoft.Storage/storageAccounts@2023-05-01' = [for i in range(0, storageCount): {
  name: '${i}storage${uniqueString(resourceGroup().id)}'
  location: location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'Storage'
}]

output storageInfo array = [for i in range(0, storageCount): {
  id: storageAcct[i].id
  blobEndpoint: storageAcct[i].properties.primaryEndpoints.blob
  status: storageAcct[i].properties.statusOfPrimary
}]

请注意,创建存储帐户资源名称时将使用索引 i

下一个示例将多次部署一个模块。

param location string = resourceGroup().location
param storageCount int = 2

var baseName = 'store${uniqueString(resourceGroup().id)}'

module stgModule './storageAccount.bicep' = [for i in range(0, storageCount): {
  name: '${i}deploy${baseName}'
  params: {
    storageName: '${i}${baseName}'
    location: location
  }
}]

output storageAccountEndpoints array = [for i in range(0, storageCount): {
  endpoint: stgModule[i].outputs.storageEndpoint
}]

数组元素

以下示例为 storageNames 参数中提供的每个名称创建一个存储帐户。 请注意,每个资源实例的名称属性必须独一无二。

param location string = resourceGroup().location
param storageNames array = [
  'contoso'
  'fabrikam'
  'coho'
]

resource storageAcct 'Microsoft.Storage/storageAccounts@2023-05-01' = [for name in storageNames: {
  name: '${name}${uniqueString(resourceGroup().id)}'
  location: location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'Storage'
}]

下一个示例将循环访问数组以定义属性。 它将在虚拟网络内创建两个子网。 请注意,子网名称必须独一无二。

param rgLocation string = resourceGroup().location

var subnets = [
  {
    name: 'api'
    subnetPrefix: '10.144.0.0/24'
  }
  {
    name: 'worker'
    subnetPrefix: '10.144.1.0/24'
  }
]

resource vnet 'Microsoft.Network/virtualNetworks@2023-11-01' = {
  name: 'vnet'
  location: rgLocation
  properties: {
    addressSpace: {
      addressPrefixes: [
        '10.144.0.0/20'
      ]
    }
    subnets: [for subnet in subnets: {
      name: subnet.name
      properties: {
        addressPrefix: subnet.subnetPrefix
      }
    }]
  }
}

数组和索引

以下示例在定义存储帐户时使用数组元素和索引值。

param storageAccountNamePrefix string

var storageConfigurations = [
  {
    suffix: 'local'
    sku: 'Standard_LRS'
  }
  {
    suffix: 'geo'
    sku: 'Standard_GRS'
  }
]

resource storageAccountResources 'Microsoft.Storage/storageAccounts@2023-05-01' = [for (config, i) in storageConfigurations: {
  name: '${storageAccountNamePrefix}${config.suffix}${i}'
  location: resourceGroup().location
  sku: {
    name: config.sku
  }
  kind: 'StorageV2'
}]

下一个示例使用数组元素和索引来输出有关新资源的信息。

param location string = resourceGroup().location
param orgNames array = [
  'Contoso'
  'Fabrikam'
  'Coho'
]

resource nsg 'Microsoft.Network/networkSecurityGroups@2023-11-01' = [for name in orgNames: {
  name: 'nsg-${name}'
  location: location
}]

output deployedNSGs array = [for (name, i) in orgNames: {
  orgName: name
  nsgName: nsg[i].name
  resourceId: nsg[i].id
}]

字典对象

要在字典对象中循环访问元素,请使用 items 函数,该函数会将对象转换为数组。 请使用 value 属性获取对象的属性。 请注意,nsg 资源名称必须独一无二。

param nsgValues object = {
  nsg1: {
    name: 'nsg-chinanorth1'
    location: 'chinanorth'
  }
  nsg2: {
    name: 'nsg-east1'
    location: 'chinaeast'
  }
}

resource nsg 'Microsoft.Network/networkSecurityGroups@2023-11-01' = [for nsg in items(nsgValues): {
  name: nsg.value.name
  location: nsg.value.location
}]

带条件的循环

对于资源和模块,可以在循环语法中添加 if 表达式,以有条件地部署集合。

以下示例演示了一个结合使用条件语句的循环。 此示例将单个条件应用于模块的所有实例。

param location string = resourceGroup().location
param storageCount int = 2
param createNewStorage bool = true

var baseName = 'store${uniqueString(resourceGroup().id)}'

module stgModule './storageAccount.bicep' = [for i in range(0, storageCount): if(createNewStorage) {
  name: '${i}deploy${baseName}'
  params: {
    storageName: '${i}${baseName}'
    location: location
  }
}]

下一个示例演示如何应用特定于数组中当前元素的条件。

resource parentResources 'Microsoft.Example/examples@2024-06-06' = [for parent in parents: if(parent.enabled) {
  name: parent.name
  properties: {
    children: [for child in parent.children: {
      name: child.name
      setting: child.settingValue
    }]
  }
}]

批量部署

默认情况下,将并行部署 Azure 资源。 使用循环创建多个资源类型实例时,将同时部署这些实例。 不会保证它们的创建顺序。 除了 Bicep 文件中的资源总数为 800 这一限制外,并行部署的资源数量没有限制。

你可能不希望同时更新资源类型的所有实例。 例如,在更新生产环境时,可能需要错开更新,使任何一次仅更新一定数量。 可指定同时批处理和部署其中一部分实例。 其他实例等待该批处理完成。

若要串行部署资源的实例,请添加 batchSize 修饰器。 将其值设置为要并发部署的实例数。 在循环中创建先前实例的依赖关系,使其在上一个批处理完成后才启动批处理。

param location string = resourceGroup().location

@batchSize(2)
resource storageAcct 'Microsoft.Storage/storageAccounts@2023-05-01' = [for i in range(0, 4): {
  name: '${i}storage${uniqueString(resourceGroup().id)}'
  location: location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'Storage'
}]

对于顺序部署,请将批大小设置为 1。

batchSize 修饰器位于 sys 命名空间中。 如果你需要将此装饰器与同名的其他项区分开来,请在修饰器前面加上 sys:@sys.batchSize(2)

子资源的迭代

若要创建子资源的多个实例,以下两个 Bicep 文件都将正常工作。

嵌套子资源

param location string = resourceGroup().location

resource stg 'Microsoft.Storage/storageAccounts@2023-05-01' = {
  name: 'examplestorage'
  location: location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
  resource service 'fileServices' = {
    name: 'default'
    resource share 'shares' = [for i in range(0, 3): {
      name: 'exampleshare${i}'
    }]
  }
}

顶级子资源

resource stg 'Microsoft.Storage/storageAccounts@2023-05-01' = {
  name: 'examplestorage'
  location: resourceGroup().location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
}

resource service 'Microsoft.Storage/storageAccounts/fileServices@2023-05-01' = {
  name: 'default'
  parent: stg
}

resource share 'Microsoft.Storage/storageAccounts/fileServices/shares@2023-05-01' = [for i in range(0, 3): {
  name: 'exampleshare${i}'
  parent: service
}]

引用资源/模块集合

ARM 模板 references 函数会返回表示资源集合运行时状态的一组对象。 在 Bicep 中,不存在显式引用函数。 相反,符号集合用法是直接使用的,并且在代码生成期间,Bicep 会将其转换为利用 ARM 模板引用函数的 ARM 模板。 对于使用引用函数将符号集合转换为 ARM 模板的转换功能,必须具有 Bicep CLI 版本 0.20.X 或更高版本。 此外,在 bicepconfig.json 文件中,应显示 symbolicNameCodegen 设置并设置为 true

整数索引中两个示例的输出可以编写为:

param location string = resourceGroup().location
param storageCount int = 2

resource storageAcct 'Microsoft.Storage/storageAccounts@2023-05-01' = [for i in range(0, storageCount): {
  name: '${i}storage${uniqueString(resourceGroup().id)}'
  location: location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'Storage'
}]

output storageInfo array = map(storageAcct, store => {
  blobEndpoint: store.properties.primaryEndpoints
  status: store.properties.statusOfPrimary
})

output storageAccountEndpoints array = map(storageAcct, store => store.properties.primaryEndpoints)

此 Bicep 文件会转译到以下利用 references 函数的 ARM JSON 模板中:

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "languageVersion": "1.10-experimental",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "location": {
      "type": "string",
      "defaultValue": "[resourceGroup().location]"
    },
    "storageCount": {
      "type": "int",
      "defaultValue": 2
    }
  },
  "resources": {
    "storageAcct": {
      "copy": {
        "name": "storageAcct",
        "count": "[length(range(0, parameters('storageCount')))]"
      },
      "type": "Microsoft.Storage/storageAccounts",
      "apiVersion": "2023-04-01",
      "name": "[format('{0}storage{1}', range(0, parameters('storageCount'))[copyIndex()], uniqueString(resourceGroup().id))]",
      "location": "[parameters('location')]",
      "sku": {
        "name": "Standard_LRS"
      },
      "kind": "Storage"
    }
  },
  "outputs": {
    "storageInfo": {
      "type": "array",
      "value": "[map(references('storageAcct', 'full'), lambda('store', createObject('blobEndpoint', lambdaVariables('store').properties.primaryEndpoints, 'status', lambdaVariables('store').properties.statusOfPrimary)))]"
    },
    "storageAccountEndpoints": {
      "type": "array",
      "value": "[map(references('storageAcct', 'full'), lambda('store', lambdaVariables('store').properties.primaryEndpoints))]"
    }
  }
}

请注意,在前面的 ARM JSON 模板中,必须将 languageVersion 设置为 1.10-experimental,并且资源元素是对象而不是数组。

后续步骤

  • 若要了解如何创建 Bicep 文件,请参阅文件