Partilhar via


Loops iterativos no Bicep

Este artigo mostra como usar a sintaxe for para iterar sobre itens numa coleção. Esta funcionalidade é suportada a partir da v0.3.1. Você pode usar loops para definir várias cópias de um recurso, módulo, variável, propriedade ou saída. Use loops para evitar a repetição da sintaxe no arquivo Bicep e para definir dinamicamente o número de cópias a serem criadas durante a implantação. Consulte Guia de início rápido: criar várias instâncias de recursos no Bicep para obter um início rápido de como usar sintaxes diferentes for para criar várias instâncias de recursos no Bicep.

Para usar loops para criar vários recursos ou módulos, cada instância deve ter um valor exclusivo para a name propriedade. Você pode usar o valor de índice ou valores exclusivos em matrizes ou coleções para criar os nomes.

Recursos de formação

Para obter orientação passo a passo sobre loops, consulte o módulo Criar arquivos Bicep flexíveis usando condições e loops no Microsoft Learn.

Sintaxe de ciclo

Os loops podem ser declarados por:

  • Usando um índice inteiro. Essa opção funciona quando o cenário é: "Quero criar tantas instâncias". A função range cria uma matriz de inteiros que começa no índice inicial e contém o número de elementos especificados. Dentro do loop, você pode usar o índice inteiro para modificar valores. Para obter mais informações, consulte Índice inteiro.

    [for <index> in range(<startIndex>, <numberOfElements>): {
      ...
    }]
    
  • Usando itens em uma matriz: essa opção funciona quando o cenário é "Quero criar uma instância para cada elemento em uma matriz". Dentro do loop, você pode usar o valor do elemento de matriz atual para modificar valores. Para obter mais informações, consulte Elementos de matriz.

    [for <item> in <collection>: {
      ...
    }]
    
  • Usando itens em um objeto de dicionário: essa opção funciona quando o cenário é "Quero criar uma instância para cada item em um objeto". A função items converte o objeto em uma matriz. Dentro do loop, você pode usar propriedades do objeto para criar valores. Para obter mais informações, consulte Objeto de dicionário.

    [for <item> in items(<object>): {
      ...
    }]
    
  • Usando índice inteiro e itens em uma matriz: essa opção funciona quando o cenário é: "Quero criar uma instância para cada elemento em uma matriz, mas também preciso do índice atual para criar outro valor". Para obter mais informações, consulte Matriz de loop e índice.

    [for (<item>, <index>) in <collection>: {
      ...
    }]
    
  • Adicionando uma implantação condicional: essa opção funciona quando o cenário é "Quero criar várias instâncias, mas só quero implantar cada instância quando uma condição for verdadeira". Para obter mais informações, consulte Loop with condition.

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

Limites de ciclo

O uso de loops no Bicep tem estas limitações:

  • Os laços de bíceps só funcionam com valores que podem ser definidos no início da implantação.
  • As iterações de loop não podem ser um número negativo ou exceder 800 iterações.
  • Como um recurso não pode fazer loop com recursos filho aninhados, altere os recursos filho para recursos de nível superior. Para obter mais informações, consulte Iteração para um recurso filho.
  • Para fazer loop em vários níveis de propriedade, use a função lambdamap.

Índice inteiro

Para obter um exemplo simples de uso de um índice, crie uma variável que contenha uma matriz de cadeias de caracteres:

param itemCount int = 5

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

output arrayResult array = stringArray

A saída retorna uma matriz com os seguintes valores:

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

O próximo exemplo cria o número de contas de armazenamento especificadas no storageCount parâmetro. Ele retorna três propriedades para cada conta de armazenamento:

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

Observe que o índice i é usado na criação do nome do recurso da conta de armazenamento.

O próximo exemplo implanta um módulo várias vezes:

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

Elementos de matriz

O exemplo a seguir cria uma conta de armazenamento para cada nome fornecido no storageNames parâmetro. Observe que a propriedade nome para cada instância de recurso deve ser única.

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

O próximo exemplo itera sobre uma matriz para definir uma propriedade. Ele cria duas sub-redes dentro de uma rede virtual. Observe que os nomes das sub-redes devem ser exclusivos:

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

Matriz e índice

O exemplo a seguir usa o elemento array e o valor de índice ao definir a conta de armazenamento:

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

O próximo exemplo usa os elementos de uma matriz e um índice para gerar informações sobre os novos recursos:

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

Objeto de dicionário

Para iterar sobre elementos em um objeto de dicionário, use a items função, que converte o objeto em uma matriz. Use a propriedade value para obter propriedades dos objetos. Observe que os nomes de recursos nsg devem ser exclusivos.

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

Loop com condição

Para recursos e módulos, você pode adicionar uma if expressão com a sintaxe de loop para implantar condicionalmente a coleção.

O exemplo a seguir mostra um loop combinado com uma instrução condicional. Neste exemplo, uma única condição é aplicada a todas as instâncias do módulo:

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

O próximo exemplo mostra como aplicar uma condição específica ao elemento atual na matriz:

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

Implantar em lotes

Os recursos do Azure são implantados em paralelo por padrão. Quando você usa um loop para criar várias instâncias de um tipo de recurso, essas instâncias são todas implantadas ao mesmo tempo. A ordem em que são criados não é garantida. Não há um limite para o número de recursos implantados em paralelo além do limite total de 800 recursos no arquivo Bicep.

Talvez você não queira atualizar todas as instâncias de um tipo de recurso ao mesmo tempo. Por exemplo, ao atualizar um ambiente de produção, convém escalonar as atualizações para apenas um determinado número a ser atualizado a qualquer momento. Você pode especificar para um subconjunto das instâncias a serem agrupadas em lote e implantadas ao mesmo tempo. As outras instâncias aguardam a conclusão desse lote.

Para implantar instâncias em série de um recurso, adicione o batchSize decorador. Defina seu valor para o número de instâncias a serem implantadas simultaneamente. Uma dependência é criada durante instâncias anteriores no loop, portanto, ela não inicia um lote até que o lote anterior seja concluído.

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

Para implantação sequencial, defina o tamanho do lote como 1.

O batchSize decorador está no espaço de nomes sys. Se você precisa diferenciar este decorador de outro item com o mesmo nome, prefacie o decorador com sys: @sys.batchSize(2)

Iteração para um recurso filho

Para criar mais de uma instância de um recurso filho, ambos os seguintes arquivos Bicep oferecem suporte a essa tarefa:

Recursos filho aninhados

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

Recursos filho de nível superior

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

Coleções de recursos/módulos de referência

A função de modelo do Azure Resource Manager (modelo ARM) references retorna uma matriz de objetos que representam os estados de tempo de execução de uma coleção de recursos. Como não há uma função explícita references no Bicep e o uso da coleção simbólica é empregado diretamente, o Bicep traduz para um modelo de ARM que utiliza a função references do modelo ARM durante a geração de código. Para o recurso de tradução que usa a função references para transformar coleções simbólicas em modelos ARM, é necessário ter a versão 0.20.X ou superior da CLI do Bicep. Além disso, no arquivo bicepconfig.json , a symbolicNameCodegen configuração deve ser apresentada e definida como true.

As saídas dos dois exemplos no índice inteiro podem ser escritas como:

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)

Este arquivo Bicep é transpilado no seguinte modelo ARM JSON que utiliza a função 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))]"
    }
  }
}

Observação no modelo JSON ARM anterior, languageVersion deve ser definido como 1.10-experimental, e o elemento resource é um objeto em vez de uma matriz.

Próximos passos

Para saber como criar arquivos Bicep, consulte Sintaxe e estrutura de arquivos Bicep.