Pętle iteracyjne w Bicep

W tym artykule pokazano, jak używać for składni do iterowania elementów 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. Aby zapoznać się z przewodnikiem Szybki start, zobacz Szybki start: tworzenie wielu wystąpień.

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

Zasoby szkoleniowe

Jeśli wolisz dowiedzieć się więcej na temat pętli za pomocą wskazówek krok po kroku, zobacz Tworzenie elastycznych szablonów Bicep przy użyciu warunków i pętli.

Składnia pętli

Pętle mogą być deklarowane przez:

  • Za pomocą indeksu liczb całkowitych. Ta opcja działa, gdy twój scenariusz to: "Chcę utworzyć to wiele wystąpień". Funkcja zakresu tworzy tablicę liczb całkowitych rozpoczynających się od indeksu początkowego i zawiera liczbę określonych 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 w twoim scenariuszu jest: "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żywanie indeksu całkowitego i elementów w tablicy. Ta opcja działa, gdy twój 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 w twoim scenariuszu jest: "Chcę utworzyć wiele wystąpień, ale dla każdego wystąpienia chcę wdrożyć tylko wtedy, gdy warunek ma wartość true". 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ą tylko z wartościami, które można określić na początku wdrożenia.
  • Iteracje pętli nie mogą być liczbą ujemną ani przekraczać 800 iteracji.
  • Nie można pętli zasobu z zagnieżdżonym zasobami podrzędnymi. Zmień zasoby podrzędne na zasoby najwyższego poziomu. Zobacz Iteracja zasobu podrzędnego.
  • Aby przeprowadzić pętlę na wielu poziomach właściwości, użyj funkcji mapy lambda.

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 zostanie utworzona liczba kont magazynu określonych w parametrze storageCount . Zwraca trzy właściwości dla każdego konta magazynu.

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

Zwróć uwagę, że indeks i jest używany podczas tworzenia nazwy zasobu konta magazynu.

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 magazynu dla każdej nazwy podanej w parametrze storageNames . Zanotuj właściwość name dla każdego wystąpienia zasobu musi być unikatowa.

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

W następnym przykładzie iteruje tablicę w celu zdefiniowania właściwości. Tworzy dwie podsieci w sieci wirtualnej. Należy pamiętać, ż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@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
      }
    }]
  }
}

Tablica i indeks

W poniższym przykładzie użyto zarówno elementu tablicy, jak i wartości indeksu podczas definiowania konta magazynu.

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

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

Obiekt słownika

Aby iterować elementy w obiekcie słownika, użyj funkcji items, która konwertuje obiekt na tablicę. value Użyj właściwości , aby uzyskać właściwości obiektów. 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@2020-06-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ą condition. 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@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
    }]
  }
}]

Wdrażanie w partiach

Domyślnie zasoby platformy Azure są wdrażane równolegle. 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 żadnego limitu liczby zasobów wdrożonych równolegle, poza całkowitym limitem 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że być konieczne rozłożenie aktualizacji tak, aby tylko określona liczba została zaktualizowana w dowolnym momencie. Można określić, że podzbiór wystąpień jest wsadowy i wdrażany w tym samym czasie. Inne wystąpienia czekają na ukończenie tej partii.

Aby szeregowo wdrożyć wystąpienia zasobu, dodaj dekorator batchSize. Ustaw jej wartość na liczbę wystąpień, które mają być wdrażane współbieżnie. Zależność jest tworzona na wcześniejszych wystąpieniach 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@2022-09-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, należy poprzeć dekorator za pomocą systemu: @sys.batchSize(2)

Iteracja zasobu podrzędnego

Nie można użyć pętli dla zagnieżdżonego zasobu podrzędnego. Aby utworzyć więcej niż jedno wystąpienie zasobu podrzędnego, zmień zasób podrzędny na zasób najwyższego poziomu.

Załóżmy na przykład, że zazwyczaj definiujesz usługę plików i udział plików jako zagnieżdżone zasoby dla konta magazynu.

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

Aby utworzyć więcej niż jeden udział plików, przenieś go poza konto magazynu. Relację z zasobem nadrzędnym definiuje się za pomocą parent właściwości .

W poniższym przykładzie pokazano, jak utworzyć konto magazynu, usługę plików i więcej niż jeden udział plików:

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

Odwołania do kolekcji zasobów/modułów

Funkcja szablonu references usługi ARM zwraca tablicę obiektów reprezentujących stany środowiska uruchomieniowego kolekcji zasobów. W Bicep nie ma jawnej funkcji odwołań. Zamiast tego użycie kolekcji symbolicznej jest stosowane bezpośrednio, a podczas generowania kodu Bicep tłumaczy go na szablon usługi ARM, który korzysta z funkcji odwołań do szablonu usługi ARM. Aby funkcja tłumaczenia przekształcała kolekcje symboliczne w szablony usługi ARM przy użyciu funkcji references, konieczne jest posiadanie interfejsu wiersza polecenia Bicep w wersji 0.20.X lub nowszej. Ponadto w bicepconfig.json pliku symbolicNameCodegen należy przedstawić ustawienie i ustawić wartość true.

Dane wyjściowe dwóch przykładów w indeksie liczba całkowita można zapisać jako:

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)

Ten plik Bicep jest transpilowany do następującego szablonu JSON usługi ARM, który korzysta z references funkcji:

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

Uwaga w poprzednim szablonie languageVersion JSON usługi ARM musi być ustawiona na 1.10-experimental, a element zasobu jest obiektem zamiast tablicy.

Następne kroki

  • Aby dowiedzieć się więcej o tworzeniu plików Bicep, zobacz plik.