Bicep 中的迭代循环

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

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

培训资源

有关循环的分步指南,请参阅 Microsoft Learn 中的使用条件和循环构建灵活 Bicep 文件模块

循环语法

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

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

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

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

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

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

    [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-westus1'
    ___location: 'westus'
  }
  nsg2: {
    name: 'nsg-east1'
    ___location: 'eastus'
  }
}

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

带条件的循环

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

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

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:

子资源的迭代

为了创建子资源的多个实例,以下两个 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
}]

引用资源/模块集合

Azure 资源管理器模板 (ARM 模板) references 函数返回表示资源集合运行时状态的对象数组。 由于 Bicep 中没有显式 references 函数,并且直接使用符号集合用法,Bicep 会将它转换为 ARM 模板,该模板在代码生成时利用 ARM 模板 references 函数。 对于使用该 references 函数将符号集合转换为 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 文件,请参阅 Bicep 文件结构和语法