본문 바로가기
  • lakescript
스터디 이야기/Terraform

[T101] 3-1. Terraform - 반복문(for_each)

by lakescript 2024. 6. 24.

 

더보기

이 스터디는 CloudNet@에서 진행하는 T101 스터디를 참여하면서 공부하는 내용을 기록하는 블로그 포스팅입니다.

CloudNet@에서 제공해주는 자료들과 테라폼으로 시작하는 IaC 를 바탕으로 작성되었습니다.

 

반복문

for_each

리소스 또는 모듈 블록에서 for_each에 입력된 데이터 형태가 map 또는 set이면, 선언된 key 값 개수만큼 리소스를 생성합니다. 즉, for_each 표현식을 사용하면 집합(sets), 맵(maps)을 사용하여 전체 리소스의 여러 복사본 또는 리소스 내 인라인 블록의 여러 복사본, 모듈의 복사본을 생성 할 수 있습니다.

map

resource "azurerm_resource_group" "rg" {
  for_each = tomap({
    a_group       = "eastus"
    another_group = "westus2"
  })
  name     = each.key
  location = each.value
}

set

resource "aws_iam_user" "the-accounts" {
  for_each = toset(["Todd", "James", "Alice", "Dottie"])
  name     = each.key
}

 

 

실습

resource "local_file" "abc" {
  for_each = {
    a = "content a"
    b = "content b"
  }
  
  content = each.value
  filename = "${path.module}/${each.key}.txt
}

위의 코드와 같이 main.tf 파일을 수정하여 terraform init & apply --auto-approve를 실행합니다.

2개의 resouece가 생성되었다고 결과를 반환해주는 것을 확인하실 수 있습니다.

이처럼 for_each 구문에 있는 값처럼 2개의 파일이 생성되고 각각 내용이 작성되었습니다.

 

for_each가 설정된 블록에서는 each 속성을 사용하여 구성을 수정할 수 있습니다.

  • each.key : 이 인스턴스에 해당하는 map 타입의 key 값
  • each.value : 이 인스턴스에 해당하는 map타입의 value 값

생성되는 resource의 경우 <리소스 타입>.<이름>.[<key>], module의 경우 module.<이름>[<key>]로 해당하는 리소스의 값을 참조합니다. 이 참조 방식을 통해 리소스간 종속성을 정의하기도 하고 변수로 다른 리소스에서 사용하거나 출력을 위한 결과값으로 사용합니다. 

 

variable "names" {
  default = {
    a = "content a"
    b = "content b"
    c = "content c"
  }
}

resource "local_file" "abc" {
  for_each = var.names
  content = each.value
  filename = "${path.module}/abc-${each.key}.txt"
}

resource "local_file" "def" {
  for_each = local_file.abc
  content = each.value.content
  filename = "${path.module}/def-${each.key}.txt"
}

위와 같이 코드를 작성하여 terraform apply를 실행해보겠습니다. 그전에 잠깐 코드를 살펴보면 local_file.abc는 변수의 map 형태의 값을 참조하고, local_file.def의 경우 local_file.abc의 결과가 map으로 반환되므로 다시 for_each 구문을 사용할 수 있습니다.

 

for_each 를 사용한 후에는 하나의 resource 또는 count 를 사용한 것과 같은 리소스 배열이 되는 것이 아니리 리소스 맵이 됩니다.

variable "names" {
  default = {
    a = "content a"
    c = "content c"
  }
}

resource "local_file" "abc" {
  for_each = var.names
  content = each.value
  filename = "${path.module}/abc-${each.key}.txt"
}

resource "local_file" "def" {
  for_each = local_file.abc
  content = each.value.content
  filename = "${path.module}/def-${each.key}.txt"
}

key 값은 count의 index와 달리 고유하므로 중간에 값을 삭제한 후 다시 적용해도 삭제한 값에 대해서만 리소스를 삭제합니다. 그것을 확인해보기 위해 위와 같이 main.tf 파일을 수정한 후 terraform apply를 실행해보겠습니다.

 

결과 값에 2 destoryed가 반환되는데, map 형태의 중간 값을 삭제했을 때 local_file.abc와 local_file.def 리소스에 각각 하나씩 영향을 받는다는 결과를 확인하실 수 있습니다. 

resource "local_file" "abc" {
  for_each = toset(["a", "b", "c"])
  content = "abc"
  filename = "${path.module}/abc-${each.key}.txt"
}


이때, key에 해당하는 리소스만 영향을 받아 삭제가 됩니다.
위의 코드처럼 index에 영향을 받지 않도록 구성하려면 list 대신 set을 활용하여 중간 값의 삭제로 인해 다른 리소스가 삭제되는 것을 방지할 수 있습니다. 즉, for_each 를 사용해 리소스으로 처리하면 컬렉션 중간의 항목도 안전하게 제거할 수 있어서, count리소스를 배열 처리보다 이점이 큽니다.

 

count VS for_each

count

count 는 index 로 몇번에 어떤 내용이 있는지를 알 수 없기 때문에 중간 리소스 삭제나 추가 시 문제가 발생할 수 있습니다.

resource "local_file" "abc" {
  count    = 3
  content  = "This is filename abc${count.index}.txt"
  filename = "${path.module}/abc${count.index}.txt"
}

위와 같이 main.tf 를 작성한 후 terraform apply를 진행합니다. 

{
  "version": 4,
  "terraform_version": "0.14.11",
  "serial": 1,
  "lineage": "b029fb2c-2c27-fe96-dae3-1552c8efdcfd",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "local_file",
      "name": "abc",
      "provider": "provider[\"registry.terraform.io/hashicorp/local\"]",
      "instances": [
        {
          "index_key": 0,
          "schema_version": 0,
          ...
          "sensitive_attributes": []
        },
        {
          "index_key": 1,
          "schema_version": 0,
          ...
          "sensitive_attributes": []
        },
        {
          "index_key": 2,
          "schema_version": 0,
      		...
          "sensitive_attributes": []
        }
      ]
    }
  ]
}

그 후 생성되는 terraform.tfstate 파일을 확인해보면 위와 같이 index_key가 부여되어 리소스가 생성된 것을 확인하실 수 있습니다.

 

for_each

key 값은 count의 index와는 달리 고유하므로 중간에 값을 삭제한 후 다시 적용해도 삭제한 값에 대해서만 리소스를 삭제합니다.

resource "local_file" "abc" {
  for_each    = toset(["a", "b", "c"])
  content  = "This is filename abc-${each.key}.txt"
  filename = "${path.module}/abc${each.key}.txt"
}

위와 같이 main.tf 파일을 수정한 후 terraform apply를 진행합니다.

{
  "version": 4,
  "terraform_version": "0.14.11",
  "serial": 8,
  "lineage": "b029fb2c-2c27-fe96-dae3-1552c8efdcfd",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "local_file",
      "name": "abc",
      "provider": "provider[\"registry.terraform.io/hashicorp/local\"]",
      "instances": [
        {
          "index_key": "a",
          "schema_version": 0,
          ...
          "sensitive_attributes": []
        },
        {
          "index_key": "b",
          "schema_version": 0,
          ...
          "sensitive_attributes": []
        },
        {
          "index_key": "c",
          "schema_version": 0,
          ...
          "sensitive_attributes": []
        }
      ]
    }
  ]
}

그 후 생성되는 terraform.tfstate 파일을 확인해보면 위와 같이 index_key가 for_each의 값으로 부여되어 리소스가 생성된 것을 확인하실 수 있습니다.