Итеративные циклы в Bicep

В этой статье приводятся сведения об использовании синтаксиса for для итерации по элементам в коллекции. Эта функция поддерживается, начиная с версии 0.3.1. С помощью циклов можно определить несколько копий ресурса, модуля, переменной, свойства или выходных данных. Циклы позволяют избежать повторения синтаксиса в файле Bicep и динамически настроить количество копий, создаваемых во время развертывания. Краткое руководство см. в статье Создание нескольких экземпляров.

Чтобы использовать циклы для создания нескольких ресурсов или модулей, каждый экземпляр должен иметь уникальное значение для свойства name. Для создания имен можно использовать значение индекса или уникальные значения в массивах или коллекциях.

Обучающие материалы

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

Синтаксис циклов

Ниже приводятся способы объявления циклов.

  • Использование целочисленного индекса. Этот вариант работает в случае, если нужно создать определенное количество экземпляров. Функция range создает массив целых чисел, который начинается с начального индекса и содержит указанное количество элементов. В цикле целочисленный индекс можно использовать для изменения значений. Дополнительные сведения см. в разделе Целочисленный индекс.

    [for <index> in range(<startIndex>, <numberOfElements>): {
      ...
    }]
    
  • Использование элементов в массиве. Этот вариант работает в случае, если нужно создать экземпляр для каждого элемента в массиве. В цикле значение текущего элемента массива можно использовать для изменения значений. Дополнительные сведения см. в разделе Элементы массива.

    [for <item> in <collection>: {
      ...
    }]
    
  • Использование элементов в объекте Dictionary. Этот вариант работает в случае, если нужно создать экземпляр для каждого элемента в объекте. Функция items преобразует объект в массив. В цикле свойства объекта можно использовать для создания значений. Дополнительные сведения см. в разделе Объект Dictionary.

    [for <item> in items(<object>): {
      ...
    }]
    
  • Использование целочисленного индекса и элементов в массиве. Этот вариант работает в случае, если нужно создать экземпляр для каждого элемента в массиве, но при этом требуется текущий индекс для создания еще одного значения. Дополнительные сведения см. в разделе Массив и индекс цикла.

    [for (<item>, <index>) in <collection>: {
      ...
    }]
    
  • Добавление условного развертывания. Этот вариант работает в случае, если нужно создать несколько экземпляров, но для каждого из них требуется выполнять развертывание, только если условие истинно. Дополнительные сведения см. в разделе Цикл с условием.

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

Ограничения цикла

При использовании циклов в Bicep действуют следующие ограничения:

  • Циклы Bicep работают только со значениями, которые можно определить в начале развертывания.
  • Цикл не может содержать отрицательное число итераций или больше 800 итераций.
  • Нельзя использовать цикл для вложенного дочернего ресурса. Дочерние ресурсы следует изменить на ресурсы верхнего уровня. См. раздел Итерация дочерних ресурсов.
  • Чтобы выполнить цикл на нескольких уровнях свойств, используйте функцию лямбда-карты.

Целочисленный индекс

Для простого примера использования индекса создайте переменную, которая содержит массив строк.

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. Обратите внимание, что свойство name для каждого экземпляра ресурса должно быть уникальным.

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
}]

Объект Dictionary

Для итерации по элементами в объекте Dictionary используйте функцию 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 развертываются параллельно. При использовании цикла для создания нескольких экземпляров типа ресурса, развертывание всех этих экземпляров происходит одновременно. Порядок, в котором они создаются, не гарантируется. Количество ресурсов, развертываемых параллельно, не ограничено, за исключением общего ограничения в 800 ресурсов в файле Bicep.

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

Чтобы последовательно развернуть экземпляры ресурса, добавьте декоратор 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
}]

Справочные коллекции ресурсов и модулей

Функция шаблона references ARM возвращает массив объектов, представляющих состояния среды выполнения коллекции ресурсов. В Bicep нет явной функции ссылок. Вместо этого использование символьной коллекции используется напрямую, а во время создания кода Bicep преобразует его в шаблон ARM, который использует функцию ссылок на шаблон ARM. Для функции перевода, которая преобразует символьные коллекции в шаблоны ARM с помощью функции ссылок, необходимо иметь Bicep CLI версии 0.20.X или более поздней. Кроме того, symbolicNameCodegen в bicepconfig.json файле параметр должен быть представлен и задан в 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-файл преобразуется в следующий шаблон JSON ARM, который использует функцию references :

{
  "$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))]"
    }
  }
}

Обратите внимание, что в предыдущем шаблоне languageVersion JSON ARM необходимо задать 1.10-experimentalзначение, а элемент ресурса — это объект вместо массива.

Следующие шаги

  • Дополнительные сведения о создании Bicep-файлов см . в файле.