본문 바로가기

Terraform

[Terraform] Resource Block 구문 문법(count, for_each)

반응형

1. Terraform Resource Block 구문 문법

  • Terraform의 리소스 블록 구문에는 두 가지 주요 요소가 있습니다.
  • 첫 번째는 Arguments로, 리소스를 생성하기 위해 필요한 특정 정보입니다.
    • 필수적으로 명시해야 하는 필수(Required) 항목과 추가 설정을 위한 선택적(Optional) 항목으로 나뉩니다.
    • 각 리소스에 필요한 Arguments는 해당 프로바이더의 docs 페이지에서 확인할 수 있습니다.
  • 두 번째는 Meta Arguments로, 모든 리소스에서 공통적으로 사용할 수 있는 Arguments입니다.
    • 리소스 생성에 필요한 기본 Arguments 외에 추가적인 설정을 가능하게 합니다.
    • ex) count, for_each, depends_on, lifecycle 등등

 

2. Arguments 예시

  • Terraform에서 AWS EC2 인스턴스를 생성하기 위한 리소스 블록을 예로 들어 설명합니다. (AWS EC2 Argument Reference)
  • aws_instacne 리소스의 기본 Arguments는 리소스를 생성하는 데 필수적인 정보를 포함합니다.
  • 예를 들어, amiinstance_type는 필수 Arguments로 필수(Required) 요소입니다.
    • ami와 instance_type는 필수 Arguments로, 각각 EC2 인스턴스에 사용할 Amazon Machine Image(AMI)의 ID와 인스턴스 유형을 지정합니다. 이 두 필수 Arguments는 EC2 인스턴스 생성 시 반드시 명시되어야 합니다. 
  • 선택적(Optional) Arguments로는 key_name 과 vpc_security_group_ids 등을 설정할 수 있습니다.
    • key_name과 vpc_security_group_ids는 선택 Arguments로, 각각 EC2 인스턴스에 할당할 키 페어 이름과 인스턴스에 적용할 보안 그룹을 지정합니다. 
    • 선택적 Arguments로는  이러한 구조를 통해 Terraform 사용자는 필요에 맞게 EC2 인스턴스를 세부적으로 설정할 수 있습니다.
# EC2 인스턴스를 생성하는 Terraform 리소스 블록
resource "aws_instance" "example" {
  # 필수 Arguments
  ami           = "ami-1234567890abcdef0" # 인스턴스 생성에 사용할 Amazon Machine Image (AMI) ID
  instance_type = "t2.micro"              # EC2 인스턴스 유형

  # 선택적 Arguments
  key_name                = "my-key-pair"   # 인스턴스에 할당할 키 페어 이름
  vpc_security_group_ids  = ["sg-12345678"] # 인스턴스에 적용할 보안 그룹 ID

  # 태그 추가 (선택적)
  tags = {
    Name = "MyExampleInstance"
  }
}

 

 

3. Meta Arguments

  • resource를 생성하기 위해 필요한 Arguments 외에 모든 resource가 공통적으로 사용할 수 있는 Arguments를 Meta Arguments라 합니다.

 

3-1) count

  • count는 리소스 복수 생성 시 사용되는 Meta Argument로, 정수 값을 받아 해당 수만큼 리소스를 생성합니다.
  • 아래와 같이 count를 사용하여 AWS EC2를 여러개 생성할 수 있습니다.
data "aws_ami" "latest_amazon_linux_2" {
  most_recent = true
  filter {
    name   = "name"
    values = ["*amzn2-ami-hvm*"]
  }
  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
  filter {
    name   = "architecture"
    values = ["x86_64"]
  }
  owners = ["amazon"]
}

resource "aws_instance" "example" {
  count         = 3  # 세 개의 EC2 인스턴스 생성
  ami           = data.aws_ami.latest_amazon_linux_2.id
  instance_type = "t2.micro"
}
  • 위 코드는 AWS EC2를 생성하는 resource block에 count Meta Argument를 사용하여 count에 정의해놓은 수 만큼 반복하여 AWS EC2를 생성합니다.

 

  • count Argument 로 생성된 각 인스턴스에는 count 0부터 시작하여 추가 인스턴스마다 1씩 증가하는 고유 index가 할당됩니다.
  • 이 index는 리소스 블록 내에서 count.index로 액세스할 수 있으므로 각 인스턴스의 구성을 차별화할 수 있습니다.
  • 예를 들어 인덱스를 사용하여 각 인스턴스에 고유한 이름을 할당하거나 인덱스 위치를 기반으로 목록에서 특정 값을 검색할 수 있습니다.
resource "aws_instance" "example" {
  count         = 3  # 세 개의 EC2 인스턴스 생성
  
  ami           = data.aws_ami.latest_amazon_linux_2.id
  instance_type = "t2.micro"
  
  tags = {
    Name = "Server-${count.index}"  # Names will be Server-0, Server-1, Server-2
  }
}

 

 

  • output으로 count로 생성된 리소스값을 추출하기 위해 을 정의하는 방법은 다음과 같습니다.
  • output 블록은 리소스 블록이 count로 생성한 두 번째 인스턴스의 퍼블릭 IP 주소를 검색합니다.
  • index [1]는 0부터 시작한다는 점을 고려하여 두 번째 인스턴스를 참조하는 데 사용됩니다. 이렇게 하면 으로 여러 인스턴스를 생성한 경우 특정 인스턴스에 직접 쉽게 액세스할 수 있습니다 .
output "secondary_instance_public_ip" {
  value = aws_instance.example[1].public_ip
}

 

 

 

 

3-2) for_each

  • for_each는 Terraform에서 동일한 리소스 유형의 여러 리소스를 생성할 때 사용되며, count와는 달리 map이나 set 형식을 기반으로 각 요소에 대한 리소스를 생성합니다.
  • for_each를 사용할 경우, 리소스 블록 내에서 동일한 리소스 타입의 다수 인스턴스를 효율적으로 관리할 수 있습니다. 단, for_each와 count는 동일한 리소스 블록에서 함께 사용할 수 없습니다.
  • set 형식은 list unique element 특징을 가지고 있어서 리스트 안에 중복 값을 가질 수 없습니다.
    • [a,b,c]는 가능하지만, [a,a,b,c]는 불가능
  • map 형식은 각 요소를 each.key와 each.value를 통해 참조할 수 있습니다.
    • {name: "junho"}에서 each.key는 "name", each.value는 "junho"

 

  • set 형식 예시
    • for_each를 통해 여러명의 IAM User를 생성합니다.
# set 형식
resource "aws_iam_user" "the-accounts" {
  for_each = toset( ["Todd", "James", "Alice", "Dottie"] )
  name     = each.key
}

 

 

  • set 형식 output 예시
    • for_each를 set 형식으로 생성 후 output은 다음과 같이 확인할 수 있습니다.
# 모든 User의 이름 출력
output "user_names" {
  value = [for user in aws_iam_user.the-accounts : user.name]
}

# Alice User의 이름 출력
output "Alice_name" {
  value = aws_iam_user.the-accounts["Alice"].name
}

 

 

 

  • map 형식 예시
    • for_each로 리소스를 생성시 map 형식을 사용하여 key, value값을 통해 user 이름과 tag 를 함께 코드를 작성 가능합니다.
# map 형식
resource "aws_iam_user" "iam-user-account" {
  for_each = {
    alice = {
      level = "low"
      manager = "developer"
    }
    bob = {
      level = "mid"
      manager = "cloud-ta"
    }
    jh = {
      level = "high"
      manager = "devops"
    }
  }
  name = each.key
  tags = each.value
}

 

 

  • map 형식 output 예시
    • for_each를 map 형식으로 생성 후 output은 다음과 같이 확인할 수 있습니다.
output "user_names" {
  value = [for user in aws_iam_user.iam_user_account : user.name]
}

output "alice_user_arn" {
  value = aws_iam_user.iam_user_account["alice"].arn
}

 

 

4. for_each 와 count 비교

  • for_each 문은 반복을 수행 시 object key로 접근하여, index로 접근하는 list type의 count 와는 다르게 효율적입니다.
  • 예로 index형식의 count를 사용하여 iam user 생성 시 중간에 유저를 지우고 apply하면 오류가 발생하지만, for_each 를 사용하여 iam user 생성 시 중간 유저를 지우고 apply하면 정상적으로 유저를 지울 수 있습니다.

 

4-1) count를 이용하여 IAM 사용자 생성 및 삭제 테스트

  • count를 사용하여 iam user 생성 시 중간에 유저를 지우고 apply하는 테스트를 진행합니다.
resource "aws_iam_user" "user_count" {
  count = length(var.user_names)  # var.user_names is a list of names
  name  = var.user_names[count.index]
}

# Assuming var.user_names = ["user1", "user2", "user3"]
variable "user_names" {
  default = ["user1", "user2", "user3"]
}
  • var.user_names에서 "user2"를 제거하고 적용하면 Terraform은 나머지 사용자를 다시 색인화합니다. 이로 인해 "user3"이 "user2"의 위치로 이동하므로 의도하지 않은 삭제나 재생성이 발생할 수 있습니다.

 

count를 사용하여 유저3명 추가 코드

 

user_names에서 "user2"를 제거 후 Apply test code

 

  • "user2"라는 IAM 사용자를 "user3"으로 업데이트하려고 할 때 오류가 발생한 것 같지만 "user3"이 AWS 환경에 이미 존재합니다. 이 오류는 일반적으로 IAM 시스템의 다른 활성 사용자가 이미 사용한 이름으로 사용자의 이름을 바꾸거나 다시 생성하려고 할 때 발생합니다.
  • 즉, "user3"이 지워지기 전에 "user2"의 이름을 "user3"으로 변경하려고 했기 때문에 발생한 에러입니다.
  • 다시 Terraform apply를 실행하게 되면 다음과 같이 "user2"가 "user3"이란 이름으로 변경됨을 확인할 수 있습니다.

 

  • 결론
    • 처음 원했던 결과는 "user2"만 삭제되는 것이였지만, "user2"의 이름이 "user3"으로 변경되었고, "user3"은 삭제가 되었습니다.
    • 만약 실제 운영환경에서의 서버 리소스였다면 중요한 서버 리소스를 잘못 삭제하는 경우, 복구할 수 없는 심각한 결과를 초래할 수 있습니다.

 

 

4-2) for_each를 이용하여 IAM 사용자 생성 및 삭제 테스트

  • for_each를 사용하여 iam user 생성 시 중간에 유저를 지우고 apply하는 테스트를 진행합니다.
resource "aws_iam_user" "user_each" {
  for_each = toset(var.user_names)  # Convert the list of names to a set
  name     = each.value             # Use each.value to access the user name
}

variable "user_names" {
  default = ["user1", "user2", "user3"]
}

 

 

for_each를 사용하여 유저3명 추가 코드

 

user_names에서 "user2"를 제거 후 Apply test code

  • 결론
    • count와 다르게 for_each를 사용했을 경우 원했던 결과는 "user2"만 삭제되는 것이였으며, 원하는 결과처럼 "user2"만 삭제" 되었습니다.

 

 

 

 

 

 

반응형