클라우드에서 인프라를 구축을 할 때 비용 절감, 배포 속도 향상, 일관성, 안정성 및 재사용성을 고려하여 웹 콘솔로 구축하기보다는 IaC(Infrastructure as Code) 도구를 활용하여 구축하는 것이 좋습니다. 오픈소스이며 IaC 도구 중 가장 많이 사용하는 것이 Terraform입니다. Terraform 코드를 효율적이고 효과적으로 작성하기 위해서는 Terraform에서 제공되는 기능들을 적절하게 사용하는 것이 중요합니다. 그렇지 않으면 예상과 다른 결과가 발생할 수 있습니다. 이번 글에서는 Terraform에서 제공하는 각 반복문의 특징과 차이점을 알아보겠습니다.
Terraform 반복문
일반적인 선언적 언어는 반복문이 없지만, Terraform에서는 다양한 함수를 지원하여 반복문을 사용할 수 있습니다.
대표적으로 사용하는 반복문은 count, for_each가 있습니다. 각각의 특징은 아래와 같습니다.
- count 매개 변수: 리소스와 모듈을 반복
- for_each 표현식: 리소스 및 리소스 내의 인라인 블록과 모듈 반복
각 반복문의 사용 방법을 요약하자면,
- 생성하려는 리소스가 거의 동일하고 변경되지 않을 때
count
를 활용한다면 빠르고 간결하게 생성할 수 있습니다. - 고유한 이름, 데이터가 들어 있는 리소스, 재생성되지 말아야 할 리소스 등에는
for_each
를 활용한다면 안전하게 생성 및 변경할 수 있습니다.
Terraform 코드를 실행해 보면서 count
와 for_each
의 차이점에 대해 알아보겠습니다.
Count
count
매개 변수는 정수를 허용하고 전체 리소스, 모듈을 반복하여 만들 수 있습니다.
count
로 구성하게 되면 count.index
를 활용하여 count
에서 생성한 인덱스에 접근 가능합니다.
아래 코드는 AWS IAM 사용자를 생성하는 코드입니다.
만약 3명의 사용자를 생성하려면 어떻게 해야 할까요?
Terraform 리소스에서 사용할 수 있는 count
를 활용하여 단순하게 반복할 수 있습니다.
resource "aws_iam_user" "Rei" {
name = "Rei"
}
아래 코드에서 count
를 사용하지만 생성되지 않습니다.
IAM 사용자의 이름은 고유해야 생성할 수 있는데 이름이 같아서 오류가 발생합니다.
count
는 단순히 생성할 리소스의 수를 반복하여 작업하기 때문입니다.
resource "aws_iam_user" "count" {
count = 3
name = "Rei"
}
count.index
를 사용하여 이름 뒤에 인덱스를 추가하여 고유한 이름을 만들 수 있습니다.
resource "aws_iam_user" "count" {
count = 3
name = "Rei${count.index}"
}
terraform apply
명령을 사용하면 생성하려는 리소스가 리스트로 저장되는 것을 확인할 수 있고, IAM 사용자에 Rei0, Rei1, Rei2가 생성된다는 것을 확인할 수 있습니다.
# aws_iam_user.count[0] will be created
+ resource "aws_iam_user" "count" {
+ name = "Rei0"
(...)
}
# aws_iam_user.count[1] will be created
+ resource "aws_iam_user" "count" {
+ name = "Rei1"
(...)
}
# aws_iam_user.count[2] will be created
+ resource "aws_iam_user" "count" {
+ name = "Rei2"
(...)
}
Input Variable을 이용한 Count 활용
만들고 싶은 IAM 사용자를 리스트 타입의 variable
로 선언하고, length
내장 함수를 활용하여 리소스를 생성할 수 있습니다.
variable "user_name" {
type = list(string)
default = ["joe", "kim", "lee", "park"]
}
resource "aws_iam_user" "count" {
count = length(var.user_name)
name = var.user_name[count.index]
}
위와 같이 리소스에 count
를 사용하게 되면 각각의 리소스가 아니라 리소스의 배열로 저장됩니다.
apply
명령 및 tfstate
에서 확인 가능합니다.
IAM 사용자에 joe
, kim
, lee
, park
이 생성된다는 것을 확인할 수 있습니다.
# aws_iam_user.count[0] will be created
+ resource "aws_iam_user" "count" {
+ name = "joe"
(...)
}
# aws_iam_user.count[1] will be created
+ resource "aws_iam_user" "count" {
+ name = "kim"
(...)
}
# aws_iam_user.count[2] will be created
+ resource "aws_iam_user" "count" {
+ name = "lee"
(...)
}
# aws_iam_user.count[3] will be created
+ resource "aws_iam_user" "count" {
+ name = "park"
(...)
}
Output Value에서 Count 활용
각 리소스에 대한 출력 값을 읽으려면, 해당 리소스가 배열로 저장되었으므로 인덱스를 지정하여 속성을 읽을 수 있습니다.
인덱스 대신 *
을 사용하면 모든 인덱스 조회가 가능합니다.
output "first_arn"{
value = aws_iam_user.count[0].arn
}
output "all_arn"{
value = aws_iam_user.count[*].arn
}
Outputs:
first_arn = "arn:aws:iam::<num>:user/joe"
all_arn = [
"arn:aws:iam::<num>:user/joe",
"arn:aws:iam::<num>:user/kim",
"arn:aws:iam::<num>:user/lee",
"arn:aws:iam::<num>:user/park",
]
Count 제약사항
count
에는 두 가지 제약 사항이 있습니다.
첫 번째는, count
는 전체 리소스를 반복하지만 리소스 내에 있는 인라인 블록은 반복하지 않습니다.
대표적인 인라인 블록 코드입니다.
count
는 인라인 블록인 tags
에 Key 또는 Value 값에 해당하는 인수들에 대해서 지원되지 않습니다.
resource "<resource>" "<name>" {
(...)
tags = {
"Name" = ""
"<key>" = "<value>"
}
}
두 번째는, count
값 변경 시 발생합니다.
# aws_iam_user.count[1] will be updated in-place
~ resource "aws_iam_user" "count" {
~ name = "kim" -> "lee"
(...)
}
# aws_iam_user.count[2] will be updated in-place
~ resource "aws_iam_user" "count" {
~ name = "lee" -> "park"
(...)
}
# aws_iam_user.count[3] will be destroyed
# (because index [3] is out of range for count)
- resource "aws_iam_user" "count" {
- id = "park" -> null
- name = "park" -> null
(...)
}
앞서 선언한 변수에서 kim
을 제거하면 kim
을 삭제하는 것이 아니라 kim -> lee
, lee -> parck
, park -> null
순서로 삭제 및 생성합니다.
count
는 앞서 설명드린 대로 리소스의 배열로 저장됩니다. Terraform에서 리소스를 식별할 때 해당 배열의 인덱스를 사용합니다.
count
로 생성한 리소스의 중간 값을 제거하면 위와 같이 뒤의 모든 값들이 삭제 후 새로 생성하게 됩니다.
최종 결과물은 동일하지만 재생성되지 말아야 할 리소스라면 문제가 발생할 수 있습니다.
For_each
for_each
표현식은 list
, set
과 map
을 반복하여 전체 리소스, 리소스 내 인라인 블록, 모듈을 반복하여 만들 수 있습니다.
for_each로 구성되면 each.key
와 each.value
를 사용하여 해당 키와 값에 접근 가능합니다.
리소스에 for_each
를 사용할 때는 리스트가 지원되지 않습니다.
Collection에는 반복할 set
, map
을 정의하고 Config에 each.key
와 each.value
를 사용하여 Collection에 정의된 키와 값을 가져올 수 있습니다.
resource "<PROVIDER>_<TYPE>" "<NAME>" {
for_each = <Coleection>
[Config ...]
}
Input Variable을 이용한 for_each 활용
variable은 이전에 선언한 것과 동일한 것을 사용하겠습니다.
toset
은 list
를 set
으로 변환하는 데 사용합니다. for_each
는 리소스에서 사용될 때는 set
과 map
을 지원합니다. 해당 리소스가 반복하는 동안 each.value
를 통해 사용자 이름을 정의했습니다.
resource "aws_iam_user" "for_each" {
for_each = toset(var.user_names)
name = each.value
}
terraform apply
명령을 사용하면 생성하려는 리소스를 set
으로 확인할 수 있고, IAM 사용자에 joe
, kim
, lee
, park
이 생성된다는 것을 확인할 수 있습니다.
for_each
도 각각의 리소스가 아니라 하나의 리소스에 배열로 저장됩니다.
# aws_iam_user.for_each["joe"] will be created
+ resource "aws_iam_user" "for_each" {
+ name = "joe"
(...)
}
# aws_iam_user.for_each["kim"] will be created
+ resource "aws_iam_user" "for_each" {
+ name = "kim"
(...)
}
# aws_iam_user.for_each["lee"] will be created
+ resource "aws_iam_user" "for_each" {
+ name = "lee"
(...)
}
# aws_iam_user.for_each["park"] will be created
+ resource "aws_iam_user" "for_each" {
+ name = "park"
(...)
}
Output Value에서 for_each 활용
해당 리소스에 대한 출력 값을 읽기 위해서는 배열 구문을 활용하여 값을 지정하여 속성을 읽을 수 있습니다.
values
내장 함수와 *
을 사용하여 모든 인덱스 조회가 가능합니다.
output "first_arn"{
value = aws_iam_user.for_each["joe"].arn
}
output "all_users" {
value = values(aws_iam_user.for_each)[*].arn
}
Outputs:
first_arn = "arn:aws:iam::<num>:user/joe"
all_users = [
"arn:aws:iam::<num>:user/joe",
"arn:aws:iam::<num>:user/kim",
"arn:aws:iam::<num>:user/lee",
"arn:aws:iam::<num>:user/park",
]
for_each
는 앞서 설명드린 대로 하나의 리소스에 map
으로 저장됩니다.
variable
의 값 중 중간을 제거해도 해당 리소스 값만 변경 및 삭제됩니다.
count
와 다르게 삭제 및 생성을 반복하지 않고 원하는 값만 삭제 가능합니다.
# aws_iam_user.for_each["kim"] will be destroyed
# (because key ["kim"] is not in for_each map)
- resource "aws_iam_user" "for_each" {
- id = "kim" -> null
- name = "kim" -> null
(...)
}
맺음말
지금까지 Terraform 반복문 count
와 for_each
에 대해서 알아보았습니다. 반복문마다 각 특징을 인지한 후 사용하면 코드 작성 및 State 관리의 큰 도움이 될 거라 생각합니다.
인포그랩은 GitLab 및 DevOps에 대한 맞춤 기술 지원을 제공합니다. GitLab(Omnibus/Cloud Native Hybrid) 구축 관련한 지원이 필요하시면 문의하기 로 연락 주십시오.