Compartir vía


Bucles iterativos en Bicep

En este artículo se muestra cómo utilizar la sintaxis for para recorrer en iteración los elementos de una colección. Esta funcionalidad se admite a partir de la versión 0.3.1. Puede usar bucles para definir varias copias de un recurso, un módulo, una variable, una propiedad o una salida. Use bucles para evitar la repetición de la sintaxis en el archivo de Bicep y para establecer de manera dinámica la cantidad de copias que se van a crear durante la implementación. Para ver un inicio rápido, consulte Inicio rápido: Creación de varias instancias.

Para usar bucles para crear varios recursos o módulos, cada instancia debe tener un valor único para la propiedad name. Puede usar el valor de índice o los valores únicos en matrices o colecciones para crear los nombres.

Recursos de aprendizaje

Si prefiere información sobre los bucles mediante instrucciones detalladas, consulte Creación de plantillas de Bicep flexibles mediante condiciones y bucles.

Sintaxis de bucle

Los bucles se pueden declarar como se indica a continuación:

  • Mediante el uso de un índice de enteros. Esta opción funciona cuando el escenario es: "Quiero crear estas instancias". La función range crea una matriz de enteros que comienza en el índice de inicio y contiene el número de elementos especificados. En el bucle, puede utilizar el índice entero para modificar los valores. Para más información, consulte la sección Índice de enteros.

    [for <index> in range(<startIndex>, <numberOfElements>): {
      ...
    }]
    
  • Mediante el uso de los elementos de una matriz. Esta opción funciona cuando el escenario es: "Quiero crear una instancia para cada elemento de una matriz". Dentro del bucle, puede usar el valor del elemento de la matriz actual para modificar los valores. Para más información, consulte la sección Elementos de matriz.

    [for <item> in <collection>: {
      ...
    }]
    
  • Mediante el uso de los elementos de un objeto de diccionario. Esta opción es útil cuando el escenario es: "Quiero crear una instancia para cada elemento de un objeto". La función items convierte el objeto en una matriz. En el bucle, puede usar las propiedades del objeto para crear los valores. Para más información, consulte la sección Objeto de diccionario.

    [for <item> in items(<object>): {
      ...
    }]
    
  • Mediante el uso de un índice de enteros y los elementos de una matriz. Esta opción es útil cuando el escenario es: "Quiero crear una instancia para cada elemento de una matriz, pero también necesito que el índice actual cree otro valor". Para más información, consulte Índice y matriz de bucles.

    [for (<item>, <index>) in <collection>: {
      ...
    }]
    
  • Mediante la incorporación de una implementación condicional. Esta opción es útil cuando el escenario es: "Quiero crear varias instancias pero, para cada instancia, solo quiero realizar la implementación cuando se cumpla una condición". Para más información, consulte Bucle con condición.

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

Límites del bucle

El uso de bucles en Bicep tiene las limitaciones siguientes:

  • Los bucles de Bicep solo funcionan con valores que se pueden determinar al principio de la implementación.
  • Las iteraciones de bucle no pueden ser un número negativo ni superar las 800 iteraciones.
  • No se puede realizar un bucle de un recurso con recursos secundarios anidados. Cambie los recursos secundarios a recursos de nivel superior. Consulte Iteración para un recurso secundario.
  • Para recorrer en bucle varios niveles de propiedades, use la función de asignación lambda.

Índice de enteros

Para un ejemplo sencillo del uso de un índice, cree una variable que contenga cualquier matriz de cadenas.

param itemCount int = 5

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

output arrayResult array = stringArray

La salida devuelve una matriz con los valores siguientes:

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

En el ejemplo siguiente, se crea el número de cuentas de almacenamiento especificado en el parámetro storageCount. Devuelve tres propiedades para cada cuenta de almacenamiento.

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 el índice i se usa para crear el nombre del recurso de la cuenta de almacenamiento.

En el ejemplo siguiente, un módulo se implementa varias veces.

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

En el ejemplo siguiente, se crea una cuenta de almacenamiento para cada nombre proporcionado en el parámetro storageNames. Tenga en cuenta que la propiedad name de cada instancia de recurso debe 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'
}]

En el ejemplo siguiente, se recorre en iteración una matriz a fin de definir una propiedad. Crea dos subredes dentro de una red virtual. Tenga en cuenta que los nombres de subred deben ser únicos.

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

En el ejemplo siguiente se usa el elemento de matriz y el valor de índice al definir la cuenta de almacenamiento.

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

En el ejemplo siguiente, se utilizan los elementos de una matriz y un índice para generar información sobre los recursos nuevos.

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 diccionario

Para recorrer en iteración los elementos de un objeto de diccionario, use la función items, que convierte el objeto en una matriz. Utilice la propiedad value para obtener propiedades de los objetos. Tenga en cuenta que los nombres de los recursos del grupo de seguridad de red deben ser únicos.

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

Bucle con condición

En el caso de los recursos y módulos, puede agregar una expresión if con la sintaxis de bucle para implementar condicionalmente la colección.

En el ejemplo siguiente, se muestra un bucle combinado con una instrucción de condición. En este ejemplo, se aplica una condición única a todas las instancias del 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
  }
}]

En el ejemplo siguiente, se muestra cómo aplicar una condición específica del elemento actual en la 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
    }]
  }
}]

Implementación por lotes

De manera predeterminada, los recursos de Azure se implementan en paralelo. Cuando se usa un bucle para crear varias instancias de un tipo de recurso, todas esas instancias se implementan al mismo tiempo. No se garantiza el orden en el que se crean. No hay ningún límite en el número de recursos implementados en paralelo, aparte del límite total de 800 recursos del archivo de Bicep.

Es posible que no quiera actualizar todas las instancias de un tipo de recurso al mismo tiempo. Por ejemplo, al actualizar un entorno de producción, puede que quiera escalonar las actualizaciones para que solo una cantidad determinada se actualice al mismo tiempo. Puede especificar que un subconjunto de las instancias se agrupe por lotes e implemente al mismo tiempo. Las demás instancias esperan a que se complete ese lote.

Para implementar instancias de un recurso en serie, agregue el decorador batchSize. Establezca su valor en el número de instancias que se van a implementar a la vez. Se crea una dependencia en las instancias anteriores del bucle, por lo que no se inicia ningún lote hasta que se completa el lote anterior.

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 una implementación secuencial, establezca el tamaño del lote en 1.

El decorador batchSize está en el espacio de nombres sys. Si necesita diferenciar este decorador de otro elemento con el mismo nombre, ponga sys delante: @sys.batchSize(2)

Iteración para un recurso secundario

Para crear más de una instancia de un recurso secundario, funcionarán los dos archivos Bicep siguientes.

Recursos secundarios anidados

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 secundarios de nivel 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
}]

Hacer referencia a colecciones de recursos o módulos

La función references de plantilla de ARM devuelve una matriz de objetos que representan los estados en tiempo de ejecución de una colección de recursos. En Bicep, no hay ninguna función de referencias explícita. En su lugar, se emplea directamente el uso de la colección simbólica y, durante la generación de código, Bicep lo traduce a una plantilla de ARM que usa la función de referencias de plantilla de ARM. Para la característica de traducción que transforma colecciones simbólicas en plantillas de ARM mediante la función de referencias, es necesario tener la versión 0.20.X o posterior de la CLI de Bicep. Además, en el archivo bicepconfig.json, la configuración de symbolicNameCodegen debe presentarse y establecerse en true.

Las salidas de los dos ejemplos en Índice enteros se pueden escribir 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 archivo de Bicep se transpila en la siguiente plantilla JSON de ARM que usa la función 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))]"
    }
  }
}

Tenga en cuenta que en la plantilla JSON de ARM anterior, languageVersion debe establecerse en 1.10-experimental y el elemento de recurso es un objeto en lugar de una matriz.

Pasos siguientes

  • Para obtener información sobre cómo crear archivos de Bicep, consulte archivo.