Cicli iterativi in Bicep

Questo articolo illustra come usare la for sintassi per scorrere gli elementi in una raccolta. Questa funzionalità è supportata a partire dalla versione 0.3.1 successiva. È possibile usare cicli per definire più copie di una risorsa, un modulo, una variabile, una proprietà o un output. Usare i cicli per evitare la sintassi ripetuta nel file Bicep e impostare dinamicamente il numero di copie da creare durante la distribuzione. Per una guida introduttiva, vedere Avvio rapido: Creare più istanze.

Per usare i cicli per creare più risorse o moduli, ogni istanza deve avere un valore univoco per la proprietà name. È possibile usare il valore di indice o i valori univoci in matrici o raccolte per creare i nomi.

Risorse di formazione

Se si preferisce ottenere informazioni sui cicli di istruzioni dettagliate, vedere Creare modelli Bicep flessibili usando condizioni e cicli.

Sintassi del ciclo

I cicli possono essere dichiarati da:

  • Utilizzo di un indice integer. Questa opzione funziona quando lo scenario è: "Voglio creare queste numerose istanze". La funzione range crea una matrice di numeri interi che inizia all'indice iniziale e contiene il numero di elementi specificati. All'interno del ciclo è possibile usare l'indice integer per modificare i valori. Per altre informazioni, vedere Indice intero.

    [for <index> in range(<startIndex>, <numberOfElements>): {
      ...
    }]
    
  • Uso di elementi in una matrice. Questa opzione funziona quando lo scenario è: "Si vuole creare un'istanza per ogni elemento in una matrice". All'interno del ciclo è possibile usare il valore dell'elemento della matrice corrente per modificare i valori. Per altre informazioni, vedere Elementi della matrice.

    [for <item> in <collection>: {
      ...
    }]
    
  • Utilizzo di elementi in un oggetto dizionario. Questa opzione funziona quando lo scenario è: "Si vuole creare un'istanza per ogni elemento in un oggetto". La funzione items converte l'oggetto in una matrice. All'interno del ciclo è possibile usare le proprietà dell'oggetto per creare valori. Per altre informazioni, vedere Oggetto Dictionary.

    [for <item> in items(<object>): {
      ...
    }]
    
  • Uso dell'indice integer e degli elementi in una matrice. Questa opzione funziona quando lo scenario è: "Si vuole creare un'istanza per ogni elemento in una matrice, ma è necessario anche l'indice corrente per creare un altro valore". Per altre informazioni, vedere Matrice di cicli e indice.

    [for (<item>, <index>) in <collection>: {
      ...
    }]
    
  • Aggiunta di una distribuzione condizionale. Questa opzione funziona quando lo scenario è: "Si vogliono creare più istanze, ma per ogni istanza si vuole distribuire solo quando una condizione è true". Per altre informazioni, vedere Ciclo con condizione.

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

Limiti del ciclo

L'uso di cicli in Bicep presenta queste limitazioni:

  • I cicli Bicep funzionano solo con valori che possono essere determinati all'inizio della distribuzione.
  • Le iterazioni del ciclo non possono essere un numero negativo o superare 800 iterazioni.
  • Non è possibile eseguire il ciclo di una risorsa con risorse figlio annidate. Modificare le risorse figlio in risorse di primo livello. Vedere Iterazione per una risorsa figlio.
  • Per eseguire un ciclo su più livelli di proprietà, usare la funzione mappa lambda.

Indice intero

Per un semplice esempio di utilizzo di un indice, creare una variabile contenente una matrice di stringhe.

param itemCount int = 5

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

output arrayResult array = stringArray

L'output restituisce una matrice con i valori seguenti:

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

Nell'esempio seguente viene creato il numero di account di archiviazione specificati nel storageCount parametro . Restituisce tre proprietà per ogni account di archiviazione.

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

Si noti che l'indice i viene usato per creare il nome della risorsa dell'account di archiviazione.

Nell'esempio seguente viene distribuito più volte un modulo.

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

Elementi della matrice

Nell'esempio seguente viene creato un account di archiviazione per ogni nome specificato nel storageNames parametro . Si noti che la proprietà name per ogni istanza di risorsa deve essere univoca.

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

L'esempio successivo scorre una matrice per definire una proprietà. Crea due subnet all'interno di una rete virtuale. Si noti che i nomi delle subnet devono essere univoci.

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

Matrice e indice

Nell'esempio seguente vengono usati sia l'elemento della matrice che il valore di indice durante la definizione dell'account di archiviazione.

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

Nell'esempio seguente vengono usati sia gli elementi di una matrice che un indice per restituire informazioni sulle nuove risorse.

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

Oggetto Dictionary

Per scorrere gli elementi in un oggetto dizionario, usare la funzione items, che converte l'oggetto in una matrice. Utilizzare la value proprietà per ottenere proprietà sugli oggetti . Si noti che i nomi delle risorse nsg devono essere univoci.

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

Ciclo con condizione

Per le risorse e i moduli, è possibile aggiungere un'espressione if con la sintassi del ciclo per distribuire in modo condizionale la raccolta.

Nell'esempio seguente viene illustrato un ciclo combinato con un'istruzione condition. In questo esempio viene applicata una singola condizione a tutte le istanze del modulo.

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

Nell'esempio seguente viene illustrato come applicare una condizione specifica per l'elemento corrente nella matrice.

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

Distribuire in batch

Per impostazione predefinita, le risorse di Azure vengono distribuite in parallelo. Quando si usa un ciclo per creare più istanze di un tipo di risorsa, tali istanze vengono distribuite contemporaneamente. L'ordine di creazione non è garantito. Non esiste alcun limite al numero di risorse distribuite in parallelo, oltre al limite totale di 800 risorse nel file Bicep.

Potrebbe non essere necessario aggiornare tutte le istanze di un tipo di risorsa contemporaneamente. Ad esempio, quando si aggiorna un ambiente di produzione, è consigliabile sfalsare gli aggiornamenti per aggiornarne solo un determinato numero in un dato momento. È possibile specificare che un subset delle istanze deve essere raggruppato e distribuito contemporaneamente. Le altre istanze attendono il completamento del batch.

Per distribuire in modo seriale le istanze di una risorsa, aggiungere l'elemento decorator batchSize. Impostarne il valore sul numero di istanze da distribuire contemporaneamente. Una dipendenza viene creata nelle istanze precedenti del ciclo, quindi non avvia un batch fino al completamento del batch precedente.

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

Per la distribuzione sequenziale, impostare le dimensioni del batch su 1.

L'elemento batchSize decorator si trova nello spazio dei nomi sys. Se è necessario distinguere questo elemento decorator da un altro elemento con lo stesso nome, anteporre al decorator con sys: @sys.batchSize(2)

Iterazione di una risorsa figlio

Non è possibile usare un ciclo per una risorsa figlio annidata. Per creare più di un'istanza di una risorsa figlio, modificare la risorsa figlio in una risorsa di primo livello.

Si supponga, ad esempio, di definire in genere un servizio file e una condivisione file come risorse annidate per un account di archiviazione.

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

Per creare più condivisioni file, spostarlo all'esterno dell'account di archiviazione. Si definisce la relazione con la risorsa padre tramite la parent proprietà .

L'esempio seguente illustra come creare un account di archiviazione, un servizio file e più condivisioni file:

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

Raccolte di risorse/moduli di riferimento

La funzione modello references di Resource Manager restituisce una matrice di oggetti che rappresentano gli stati di runtime di una raccolta di risorse. In Bicep non esiste alcuna funzione di riferimenti espliciti. L'utilizzo della raccolta simbolica viene invece usato direttamente e, durante la generazione del codice, Bicep lo converte in un modello di Resource Manager che usa la funzione dei riferimenti al modello di Resource Manager. Per la funzionalità di conversione che trasforma le raccolte simboliche in modelli arm usando la funzione references, è necessario avere l'interfaccia della riga di comando di Bicep versione 0.20.X o successiva. Inoltre, nel bicepconfig.json file, l'impostazione symbolicNameCodegen deve essere presentata e impostata su true.

Gli output dei due esempi nell'indice Integer possono essere scritti come segue:

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)

Questo file Bicep viene traspilato nel modello JSON ARM seguente che usa la references funzione :

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

Nota nel modello JSON arm precedente, languageVersion deve essere impostato su 1.10-experimentale l'elemento della risorsa è un oggetto anziché una matrice.

Passaggi successivi

  • Per informazioni sulla creazione di file Bicep, vedere file.