Bicep 中的迭代迴圈

本文說明如何使用 for 語法來逐一查看集合中的項目。 從 v0.3.1 開始支援這項功能。 您可以使用迴圈來定義資源、模組、變數、屬性或輸出的多個複本。 使用迴圈來避免 Bicep 檔案中的重複語法,並動態設定部署期間所要建立的複本數目。 若要進行快速入門,請參閱快速入門:建立多個執行個體

若要使用迴圈來建立多個資源或模組,每個執行個體都必須具有唯一的名稱屬性值。 您可以使用陣列或集合中的索引值或唯一值來建立名稱。

訓練資源

如果您比較想要透過逐步指導來了解迴圈,請參閱使用條件和迴圈來建立有彈性的 Bicep 範本

迴圈語法

迴圈可透過下列方式宣告:

  • 使用整數索引。 當您的案例為「我想在許多執行個體建立這個」時,此選項有效。range 函式會建立一個從起始索引開始,並包含指定元素數量的整數陣列。 在迴圈內,您可以使用整數索引來修改值。 如需詳細資訊,請參閱整數索引

    [for <index> in range(<startIndex>, <numberOfElements>): {
      ...
    }]
    
  • 使用陣列中的項目。 當您的案例為「我想為陣列中的每個元素建立一個執行個體」時,此選項有效。在迴圈中,您可以使用目前陣列元素的值來修改值。 如需詳細資訊,請參閱陣列元素

    [for <item> in <collection>: {
      ...
    }]
    
  • 使用字典物件中的項目。 當您的案例為「我想為物件中的每個項目建立一個執行個體」時,此選項有效。items 函式可以將物件轉換成陣列。 在迴圈內,您可以使用物件的屬性來建立值。 如需詳細資訊,請參閱字典物件

    [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@2022-09-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@2022-09-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@2020-07-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@2022-09-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@2020-06-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@2020-06-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@2020-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@2022-09-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)

子系資源反覆項目

您無法針對巢狀的子資源使用迴圈。 若要建立單一子資源的多個執行個體,請將子資源變更為最上層的資源。

例如,假設您通常將檔案服務和檔案共用定義為儲存體帳戶的巢狀資源。

resource stg 'Microsoft.Storage/storageAccounts@2022-09-01' = {
  name: 'examplestorage'
  location: resourceGroup().location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
  resource service 'fileServices' = {
    name: 'default'
    resource share 'shares' = {
      name: 'exampleshare'
    }
  }
}

若要建立一個以上的檔案共用,請將其移出儲存體帳戶。 您可以透過 parent 屬性來定義與父資源的關聯性。

下列範例顯示如何建立儲存體帳戶、檔案服務,以及一個以上的檔案共用:

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

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

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

參考資源/模組集合

ARM 範本 references 函式會傳回表示資源集合的執行階段狀態的物件陣列。 在 Bicep 中,沒有明確的 references 函式。 相反地,會直接部署符號集合使用方式,而且在程式碼產生期間,Bicep 會將它轉譯為使用 ARM 範本 references 函式的 ARM 範本。 針對使用 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@2022-09-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": "2022-09-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 檔案,請參閱檔案