개요
오늘은 Terraform 코드를 작성할 때 output for 반복문을 이용한 문제 해결을 작성해보려 한다.
문제
문제는 아래와 같다.
- 현재 서브넷은 environment 별로 다른 변수를 넣어주기 위해서 tfvars 파일을 이용해 cidr와 az를 지정해주고 있음
- 이 과정에서 for_each를 이용하게 되는데 다른 모듈에서 생성된 subnet의 id를 참조해야 할 경우가 생김. 이를 output으로 전달하고자 했음
- output에서 for_each문을 사용할 수 없어서 혹시 해당 for_each로 생성된 리소스의 id를 동일하게 반복적으로 한 output으로 불러올 순 없는지 확인이 필요했음
처음 접근을 할 때는 output 리소스 블록에서 for_each 를 사용할 수 있는데 내가 코드를 잘못 작성해서 plan이 제대로 작동하지 않는다고 생각했음.
Error: Missing resource instance key
on ../../modules/networks/subnet/outputs.tf line 3, in output "pub_subnet_ids":
3: "${aws_subnet.public_subnet.id}"
Because aws_subnet.public_subnet has "for_each" set, its attributes must be
accessed on specific instances.
For example, to correlate with indices of a referring resource, use:
aws_subnet.public_subnet[each.key]
Terraform Plan Failed!
# outputs.tf 파일에서 each.key를 사용하려고 해봄
Error: Reference to "each" in context without for_each
on ../../modules/networks/subnet/outputs.tf line 3, in output "pub_subnet_ids":
3: "${aws_subnet.public_subnet[each.key].id}"
The "each" object can be used only in "module" or "resource" blocks, and only
when the "for_each" argument is set.
Error: Reference to "each" in context without for_each
on ../../modules/networks/subnet/outputs.tf line 9, in output "pri_subnet_ids":
9: "${aws_subnet.private_subnet[each.key].id}"
The "each" object can be used only in "module" or "resource" blocks, and only
when the "for_each" argument is set.
Error: Reference to "each" in context without for_each
on ../../modules/networks/subnet/outputs.tf line 15, in output "pri_db_subnet_ids":
15: "${aws_subnet.private_db_subnet[each.key].id}"
The "each" object can be used only in "module" or "resource" blocks, and only
when the "for_each" argument is set.
Terraform Plan Failed!
계속 안되니까 테라폼에서 지원해주지 않을수도 있다는 생각을 해봤다. 그리고 구글 서칭을 시작해서 혹시 output에서 for_each 반복문을 사용할 수 있는지 만약 사용할 수 없다면 어떻게 해야하는지 알아보았다.
- https://stackoverflow.com/questions/64989080/terraform-modules-output-from-for-each
- https://terraformguru.com/terraform-certification-using-azure-cloud/34-Output-Values-with-for_each-and-for-loops/
- https://honglab.tistory.com/216
결국 for_each를 사용하지 않고 for 문으로 코드를 변경해서 작성했음 honglab님의 블로그가 결정적인 도움이 되었음. 역시 terraform의 신...
# 변경된 output 코드
output "pub_subnet_ids" {
value = [ for subnet in aws_subnet.public_subnet : subnet.id ]
}
output "pri_subnet_ids" {
value = [ for subnet in aws_subnet.private_subnet : subnet.id ]
}
output "pri_db_subnet_ids" {
value = [ for subnet in aws_subnet.private_subnet : subnet.id ]
}
변경을 하고 environment/dev/main.tf 파일에서 output을 사용하는 코드에서 또 에러가 발생했음
# 모듈에서 subnet id를 불러올 때 index를 이용해보고자 함
Error: Invalid index
on main.tf line 39, in module "nat_gw":
39: pri_subnet_id = module.subnet.pri_subnet_ids[0].id
├────────────────
│ module.subnet.pri_subnet_ids is object with no attributes
The given key does not identify an element in this collection value. An
object only supports looking up attributes by name, not by numeric index.
# subnet id를 불러올 때 실제 변수의 key 값으로 인덱싱이 되는지 확인해봄
Error: Invalid index
on main.tf line 39, in module "nat_gw":
39: pri_subnet_id = module.subnet.pri_subnet_ids["10.0.1.0/26"].id
├────────────────
│ module.subnet.pri_subnet_ids is object with no attributes
The given key does not identify an element in this collection value.
Terraform Plan Failed!
aws_nat_gateway를 선언하고 subnet_id Argument를 입력하는 과정에서 한 개의 subnet id가 필요했는데, 현재 for문을 통해서 출력되는 output은 두개임. 그래서 인덱싱을 통해 해당 값을 불러오려고 했는데, 위의 두개 사례에서 작동하지 않았다.
그래서 실제로 main.tf에서 outputs.tf 파일의 결과물을 module을 통해 불러올때 어떻게 출력되는지 곰곰히 생각해보았다.
output "pub_subnet_ids" {
value = [ for subnet in aws_subnet.public_subnet : subnet.id ]
}
이 코드를 보면 알겠지만 output에서 이미 subnet.id로 id Attribute를 지정해주었기 때문에 실제 module 블록에서 불러올때는 id 의 값이며 value 에서 list로 출력을 해주기 때문에 결과적으로 ["subnet_1_id"," subnet_2_id "] 로 출력이 될것이라고 예상이 된다. 그래서 모듈에서 불러올 때 subnet[0] 이라고 인덱싱만 해주면 아이디가 입력될 것이라고 생각함.
module "nat_gw" {
depends_on = [ module.subnet ]
source = "../../modules/networks/nat_gateway"
prefix = var.prefix
pri_subnet_id = module.subnet.pri_subnet_ids[0]
}
위와 같이 코드를 변경하니 정상적으로 리소스가 생성함을 알 수 있었다.
# module.nat_gw.aws_nat_gateway.ngw will be created
+ resource "aws_nat_gateway" "ngw" {
+ allocation_id = (known after apply)
+ association_id = (known after apply)
+ connectivity_type = "public"
+ id = (known after apply)
+ network_interface_id = (known after apply)
+ private_ip = (known after apply)
+ public_ip = (known after apply)
+ secondary_private_ip_address_count = (known after apply)
+ secondary_private_ip_addresses = (known after apply)
+ subnet_id = (known after apply)
+ tags = {
+ "Name" = "familiar-dev-nat-gw"
}
+ tags_all = {
+ "Environment" = "Dev"
+ "Name" = "familiar-dev-nat-gw"
+ "Project" = "Familiar"
+ "Terraform" = "true"
}
}
배운 것
output에서는 for 문을 이용해 리소스의 값을 출력해줘야 한다는 사실을 알게 되었다.
그리고 사실 위와 같은 과정을 정말 오랜 시간 고민하고 오랜 시간 테스트 했는데 뒤돌아보니 너무 쉬운 문제였다. 공식 Docs에 친절히 설명히 되어 있지 않은 부분의 경우 Stackoverflow 나 블로그를 더더욱 열심히 서칭해봐야겠다고 다짐이 든다.
물론 처음 써보는 툴이니 익숙하지 않아서 그랬던것도 있으니 다음부터 잘하자는 다짐도 하는걸로..
'기타 > 토이프로젝트' 카테고리의 다른 글
[Familiar] Terraform 구조 잡기 & 문제 해결 (0) | 2024.08.08 |
---|---|
[Familiar] Terraform plan & apply with GitHub Actions (0) | 2024.08.06 |
[Familiar] 테라폼 사용을 위한 Github Actions OIDC 설정 (0) | 2024.08.02 |
[Familiar] 토이 프로젝트 개요 (0) | 2024.08.01 |