brunch
매거진 테라폼 AWS

32탄-6. 2주차-테라폼-반복문

by Master Seo


<1> 명령서버 생성

<2> 테라폼은 반복문이 있다. - count 사용 (실습)

<3> 파일 5개 만드는것 테스트 (실습)

<4> list 형태의 배열을 활용한 반복문 동작 구성 (실습)

<5> 중간값을 변경할 경우, for_each 사용을 권장한다. (실습)

<6> iam user 3명 만들어보자. = count 사용 (실습)

<7> count 입력 변수를 통해 IAM 사용자 생성 (실습)

<8> for_each 입력 변수를 통해 IAM 사용자 생성 (실습)

<9> for 문= content의 값 정의에 for 구문을 사용하여 내부 값을 일괄적으로 변경 (실습)

<10> dynamic = 리소스 반복은 아니고 , 리소스내 선언 되는 내부 구성블록의 반복이다.(실습)




<1> 명령서버 생성


사전 준비

aws로그인 https://console.aws.amazon.com/

명령서버에서 사용할 pem키 필요

access-key 필요

secret-key 필요

cloudformation으로 명령 서버 1대를 만든다.





<2> 테라폼은 반복문이 있다. - count 사용 (실습)


1

참고 자료

https://www.youtube.com/playlist?list=PL1mta2YyMpPXxFGBtSJyvoKnWTqZslm5p



aws cloudformation ?

기존 반복문이 없어 EC2를 5대 배포해야 하면, 코드 줄이 길어졌다.

반복문 ForEach 제공 시작 2023년 7월 26일

https://aws.amazon.com/ko/about-aws/whats-new/2023/07/accelerate-cloudformation-authoring-experience-looping-function/



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개



# 테라폼 콘솔로 상세 내역을 찍어보자~


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



vi 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 타입인 경우 중간에 값이 삭제되면 인덱스가 줄어들어 의도했던 중간 값에 대한 리소스만 삭제되는 것이 아니라 이후의 정의된 리소스들도 삭제되고 재생성된다


rm -rf *.tf


vi main.tf


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 *


vi main.tf


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명 생성 형식


vi iam.tf


provider "aws" {

region = "us-east-2"

}


resource "aws_iam_user" "example" {

name = "1000"

}



terraform init && terraform plan

terraform apply -auto-approve


terraform destroy -auto-approve



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





다음과정

https://brunch.co.kr/@topasvga/3370




몰아보기

https://brunch.co.kr/@topasvga/3347



terraform.png


다음은 주말 CloudNet 테라폼 스터디 내용 참고하여 정리한 부분입니다.

https://gasidaseo.notion.site/gasidaseo/CloudNet-Blog-c9dfa44a27ff431dafdd2edacc8a1863



감사합니다.

keyword
매거진의 이전글32탄-5. 2주차-테라폼-local=코드내 사용