Поделиться через


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

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

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

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

См. пошаговое руководство по использованию условий и циклов в разделе Создание гибких файлов Bicep с использованием условий и циклов на Microsoft Learn.

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

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

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

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

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

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

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

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

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

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

  • Циклы Bicep работают только с теми значениями, которые можно установить в начале развертывания.
  • Количество итераций в цикле не может быть отрицательным или превышать 800 итераций.
  • Так как ресурс не может циклировать с вложенными дочерними ресурсами, измените дочерние ресурсы на ресурсы верхнего уровня. Дополнительные сведения см. в разделе "Итерация" для дочернего ресурса.
  • Чтобы выполнить цикл на нескольких уровнях свойств, используйте лямбда-функцию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. Обратите внимание, что свойство name для каждого экземпляра ресурса должно быть уникальным:

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

Объект Dictionary

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

Цикл с условием

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

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

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

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

Функция шаблона Azure Resource Manager (шаблон 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@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 преобразуется в следующий шаблон 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": "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))]"
    }
  }
}

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

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

Сведения о создании файлов Bicep см. в статье "Структура и синтаксис Bicep".