cd mkdir 39 cd 39 2 count 사용 리소스 또는 모듈 블록에 count 값이 정수인 인수가 포함된 경우, 선언된 정수 값만큼 리소스나 모듈을 생성하게 된다 아래와 같이 count를 사용해 보자. vi main.tf resource "local_file" "abc" { count = 5 content = "abc" filename = "${path.module}/abc.txt" } output "filecontent" { value = local_file.abc.*.content } output "fileid" { value = local_file.abc.*.id } output "filename" { value = local_file.abc.*.filename } 3 terraform init && terraform apply -auto-approve 아래는 컨텐츠를 5번 실행하는 것이다. Outputs: filecontent = [ "abc", "abc", "abc", "abc", "abc", ] fileid = [ "a9993e364706816aba3e25717850c26c9cd0d89d", "a9993e364706816aba3e25717850c26c9cd0d89d", "a9993e364706816aba3e25717850c26c9cd0d89d", "a9993e364706816aba3e25717850c26c9cd0d89d", "a9993e364706816aba3e25717850c26c9cd0d89d", ] filename = [ "./abc.txt", "./abc.txt", "./abc.txt", "./abc.txt", "./abc.txt", ] terraform state list local_file.abc[0] local_file.abc[1] local_file.abc[2] local_file.abc[3] local_file.abc[4] terraform state show local_file.abc[0] terraform state show local_file.abc[4] ls *.txt abc.txt 4 결과? 파일명이 같은 파일1개가 생긴다. abc.txt 1개 # ls abc.txt main.tf terraform.tfstate # 테라폼 콘솔로 상세 내역을 찍어보자~ terraform console > ----------------- local_file.abc // 5개가 실행된다. 같은 파일이 5개이다. local_file.abc[0] local_file.abc[4] exit ----------------- # terraform output terraform output filename [ "./abc.txt", "./abc.txt", "./abc.txt", "./abc.txt", "./abc.txt", ] terraform output fileid terraform output filecontent <3> 파일 5개 만드는것 테스트 rm -rf main.tf main.tf 수정 // content 내용도 abc0, abc1 // filename 은 abc0.txt , abc1.txt , ........ resource "local_file" "abc" { count = 5 content = "abc${count.index}" filename = "${path.module}/abc${count.index}.txt" } output "fileid" { value = local_file.abc.*.id } output "filename" { value = local_file.abc.*.filename } output "filecontent" { value = local_file.abc.*.content } Outputs: filecontent = [ "abc0", "abc1", "abc2", "abc3", "abc4", ] fileid = [ "062c648aaf68174757c50ab1aeebb61e059c1d1b", "9ee036287b4cfbcfa3b5bbfcf92d46eb5e75df96", "229d028063a11904f846c91224abaa99113f3a15", "99bdd81005eaa37fa71ff0787cd1d65f63d3d293", "5ac835798aa493440692c54c7ab76161f06c1b88", ] filename = [ "./abc0.txt", "./abc1.txt", "./abc2.txt", "./abc3.txt", "./abc4.txt", ] # terraform apply -auto-approve terraform state list ls *.txt # terraform state list local_file.abc[0] local_file.abc[1] local_file.abc[2] local_file.abc[3] local_file.abc[4] # ls abc0.txt abc1.txt abc2.txt abc3.txt abc4.txt main.tf terraform.tfstate terraform.tfstate.backup # terraform console > ----------------- local_file.abc[0] local_file.abc[4] exit ----------------- # terraform output terraform output filename terraform output fileid terraform output filecontent [ "abc0", "abc1", "abc2", "abc3", "abc4", ] # graph 확인 > graph.dot 파일 선택 후 오른쪽 상단 DOT 클릭 terraform graph > graph.dot <4> list 형태의 배열을 활용한 반복문 동작 구성 1 rm -rf main.tf vi main.tf // list 는 스트링 "a" "b" "c" variable "names" { type = list(string) default = ["a", "b", "c"] } resource "local_file" "abc" { count = length(var.names) content = "abc" # 변수 인덱스에 직접 접근 filename = "${path.module}/abc-${var.names[count.index]}.txt" } resource "local_file" "def" { count = length(var.names) content = local_file.abc[count.index].content # element function 활용 filename = "${path.module}/def-${element(var.names, count.index)}.txt" } // default = ["a", "b", "c"] 리스트를 사용한다. // count = length(var.names) 은 3 이 된다. // abc 3번 , def 3 번 실행 = 총 6개 파일 생김. 7 terraform apply -auto-approve terraform state list local_file.abc[0] local_file.abc[1] local_file.abc[2] local_file.def[0] local_file.def[1] local_file.def[2] ls *.txt abc-a.txt abc-b.txt abc-c.txt def-a.txt def-b.txt def-c.txt # ls abc-a.txt abc-b.txt abc-c.txt def-a.txt def-b.txt def-c.txt main.tf terraform.tfstate terraform.tfstate.backup # more abc-a.txt abc # more abc-b.txt abc # more def-a.txt abc diff abc-a.txt abc-b.txt (동일) diff abc-a.txt def-c.txt (동일) terraform console > ----------------- local_file.abc[0] local_file.def[4] exit ----------------- # terraform output terraform output filename terraform output fileid terraform output filecontent terraform refresh # graph 확인 > graph.dot 파일 선택 후 오른쪽 상단 DOT 클릭 terraform graph > graph.dot 8 변경 해보자~ cd mkdir 391 cd 391 default = ["a", "b", "c"] 리스트를 사용한다. b를 삭제해 본다. default = ["a", "c"] 리스트를 사용한다. 외부 변수가 list 타입인 경우 중간에 값이 삭제되면 인덱스가 줄어들어 의도했던 중간 값에 대한 리소스만 삭제되는 것이 아니라 이후의 정의된 리소스들도 삭제되고 재생성된다 variable "names" { type = list(string) default = ["a", "c"] # index 1자리의 b를 삭제 } resource "local_file" "abc" { count = length(var.names) content = "abc" # 변수 인덱스에 직접 접근 filename = "${path.module}/abc-${var.names[count.index]}.txt" } resource "local_file" "def" { count = length(var.names) content = local_file.abc[count.index].content # element function 활용 filename = "${path.module}/def-${element(var.names, count.index)}.txt" } 10 # terraform init terraform plan ls -l *.txt terraform apply -auto-approve # terraform state list local_file.abc[0] local_file.abc[1] local_file.def[0] local_file.def[1] // index 0 , index 1이 들어감 = count는 정수 순번대로 생성되나, 중간 값을 삭제하면 장애 발생 우려됨. // 이렇경우 for_each 사용을 권장한다. <5> 중간값을 변경할 경우, for_each 사용을 권장한다. 1 선언된 key 값 개수만큼 리소스를 생성하게 된다. rm -rf * vi main.tf resource "local_file" "abc" { for_each = { a = "content a" b = "content b" } content = each.value filename = "${path.module}/${each.key}.txt" } 2 # terraform apply -auto-approve terraform state list local_file.abc["a"] local_file.abc["b"] ls *.txt a.txt b.txt cat a.txt ;echo cat b.txt ;echo 391]# cat a.txt ;echo content a 391]# cat b.txt ;echo content b terraform console > ----------------- local_file.abc local_file.abc[a] Error: Invalid index local_file.abc["a"] { "content" = "content a" exit 3 main.tf 파일 수정 ? local_file.abc는 변수의 map 형태의 값을 참조, local_file.def의 경우 local_file.abc 또한 결과가 map으로 반환되므로 다시 for_each 구문을 사용할 수 있다 rm -rf * 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" } 4 # terraform apply -auto-approve terraform state list ls *.txt # terraform state list local_file.abc["a"] local_file.abc["b"] local_file.abc["c"] local_file.def["a"] local_file.def["b"] local_file.def["c"] # ls *.txt abc-a.txt abc-b.txt abc-c.txt def-a.txt def-b.txt def-c.txt # more abc-a.txt content a # more abc-b.txt content b 5 중간 삭제 테스트 ? key 값은 count의 index와는 달리 고유하므로 중간에 값을 삭제한 후 다시 적용해도 삭제한 값에 대해서만 리소스를 삭제한다 rm -rf * vi main.tf 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" } # terraform apply -auto-approve terraform state list ls *.txt # terraform state list local_file.abc["a"] local_file.abc["c"] local_file.def["a"] local_file.def["c"] # ls *.txt abc-a.txt abc-c.txt def-a.txt def-c.txt <6> iam user 3명 만들어보자. = count 사용 1 cd mkdir iam cd iam // 사용자 1명 생성 형식 provider "aws" { region = "us-east-2" } resource "aws_iam_user" "example" { name = "1000" } 2 // 일반 for 문은 안됨. count나 for each 사용하세요~ # This is just pseudo code. It won't actually work in Terraform. for (i = 0; i < 3; i++) { resource "aws_iam_user" "example" { name = "neo" } } // neo를 3번 만들려고 해서 에러가 난다. 3 iam.tf provider "aws" { region = "ap-northeast-2" } resource "aws_iam_user" "myiam" { count = 3 name = "myuser.${count.index}" } 4 terraform init && terraform plan Plan: 3 to add, 0 to change, 0 to destroy. terraform apply -auto-approve // aws iam console 에서 확인 = user계정 3개가 생성 됨. myuser.0 myuser.1 myuser.2 # 확인 terraform state list aws_iam_user.myiam[0] aws_iam_user.myiam[1] aws_iam_user.myiam[2] aws iam list-users | jq : # terraform console > aws_iam_user.myiam aws_iam_user.myiam[0] aws_iam_user.myiam[1].name "myuser.1" exit 5 삭제 terraform destroy -auto-approve // iam에서 계정 삭제됨 aws iam list-users | jq rm -rf * <7> count 입력 변수를 통해 IAM 사용자 생성 1 // 계정이 1000,2000,3000 3개의 계정을 만들고자 함. 입력 변수 코드 사용 = vi variables.tf variable "user_names" { description = "Create IAM users with these names" type = list(string) default = ["1000", "2000", "3000"] } 2 iam.tf provider "aws" { region = "ap-northeast-2" } resource "aws_iam_user" "myiam" { count = length(var.user_names) name = var.user_names[count.index] } // count = length(var.user_names) 은 3 이 됨. 3 # terraform plan ... # aws_iam_user.myiam[0] will be created # aws_iam_user.myiam[1] will be created # aws_iam_user.myiam[2] will be created 4 // 출력해서 확인해보자~~ // * 를 사용 가능하다. value = aws_iam_user.myiam[*].arn vi output.tf output "first_arn" { value = aws_iam_user.myiam[0].arn description = "The ARN for the first user" } output "all_arns" { value = aws_iam_user.myiam[*].arn description = "The ARNs for all users" } 5 # terraform apply -auto-approve Apply complete! Resources: 3 added, 0 changed, 0 destroyed. Outputs: all_arns = [ "arn:aws:iam::319485572629:user/1000", "arn:aws:iam::319485572629:user/2000", "arn:aws:iam::319485572629:user/3000", ] first_arn = "arn:aws:iam::319485572629:user/1000" terraform state list aws_iam_user.myiam[0] aws_iam_user.myiam[1] aws_iam_user.myiam[2] terraform output all_arns = [ "arn:aws:iam::319485572629:user/1000", "arn:aws:iam::319485572629:user/2000", "arn:aws:iam::319485572629:user/3000", ] first_arn = "arn:aws:iam::319485572629:user/1000" terraform output all_arns 6 콘솔에서 계정 생성 확인 1000 2000 3000 7 중간 사용자 퇴사 하여 삭제 하고자함. // 사번이 1000,2000,3000 중 2000번 사번자 퇴사함. vi variables.tf 수정 variable "user_names" { description = "Create IAM users with these names" type = list(string) default = ["1000", "3000"] } // 중간 항목을 제거하면, 인덱스가 하나씩 당겨짐. 문제 발생함. // 모든 유형이 같을 때만 count 사용 필요 // count 사용 시 목록 중간 항목을 제거하면 테라폼은 해당 항목 뒤에 있는 모든 리소스를 삭제한 다음 해당 리소스를 처음부터 다시 만듬 # plan : 출력 내용 확인! terraform plan # aws_iam_user.myiam[2] will be destroyed # (because index [2] is out of range for count) - resource "aws_iam_user" "myiam" { - arn = "arn:aws:iam::319485572629:user/3000" -> null - force_destroy = false -> null - id = "3000" -> null - name = "3000" -> null - path = "/" -> null - tags = {} -> null - tags_all = {} -> null - unique_id = "AIDAUUYWS6YKXZ4IL5XUV" -> null } Plan: 0 to add, 1 to change, 1 to destroy. Changes to Outputs: ~ all_arns = [ # (1 unchanged element hidden) "arn:aws:iam::319485572629:user/2000", - "arn:aws:iam::319485572629:user/3000", ] 8 terraform apply -auto-approve aws_iam_user.myiam[2]: Destroying... [id=3000] aws_iam_user.myiam[1]: Modifying... [id=2000] aws_iam_user.myiam[2]: Destruction complete after 1s ╷ │ Error: updating IAM User (2000): EntityAlreadyExists: User with name 3000 already exists. │ status code: 409, request id: 1f520af3-1a70-4ca6-8181-cdc3de0afe54 │ │ with aws_iam_user.myiam[1], │ on iam.tf line 10, in resource "aws_iam_user" "myiam": │ 10: resource "aws_iam_user" "myiam" { │ ╵ // 헉! 3000 사번이 지워짐. 2000 계정을 삭제 했는데 ~~ // 콘솔에 확인하면 1000 , 2000 계정만 존재함. 9 삭제 terraform destroy -auto-approve aws iam list-users | jq count : 반복문, 정수 값만큼 리소스나 모듈을 생성 권장함. <8> for_each 입력 변수를 통해 IAM 사용자 생성 for_each 으로 계정 생성 테스트 // 반복문, 선언된 key 값 개수만큼 리소스를 생성 // key가 있어서 숫자가 틀리지 않는다. 1 rm -rf *.tf vi iam.tf provider "aws" { region = "ap-northeast-2" } resource "aws_iam_user" "myiam" { for_each = toset(var.user_names) name = each.value } 2 vi variables.tf variable "user_names" { description = "Create IAM users with these names" type = list(string) default = ["1000", "2000", "3000"] } 3 output.tf output "all_users" { value = aws_iam_user.myiam } 4 # terraform plan && terraform apply -auto-approve # 확인 terraform state list aws_iam_user.myiam["1000"] aws_iam_user.myiam["2000"] aws_iam_user.myiam["3000"] terraform output 5 variables.tf 2명으로 수정 variable "user_names" { description = "Create IAM users with these names" type = list(string) default = ["1000", "3000"] } 6 terraform plan terraform apply -auto-approve Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: - destroy Terraform will perform the following actions: # aws_iam_user.myiam["2000"] will be destroyed # (because key ["2000"] is not in for_each map) - resource "aws_iam_user" "myiam" { - arn = "arn:aws:iam::319485572629:user/2000" -> null - force_destroy = false -> null - id = "2000" -> null - name = "2000" -> null - path = "/" -> null - tags = {} -> null terraform state list aws_iam_user.myiam["1000"] aws_iam_user.myiam["3000"] // 원하는 데로 2000 계정 삭제됨!! 7 // 계정 모두 삭제 terraform destroy -auto-approve aws iam list-users | jq rm -rf *.tf <9> for 문 = content의 값 정의에 for 구문을 사용하여 내부 값을 일괄적으로 변경 1 소문자를 대문자로 변경 vi main.tf variable "names" { default = ["a", "b", "c"] } resource "local_file" "abc" { content = jsonencode([for s in var.names : upper(s)]) # 결과 : ["A", "B", "C"] filename = "${path.module}/abc.txt" } // s가 var.names 으로 a ,b ,c 가 된다. // upper(s)에 s에 a ,b, c 가 들어가서 대문자가 된다. 2 참고 title https://developer.hashicorp.com/terraform/language/functions/title # terraform init terraform apply -auto-approve terraform state list ls *.txt cat abc.txt ;echo # terraform state list local_file.abc # ls *.txt abc.txt # cat abc.txt ;echo ["A","B","C"] # 참고 jsonencode Function terraform console > ----------------- jsonencode([for s in var.names : upper(s)]) "[\"A\",\"B\",\"C\"]" [for s in var.names : upper(s)] [ "A", "B", "C", ] [for txt in var.names : upper(txt)] title("hello world") exit 3 rm -rf *.tf vi main.tf variable "names" { type = list(string) default = ["a", "b"] } output "A_upper_value" { value = [for v in var.names : upper(v)] } output "B_index_and_value" { value = [for i, v in var.names : "${i} is ${v}"] } output "C_make_object" { value = { for v in var.names : v => upper(v) } } output "D_with_filter" { value = [for v in var.names : upper(v) if v != "a"] } 4 # terraform apply -auto-approve Outputs: A_upper_value = [ "A", "B", ] B_index_and_value = [ "0 is a", "1 is b", ] C_make_object = { "a" = "A" "b" = "B" } D_with_filter = [ "B", terraform state list # terraform output terraform output A_upper_value [ "A", "B", ] terraform output D_with_filter [ "B", ] # terraform console > ----------------- var.names tolist([ "a", "b", ]) [for v in var.names : upper(v)] [ "A", "B", ] [for i, v in var.names : "${i} is ${v}"] { for v in var.names : v => upper(v) } [for v in var.names : upper(v) if v != "a"] exit 5 rm -rf *.tf vi main.tf variable "members" { type = map(object({ role = string })) default = { ab = { role = "member", group = "dev" } cd = { role = "admin", group = "dev" } ef = { role = "member", group = "ops" } } } output "A_to_tupple" { value = [for k, v in var.members : "${k} is ${v.role}"] } output "B_get_only_role" { value = { for name, user in var.members : name => user.role if user.role == "admin" } } output "C_group" { value = { for name, user in var.members : user.role => name... } } 6 # terraform apply -auto-approve Outputs: A_to_tupple = [ "ab is member", "cd is admin", "ef is member", ] B_get_only_role = { "cd" = "admin" } C_group = { "admin" = [ "cd", ] "member" = [ "ab", "ef", ] } terraform state list # terraform output terraform output A_upper_value // not fount # terraform console > ----------------- var.members [for k, v in var.members : "${k} is ${v.role}"] {for name, user in var.members : name => user.role} {for name, user in var.members : name => user.role if user.role == "admin"} {for name, user in var.members : user.role => name...} exit <10> dynamic = 리소스 반복은 아니고 , 리소스내 선언 되는 내부 구성블록의 반복이다. 1 rm -rf *.tf vi main.tf // 속성을 4개 사용하는 경우 = 4번 입력하여 사용한다. data "archive_file" "dotfiles" { type = "zip" output_path = "${path.module}/dotfiles.zip" source { content = "hello a" filename = "${path.module}/a.txt" } source { content = "hello b" filename = "${path.module}/b.txt" } source { content = "hello c" filename = "${path.module}/c.txt" } } 2 # terraform init -upgrade terraform apply -auto-approve terraform state list data.archive_file.dotfiles terraform state show data.archive_file.dotfiles ls *.zip dotfiles.zip unzip dotfiles.zip Archive: dotfiles.zip inflating: a.txt inflating: b.txt inflating: c.txt ls *.txt cat a.txt ; echo hello a 3 dynamic으로 재구성 해보자~~~~ rm -rf * vi main.tf variable "names" { default = { a = "hello a" b = "hello b" c = "hello c" } } data "archive_file" "dotfiles" { type = "zip" output_path = "${path.module}/dotfiles.zip" dynamic "source" { for_each = var.names content { content = source.value filename = "${path.module}/${source.key}.txt" } } } 4 # terraform apply -auto-approve terraform state list terraform state show data.archive_file.dotfiles type = "zip" source { content = "hello a" filename = "./a.txt" } source { content = "hello b" filename = "./b.txt" } source { content = "hello c" filename = "./c.txt" } } ls *.zip dotfiles.zip