Bicep의 반복 루프

이 문서에서는 for 구문을 사용하여 컬렉션의 항목을 반복하는 방법을 보여 줍니다. 이 기능은 v0.3.1부터 지원됩니다. 루프를 사용하여 리소스, 모듈, 변수, 속성 또는 출력의 여러 복사본을 정의할 수 있습니다. 루프를 사용하여 Bicep 파일에서 반복되는 구문을 방지하고 배포 중에 만들 복사본 수를 동적으로 설정합니다. 빠른 시작을 진행하려면 빠른 시작: 여러 인스턴스 만들기를 참조하세요.

루프를 사용하여 여러 리소스 또는 모듈을 만들려면 각 인스턴스는 이름 속성에 대한 고유한 값이 있어야 합니다. 배열 또는 컬렉션에서 인덱스 값 또는 고유 값을 사용하여 이름을 만들 수 있습니다.

학습 리소스

단계별 지침을 통해 루프에 대해 알아보려면 조건 및 루프를 사용하여 유연한 Bicep 템플릿 빌드를 참조하세요.

루프 구문

루프는 다음과 같이 선언할 수 있습니다.

  • 정수 인덱스 사용. 이 옵션은 시나리오가 "이만큼 많은 인스턴스를 만들려고 합니다."일 때 작동합니다. range 함수는 시작 인덱스에서 시작하고 지정된 요소 수를 포함하는 정수 배열을 만듭니다. 루프 내에서 정수 인덱스를 사용하여 값을 수정할 수 있습니다. 자세한 내용은 정수 인덱스를 참조하세요.

    [for <index> in range(<startIndex>, <numberOfElements>): {
      ...
    }]
    
  • 배열의 항목 사용. 이 옵션은 시나리오가 "배열의 각 요소에 대한 인스턴스를 만들려고 합니다."일 때 작동합니다. 루프 내에서 현재 배열 요소의 값을 사용하여 값을 수정할 수 있습니다. 자세한 내용은 배열 요소를 참조하세요.

    [for <item> in <collection>: {
      ...
    }]
    
  • 사전 개체의 항목 사용. 이 옵션은 시나리오가 "개체의 각 항목에 대한 인스턴스를 만들려고 합니다."일 때 작동합니다. items 함수는 개체를 배열로 변환합니다. 루프 내에서 개체의 속성을 사용하여 값을 만들 수 있습니다. 자세한 내용은 사전 개체를 참조하세요.

    [for <item> in items(<object>): {
      ...
    }]
    
  • 배열의 정수 인덱스 및 항목 사용. 이 옵션은 시나리오가 "배열의 각 요소에 대한 인스턴스를 만들려고 하지만 다른 값을 만들려면 현재 인덱스도 필요합니다."일 때 작동합니다. 자세한 내용은 루프 배열 및 인덱스을 참조하세요.

    [for (<item>, <index>) in <collection>: {
      ...
    }]
    
  • 조건부 배포 추가. 이 옵션은 시나리오가 "여러 인스턴스를 만들려고 하지만 각 인스턴스에 대해 조건이 true인 경우에만 배포하려고 합니다."인 경우 작동합니다. 자세한 내용은 조건이 있는 루프를 참조하세요.

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

루프 한계

Bicep에서 루프를 사용하면 다음과 같은 제한 사항이 있습니다.

  • Bicep 루프는 배포 시작 시 확인할 수 있는 값에서만 작동합니다.
  • 루프 반복은 음수이거나 800회를 초과할 수 없습니다.
  • 중첩된 하위 리소스가 있는 리소스를 루프할 수 없습니다. 자식 리소스를 최상위 리소스로 변경합니다. 하위 리소스 반복을 참조하세요.
  • 여러 수준의 속성을 반복하려면 람다 맵 함수를 사용합니다.

정수 인덱스

인덱스를 사용하는 간단한 예를 보려면 문자열 배열이 포함된 변수를 만드세요.

param itemCount int = 5

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

output arrayResult array = stringArray

출력은 다음 값을 가진 배열을 반환합니다.

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

다음 예에서는 storageCount 매개 변수에 지정된 수의 스토리지 계정을 만듭니다. 각 스토리지 계정에 대해 세 가지 속성을 반환합니다.

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

인덱스 i는 스토리지 계정 리소스 이름을 만드는 데 사용됩니다.

다음 예에서는 모듈을 여러 번 배포합니다.

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

배열 요소

다음 예제에서는 storageNames 매개 변수에 제공된 각 이름에 대해 하나의 스토리지 계정을 만듭니다. 각 리소스 인스턴스에 사용할 이름 속성은 고유해야 합니다.

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

다음 예에서는 속성을 정의하기 위해 배열을 반복합니다. 가상 네트워크 내에 두 개의 서브넷을 만듭니다. 서브넷 이름은 고유해야 합니다.

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

배열 및 인덱스

다음 예에서는 스토리지 계정을 정의할 때 배열 요소와 인덱스 값을 모두 사용합니다.

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

다음 예에서는 배열의 요소와 인덱스를 모두 사용하여 새 리소스에 대한 정보를 출력합니다.

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

사전 개체

사전 개체의 요소를 반복하려면 개체를 배열로 변환하는 items 함수를 사용합니다. value 속성을 사용하여 개체의 속성을 가져옵니다. 리소스 이름은 고유해야 합니다.

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

조건이 있는 루프

리소스 및 모듈의 경우 루프 구문과 함께 if 표현식을 추가하여 조건부로 컬렉션을 배포할 수 있습니다.

다음 예는 조건문과 결합된 루프를 보여 줍니다. 이 예에서는 모듈의 모든 인스턴스에 단일 조건이 적용됩니다.

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

다음 예에서는 배열의 현재 요소에 특정한 조건을 적용하는 방법을 보여 줍니다.

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

일괄 배포

기본적으로 Azure 리소스는 병렬로 배포됩니다. 루프를 사용하여 한 리소스 종류의 여러 인스턴스를 만드는 경우 해당 인스턴스는 모두 동시에 배포됩니다. 생성되는 순서는 정해져 있지 않습니다. Bicep 파일에서 총 리소스 제한이 800개라는 점을 제외하고 병렬로 배포되는 리소스의 수에는 제한이 없습니다.

한 리소스 종류의 모든 인스턴스를 동시에 업데이트하려고 하지는 않을 수 있습니다. 예를 들어 프로덕션 환경을 업데이트할 때 특정 수를 한 번에 업데이트하도록 업데이트를 늦추려고 할 수 있습니다. 일괄 처리할 인스턴스의 하위 집합을 지정하고 동시에 배포할 수 있습니다. 다른 인스턴스는 해당 일괄 처리가 완료되기를 기다립니다.

리소스 인스턴스를 직렬로 배포하려면 batchSize 데코레이터를 추가합니다. 값을 동시에 배포할 인스턴스 수로 설정합니다. 루프에 이전 인스턴스에 대한 종속성이 생성됩니다. 따라서 이전 일괄 처리가 완료될 때까지 하나의 일괄 처리를 시작하지 않습니다.

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

순차 배포의 경우 일괄 처리 크기를 1로 설정합니다.

batchSize 데코레이터는 sys 네임스페이스에 있습니다. 이 데코레이터를 같은 이름의 다른 항목과 구별해야 하는 경우 데코레이터 앞에 sys를 추가합니다. @sys.batchSize(2)

자식 리소스에 대한 반복

중첩된 자식 리소스에 대해 루프를 사용할 수 없습니다. 자식 리소스의 인스턴스를 두 개 이상 만들려면 자식 리소스를 최상위 리소스로 변경합니다.

예를 들어, 일반적으로 파일 서비스 및 파일 공유를 스토리지 계정에 대한 중첩 리소스로 정의한다고 가정합니다.

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

파일 공유를 두 개 이상 만들려면 스토리지 계정 외부로 이동합니다. parent 속성을 통해 부모 리소스와의 관계를 정의합니다.

다음 예제에서는 스토리지 계정, 파일 서비스 및 두 개 이상의 파일 공유를 만드는 방법을 보여 줍니다.

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

참조 리소스/모듈 컬렉션

리소스 컬렉션의 런타임 상태를 나타내는 개체 배열을 반환하는 ARM 템플릿 references 함수입니다. Bicep에는 명시적 참조 함수가 없습니다. 대신 심볼 컬렉션 사용이 직접 사용되며 코드 생성 중에 Bicep은 이를 ARM 템플릿 참조 함수를 활용하는 ARM 템플릿으로 변환합니다. 참조 함수를 사용하여 기호 컬렉션을 ARM 템플릿으로 변환하는 변환 기능의 경우 Bicep CLI 버전 0.20.X 이상이 있어야 합니다. 또한 bicepconfig.json 파일에서 symbolicNameCodegen 설정을 표시하고 true로 설정해야 합니다.

정수 인덱스에 있는 두 샘플의 출력은 다음과 같이 작성할 수 있습니다.

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)

이 Bicep 파일은 references 함수를 활용하는 다음 ARM JSON 템플릿으로 변환됩니다.

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

위의 ARM JSON 템플릿에서는 languageVersion1.10-experimental로 설정해야 하며 리소스 요소는 배열 대신 개체입니다.

다음 단계

  • Bicep 파일을 만드는 방법에 대한 자세한 내용은 파일을 참조하세요.