Udostępnij za pośrednictwem


Pętle iteracyjne w Bicep

W tym artykule pokazano, jak używać składni for do przechodzenia przez elementy w kolekcji. Ta funkcja jest obsługiwana od wersji 0.3.1 do nowszej. Pętle umożliwiają definiowanie wielu kopii zasobu, modułu, zmiennej, właściwości lub danych wyjściowych. Użyj pętli, aby uniknąć powtarzania składni w pliku Bicep i dynamicznie ustawiać liczbę kopii do utworzenia podczas wdrażania. Zobacz Szybki start: tworzenie wielu wystąpień zasobów w środowisku Bicep, aby dowiedzieć się, jak używać różnych for składni do tworzenia wielu wystąpień zasobów w Bicep.

Aby używać pętli do tworzenia wielu zasobów lub modułów, każde wystąpienie musi mieć unikatową wartość dla name właściwości. Aby utworzyć nazwy, możesz użyć wartości indeksu lub unikatowych wartości w tablicach lub kolekcjach.

Zasoby szkoleniowe

Aby uzyskać szczegółowe wskazówki dotyczące pętli, zobacz moduł Tworzenie elastycznych plików Bicep przy użyciu warunków i pętli w Microsoft Learn.

Składnia pętli

Deklarowanie pętli może odbywać się poprzez:

  • Za pomocą indeksu liczb całkowitych. Ta opcja działa, gdy twój scenariusz to: "Chcę utworzyć tyle wystąpień". Funkcja range tworzy tablicę liczb całkowitych, rozpoczynającą się od indeksu początkowego i zawierającą określoną liczbę elementów. W pętli można użyć indeksu liczby całkowitej, aby zmodyfikować wartości. Aby uzyskać więcej informacji, zobacz Indeks liczb całkowitych.

    [for <index> in range(<startIndex>, <numberOfElements>): {
      ...
    }]
    
  • Używanie elementów w tablicy: ta opcja działa, gdy scenariusz to "Chcę utworzyć wystąpienie dla każdego elementu w tablicy". W pętli można użyć wartości bieżącego elementu tablicy, aby zmodyfikować wartości. Aby uzyskać więcej informacji, zobacz Elementy tablicy.

    [for <item> in <collection>: {
      ...
    }]
    
  • Używanie elementów w obiekcie słownika: ta opcja działa, gdy scenariusz to "Chcę utworzyć wystąpienie dla każdego elementu w obiekcie". Funkcja items konwertuje obiekt na tablicę. W ramach pętli można użyć właściwości z obiektu do utworzenia wartości. Aby uzyskać więcej informacji, zobacz obiekt słownika.

    [for <item> in items(<object>): {
      ...
    }]
    
  • Użycie indeksu całkowitego i elementów w tablicy: ta opcja działa, gdy scenariusz to "Chcę utworzyć wystąpienie dla każdego elementu w tablicy, ale potrzebuję również bieżącego indeksu, aby utworzyć inną wartość". Aby uzyskać więcej informacji, zobacz Tablica pętli i indeks.

    [for (<item>, <index>) in <collection>: {
      ...
    }]
    
  • Dodawanie wdrożenia warunkowego: Ta opcja działa, gdy scenariusz to "Chcę utworzyć wiele wystąpień, ale chcę wdrożyć każde wystąpienie tylko wtedy, gdy warunek jest spełniony". Aby uzyskać więcej informacji, zobacz Pętla z warunkiem.

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

Limity pętli

Używanie pętli w Bicep ma następujące ograniczenia:

  • Pętle Bicep działają wyłącznie z wartościami, które da się określić na początku wdrożenia.
  • Iteracje pętli nie mogą być liczbą ujemną ani przekraczać 800 iteracji.
  • Ponieważ zasób nie może działać w pętli z zagnieżdżonymi zasobami podrzędnymi, zmień zasoby podrzędne na zasoby najwyższego poziomu. Aby uzyskać więcej informacji, zobacz Iteracja dla zasobu potomnego.
  • Aby przeprowadzić pętlę na wielu poziomach właściwości, użyj funkcji lambdamap.

Indeks liczby całkowitej

Aby uzyskać prosty przykład użycia indeksu, utwórz zmienną zawierającą tablicę ciągów:

param itemCount int = 5

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

output arrayResult array = stringArray

Dane wyjściowe zwracają tablicę z następującymi wartościami:

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

W następnym przykładzie pokazano utworzenie liczby kont magazynowych określonych w parametrze storageCount. Zwraca trzy właściwości dla każdego konta pamięci masowej:

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

Zwróć uwagę, że indeks i jest używany przy tworzeniu nazwy zasobu konta pamięci masowej.

W następnym przykładzie jest wdrażany moduł wiele razy:

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

Elementy tablicy

Poniższy przykład tworzy jedno konto magazynowe dla każdej nazwy podanej w parametrze storageNames. Pamiętaj, że atrybut name dla każdego wystąpienia zasobu musi być unikatowy.

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

W następnym przykładzie iteruje po tablicy, aby zdefiniować właściwość. Tworzy dwie podsieci w sieci wirtualnej. Pamiętaj, że nazwy podsieci muszą być unikatowe:

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

Tablica i indeks

W następującym przykładzie użyto zarówno elementu tablicy, jak i wartości indeksu podczas definiowania konta pamięci masowej.

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

W następnym przykładzie użyto zarówno elementów tablicy, jak i indeksu, aby uzyskać informacje o nowych zasobach:

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

Obiekt słownika

Aby iterować elementy w obiekcie słownika, użyj items funkcji, która konwertuje obiekt na tablicę. Użyj właściwości value, aby uzyskać właściwości na obiektach. Pamiętaj, że nazwy zasobów sieciowej grupy zabezpieczeń muszą być unikatowe.

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

Pętla z warunkiem

W przypadku zasobów i modułów można dodać if wyrażenie ze składnią pętli w celu warunkowego wdrożenia kolekcji.

W poniższym przykładzie przedstawiono pętlę połączoną z instrukcją warunkową. W tym przykładzie pojedynczy warunek jest stosowany do wszystkich wystąpień modułu:

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

W następnym przykładzie pokazano, jak zastosować warunek specyficzny dla bieżącego elementu w tablicy:

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

Wdrażanie w partiach

Zasoby platformy Azure są wdrażane równolegle domyślnie. Gdy używasz pętli do tworzenia wielu wystąpień typu zasobu, wszystkie te wystąpienia są wdrażane w tym samym czasie. Kolejność ich tworzenia nie jest gwarantowana. Nie ma limitu liczby zasobów wdrażanych równolegle, oprócz łącznego limitu 800 zasobów w pliku Bicep.

Możesz nie chcieć zaktualizować wszystkich wystąpień typu zasobu w tym samym czasie. Na przykład podczas aktualizowania środowiska produkcyjnego, możesz chcieć stopniowo aktualizować tylko określoną liczbę zasobów jednocześnie. Można określić podzbiór wystąpień, które mają być grupowane i wdrażane jednocześnie. Inne instancje czekają na ukończenie tej partii.

Aby szeregowo wdrożyć instancje zasobu, dodaj batchSize dekorator. Ustaw jej wartość na liczbę wystąpień, które mają być wdrażane współbieżnie. Zależność jest tworzona podczas wcześniejszych wystąpień w pętli, więc nie uruchamia jednej partii, dopóki poprzednia partia nie zostanie ukończona.

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

W przypadku wdrożenia sekwencyjnego ustaw rozmiar partii na 1.

Dekorator batchSize znajduje się w przestrzeni nazw systemu. Jeśli chcesz odróżnić ten dekorator od innego elementu o tej samej nazwie, poprzedź dekorator za pomocą sys: @sys.batchSize(2)

Iteracja zasobu podrzędnego

Aby utworzyć więcej niż jedno wystąpienie zasobu podrzędnego, oba następujące pliki Bicep obsługują to zadanie:

Zagnieżdżone zasoby podrzędne

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

Zasoby podrzędne najwyższego poziomu

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

Referencyjne kolekcje zasobów/modułów

Funkcja szablonu usługi Azure Resource Manager (szablon ARM) references zwraca tablicę obiektów reprezentujących stany wykonawcze zestawu zasobów. Ponieważ w Bicep nie istnieje jawna funkcja references i użycie kolekcji symbolicznej jest stosowane bezpośrednio, Bicep tłumaczy użycie symboliczne na szablon ARM, który korzysta z funkcji references szablonu ARM podczas generowania kodu. Aby funkcjonalność tłumaczenia mogła używać references do przekształcania kolekcji symbolicznych w szablony ARM, konieczne jest posiadanie wersji interfejsu wiersza polecenia Bicep 0.20.X lub nowszej. Ponadto w pliku bicepconfig.json należy przedstawić i zastosować ustawienie symbolicNameCodegen na wartość true.

Dane wyjściowe dwóch próbek w indeksu liczby całkowitej można zapisać je jako:

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)

Ten plik Bicep jest transpilowany do następującego szablonu JSON ARM, który używa funkcji 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))]"
    }
  }
}

Zwróć uwagę, że w poprzednim szablonie ARM JSON languageVersion musi być ustawione na 1.10-experimental, a element zasobu jest obiektem, a nie tablicą.

Następne kroki

Aby dowiedzieć się, jak tworzyć pliki Bicep, zobacz Bicep file structure and syntax (Struktura i składnia plików Bicep).