Iterative Schleifen in Bicep

In diesem Artikel erfahren Sie, wie Sie die for-Syntax verwenden, um Elemente in einer Sammlung zu durchlaufen. Diese Funktionalität wird ab v0.3.1 unterstützt. Sie können Schleifen verwenden, um mehrere Kopien einer Ressource, eines Moduls, einer Variablen, einer Eigenschaft oder einer Ausgabe zu definieren. Verwenden Sie Schleifen, um eine Wiederholungssyntax in Ihrer Bicep-Datei zu vermeiden und die Anzahl der während der Bereitstellung zu erstellenden Kopien dynamisch festzulegen. Eine Schnellstartanleitung finden Sie unter Schnellstart: Erstellen mehrerer Instanzen.

Um Schleifen zum Erstellen mehrerer Ressourcen oder Module zu verwenden, muss jede Instanz über einen eindeutigen Wert für die name-Eigenschaft verfügen. Sie können den Indexwert oder eindeutige Werte in Arrays oder Sammlungen verwenden, um die Namen zu erstellen.

Schulungsressourcen

Wenn Sie Schleifen lieber anhand einer Schritt-für-Schritt-Anleitung lernen möchten, lesen Sie Flexible Bizeps-Vorlagen mithilfe von Bedingungen und Schleifen erstellen.

Schleifensyntax

Schleifen können wie folgt deklariert werden:

  • Durch Verwendung eines ganzzahligen Index. Diese Option funktioniert im Szenario „Ich möchte diese Anzahl von Instanzen erstellen“. Die Bereichsfunktion erstellt ein Array mit ganzen Zahlen, das am Startindex beginnt und die Anzahl der angegebenen Elemente enthält. Innerhalb der Schleife können Sie den ganzzahligen Index verwenden, um Werte zu ändern. Weitere Informationen finden Sie unter Ganzzahliger Index.

    [for <index> in range(<startIndex>, <numberOfElements>): {
      ...
    }]
    
  • Verwenden von Elementen in einem Array. Diese Option funktioniert im Szenario „Ich möchte für jedes Element in einem Array eine Instanz erstellen“. Innerhalb der Schleife können Sie den Wert des aktuellen Arrayelements verwenden, um Werte zu ändern. Weitere Informationen finden Sie unter Arrayelemente.

    [for <item> in <collection>: {
      ...
    }]
    
  • Verwenden von Elementen als Wörterbuchobjekt. Diese Option funktioniert im Szenario „Ich möchte für jedes Element in einem Objekt eine Instanz erstellen“. Die items-Funktion konvertiert das Objekt in ein Array. Innerhalb der Schleife können Sie Eigenschaften aus dem Objekt verwenden, um Werte zu erstellen. Weitere Informationen finden Sie unter Wörterbuchobjekt.

    [for <item> in items(<object>): {
      ...
    }]
    
  • Verwenden von ganzzahligen Indizes und Elementen in einem Array. Diese Option funktioniert im Szenario „Ich möchte eine Instanz für jedes Element in einem Array erstellen, aber ich benötige auch den aktuellen Index, um einen anderen Wert zu erstellen.“ Weitere Informationen finden Sie unter Array und Index.

    [for (<item>, <index>) in <collection>: {
      ...
    }]
    
  • Hinzufügen einer bedingten Bereitstellung. Diese Option funktioniert im Szenario „Ich möchte mehrere Instanzen erstellen, aber ich möchte für die einzelnen Instanzen nur bereitstellen, wenn eine Bedingung erfüllt ist“. Weitere Informationen finden Sie unter Schleife mit Bedingung.

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

Schleifengrenzwerte

Die Verwendung von Schleifen in Bicep weist folgende Einschränkungen auf:

  • Bicep-Schleifen funktionieren nur mit Werten, die zu Beginn der Bereitstellung bestimmt werden können.
  • Es dürfen nicht weniger als 0 oder mehr als 800 Schleifeniterationen verwendet werden.
  • Eine Ressource mit geschachtelten untergeordneten Ressourcen kann in einer Schleife nicht verwendet werden. Ändern Sie die untergeordneten Ressourcen in Ressourcen der obersten Ebene. Weitere Informationen finden Sie unter Iteration für eine untergeordnete Ressource.
  • Wenn Sie eine Schleife auf mehreren Eigenschaftenebenen ausführen möchten, verwenden Sie die Lambdazuordnungsfunktion.

Ganzzahliger Index

Erstellen Sie als einfaches Beispiel für die Verwendung eines Index eine Variable, die ein Array von Zeichenfolgen enthält.

param itemCount int = 5

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

output arrayResult array = stringArray

Die Ausgabe gibt ein Array mit den folgenden Elementen zurück:

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

Im nächsten Beispiel wird die Anzahl von Speicherkonten erstellt, die im Parameter storageCount angegeben ist. Es werden drei Eigenschaften für jedes Speicherkonto zurückgegeben.

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

Beachten Sie, dass der Index i zum Erstellen des Ressourcennamens für das Speicherkonto verwendet wird.

Im nächsten Beispiel wird ein Modul mehrmals bereitgestellt.

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

Arrayelemente

Im folgenden Beispiel wird ein Speicherkonto für jeden im storageNames-Parameter angegebenen Namen erstellt. Beachten Sie, dass die name-Eigenschaft für jede Ressourceninstanz eindeutig sein muss.

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

Im nächsten Beispiel wird ein Array durchlaufen, um eine Eigenschaft zu definieren. Es werden zwei Subnetze innerhalb eines virtuellen Netzwerks erstellt. Beachten Sie, dass die Subnetznamen eindeutig sein müssen.

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

Array und Index

Im folgenden Beispiel werden beim Definieren des Speicherkontos sowohl das Arrayelement als auch der Indexwert verwendet.

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

Im nächsten Beispiel werden sowohl die Elemente eines Arrays als auch ein Index verwendet, um Informationen zu den neuen Ressourcen auszugeben.

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

Wörterbuchobjekt

Verwenden Sie zum Iterieren von Elementen in einem Wörterbuchobjekt die items-Funktion, die das Objekt in ein Array konvertiert. Verwenden Sie die value-Eigenschaft, um Eigenschaften für die Objekte abzurufen. Beachten Sie, dass die NSG-Ressourcennamen eindeutig sein müssen.

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

Schleife mit Bedingung

Für Ressourcen und Module können Sie einen if-Ausdruck mit der Schleifensyntax hinzufügen, um die Sammlung bedingt bereitzustellen.

Das folgende Beispiel zeigt eine Schleife in Kombination mit einer Bedingungsanweisung. In diesem Beispiel wird eine einzelne Bedingung auf alle Instanzen des Moduls angewendet.

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

Das nächste Beispiel zeigt, wie eine Bedingung angewendet wird, die für das aktuelle Element im Array spezifisch ist.

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

In Batches bereitstellen

Standardmäßig werden Azure-Ressourcen parallel bereitgestellt. Wenn Sie eine Schleife verwenden, um mehrere Instanzen eines Ressourcentyps zu erstellen, werden alle diese Instanzen gleichzeitig bereitgestellt. Die Reihenfolge, in der sie erstellt werden, ist nicht garantiert. Es gibt keine Beschränkung für die Anzahl der parallel bereitgestellten Ressourcen, mit Ausnahme der Begrenzung der Gesamtanzahl auf 800 Ressourcen in der Bicep-Datei.

Es wird empfohlen, nicht alle Instanzen eines Ressourcentyps gleichzeitig zu aktualisieren. Wenn Sie z.B. eine Produktionsumgebung aktualisieren, möchten Sie die Updates möglicherweise staffeln, sodass nur eine bestimmte Anzahl von Ressourcen gleichzeitig aktualisiert wird. Sie können angeben, dass eine Teilmenge der Instanzen gleichzeitig in einem Batch zusammengefasst und bereitgestellt wird. Die anderen Instanzen warten auf den Abschluss dieses Batches.

Fügen Sie zum seriellen Bereitstellen von Instanzen einer Ressource den BatchSize-Decorator hinzu. Legen Sie dessen Wert auf die Anzahl von Instanzen fest, die gleichzeitig bereitgestellt werden sollen. Eine Abhängigkeit von früheren Instanzen wird in der Schleife erstellt, sodass ein Batch erst dann gestartet wird, wenn der vorherige Batch abgeschlossen wurde.

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

Legen Sie für eine sequenzielle Bereitstellung die Batchgröße auf 1 fest.

Der batchSize-Decorator befindet sich im sys-Namespace. Wenn Sie diesen Decorator von einem anderen Element gleichen Namens unterscheiden müssen, stellen Sie dem Decorator sys voran: @sys.batchSize(2)

Iteration für eine untergeordnete Ressource

Für eine geschachtelte untergeordnete Ressource kann keine Schleife verwendet werden. Um mehr als eine Instanz einer untergeordneten Ressource zu erstellen, ändern Sie die untergeordnete Ressource in eine Ressource der obersten Ebene.

Beispiel: Angenommen, Sie definieren in der Regel einen Dateidienst und eine Dateifreigabe als geschachtelte Ressourcen für ein Speicherkonto.

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

Wenn Sie mehrere Dateifreigaben erstellen möchten, verschieben Sie sie außerhalb des Speicherkontos. Sie definieren die Beziehung zur übergeordneten Ressource mithilfe der parent-Eigenschaft.

Das folgende Beispiel zeigt, wie Sie ein Speicherkonto, einen Dateidienst und mehrere Dateifreigaben erstellen:

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

Referenzressource/Modulsammlungen

Die ARM-Vorlagenfunktion references gibt ein Array von Objekten zurück, das die Runtimezustände einer Ressourcensammlung darstellt. In Bicep gibt es keine explizite Funktion für Verweise. Stattdessen wird die symbolische Sammlung direkt verwendet, und während der Codegenerierung übersetzt Bicep sie in eine ARM-Vorlage, welche die Funktion für ARM-Vorlagenverweise verwendet. Für die Übersetzungsfunktion, die symbolische Sammlungen mit Hilfe der Funktion Referenzen in ARM-Vorlagen umwandelt, benötigen Sie die Bicep CLI-Version 0.20.X oder höher. Darüber hinaus sollte in der bicepconfig.json-Datei die symbolicNameCodegen-Einstellung angezeigt und auf true festgelegt sein.

Die Ausgaben der beiden Beispiele im Ganzzahlindex können wie folgt geschrieben werden:

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)

Diese Bicep-Datei wird in die folgende ARM-JSON-Vorlage transpiliert, welche die references -Funktion verwendet:

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

Beachten Sie, dass languageVersion in der vorstehenden ARM-JSON-Vorlage auf 1.10-experimental festgelegt sein muss, und das Ressourcenelement ein Objekt anstelle eines Arrays ist.

Nächste Schritte

  • Informationen zum Erstellen von Bicep-Dateien finden Sie unter Datei.