#
terraform init -upgrade && terraform plan
│ Error: Invalid for_each argument
│
│ on main.tf line 2, in resource "aws_iam_user" "the-accounts":
│ 2: for_each = ["Todd", "James", "Alice", "Dottie"]
│
│ The given "for_each" argument value is unsuitable: the "for_each" argument must be a map, or
│ set of strings, and you have provided a value of type tuple.
# Jenkins 등에서 CD로 Terraform 수행 시, -no-color 활용
terraform plan -no-color
그리고 CONFIG 는 해당 리소스와 관련된 하나 이상의 인수로 구성되는데 CONFIG 내에서 each.key 또는 each.value 를 사용하여 COLLECTION 에서 현재 항목의 키와 값에 접근할 수 있다.
예를 들어 다음은 for_each 를 사용하여 3명의 IAM 사용자를 생성하는 방법
var.user_names 리스트를 집합(set)으로 변환하기 위해 toset 사용. for_each 는 리소스에 사용될 때는 set과 map만 지원.
for_each 가 이 집합을 반복하면 each.value 에서 각 사용자 이름을 사용할 수 있습니다.
main.tf : 일반적으로는 each.key 는 키/값 쌍 맵에서만 사용가능하지만, 사용자 이름은 each.key 에서도 사용할 수 있습니다.
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_iam_user" "myiam" {
for_each = toset(var.user_names)
name = each.value
}
variable "user_names" {
description = "Create IAM users with these names"
type = list(string)
default = ["gasida", "akbun", "ssoon"]
}
output "all_users" {
value = aws_iam_user.myiam
}
for_each 를 사용한 후에는 하나의 리소스 또는 count 를 사용한 것과 같은 리소스 배열이 되는 것이 아니리 리소스 맵 list into a set 이 됩니다.
init & plan & apply
#
terraform init -upgrade
terraform plan && terraform apply -auto-approve
# 확인
terraform state list
terraform output
aws iam list-users | jq
# 배포 결과 content만 필터링 확인
cat terraform.tfstate | grep -e key -e name -e "content:"
all_users 출력 변수가 for_each 의 키, 즉 사용자 이름을 키로 가지며 값이 해당 리소스의 전체 출력인 맵을 포함합니다.
for_each를 사용해리소스를맵으로 처리하면컬렉션중간의 항목도 안전하게 제거할 수 있어서,count로리소스를배열 처리보다 이점이 큽니다.
var.user_names 리스트 중간에 값을 제거하고 plan 으로 확인
**terraform plan
terraform apply -auto-approve
terraform state list**
aws iam list-users | jq
****# 배포 결과 content만 필터링 확인
cat terraform.tfstate | grep -e key -e name -e "content:"
cat terraform.tfstate.backup | grep -e key -e name -e "content:"
terraform plan
terraform apply -auto-approve
terraform state list
aws iam list-users | jq
# 배포 결과 content만 필터링 확인
cat terraform.tfstate | grep -e key -e name -e "content:"
cat terraform.tfstate.backup | grep -e key -e name -e "content:"
이제 주변 모든 리소스를 옮기지 않고 정확히 목표한 리소스만 삭제가 됩니다.
따라서 리소스의 구별되는 여러 복사본을 만들 때는 count 대신 for_each 를 사용하는 것이 바람직합니다.
list 타입과 each object 접근 비교
list 타입
sample[0] = aaa / sample[1] = bbb / sample[2] = ccc 와 같이 index 형식으로 접근
title converts the first letter of each word in the given string to uppercase.
#
terraform apply -auto-approve
terraform state list
terraform output --raw file_content | jq
ls *.txt
cat abc.txt ;echo
# 참고 jsonencode Function
terraform console
-----------------
jsonencode([for s in var.names : upper(s)])
[for s in var.names : upper(s)]
[for txt in var.names : upper(txt)]
title("hello world")
exit
-----------------
for 구문을 사용하는 몇 가지 규칙은 다음과 같다
list 유형의 경우 반환 받는 값이 하나로 되어 있으면 값을, 두 개의 경우 앞의 인수가 인덱스를 반환하고 뒤의 인수가 값을 반환
관용적으로 인덱스는 i, 값은 v로 표현
map 유형의 경우 반환 받는 값이 하나로 되어 있으면 키를, 두 개의 경우 앞의 인수가 키를 반환하고 뒤의 인수가 값을 반환
관용적으로 키는 k, 값은 v로 표현
결과 값은 for 문을 묶는 기호가 [ ]인 경우 tuple로 반환되고 { }인 경우 object 형태로 반환
{ } 형식을 사용해 object 형태로 결과를 반환하는 경우 키 값은 고유해야 하므로 값 뒤에 그룹화 모드 심볼(…)를 붙여서 키의 중복을 방지(SQL의 group by 문 또는 Java의 MultiValueMap과 같은 개념)
if 구문을 추가해 조건 부여 가능
main.tf 파일 수정 : list 유형에 대한 for 구문 처리의 몇 가지 예를 확인
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"]
}
실행 후 확인
#
terraform apply -auto-approve
terraform state list
#
terraform output
terraform output A_upper_value
terraform output D_with_filter
#
terraform console
-----------------
var.names
type(var.names)
# 리턴 타입 tuple : 값 v
[for v in var.names : upper(v)]
type([for v in var.names : upper(v)])
# 리턴 타입 tuple : 인덱스 i, 값 v
[for i, v in var.names : "${i} is ${v}"]
type([for i, v in var.names : "${i} is ${v}"])
# 리턴 타입 object : object 형태의 경우 키와 값에 대한 쌍은 ⇒ 기호로 구분
{ for v in var.names : v => upper(v) }
type({ for v in var.names : v => upper(v) })
# 조건문 활용 : f 구문을 추가해 조건 부여 가능
[for v in var.names : upper(v) if v != "a"]
type([for v in var.names : upper(v) if v != "a"])
exit
-----------------
main.tf 파일 수정 : map 유형에 대한 for 구문 처리의 몇 가지 예를 확인
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...
}
}
실행 후 확인
#
terraform apply -auto-approve
terraform state list
#
terraform output
terraform output A_to_tupple
terraform output B_get_only_role
terraform output C_group
#
terraform console
-----------------
var.members
type(var.members)
[for k, v in var.members : "${k} is ${v.role}"]
type([for k, v in var.members : "${k} is ${v.role}"])
{for name, user in var.members : name => user.role}
type({for name, user in var.members : name => user.role})
{for name, user in var.members : name => user.role if user.role == "admin"}
# { } 형식을 사용해 object 형태로 결과를 반환하는 경우 키 값은 고유해야 하므로 값 뒤에 그룹화 모드 심볼(…)를 붙여서 키의 중복을 방지
{for name, user in var.members : user.role => name...}
exit
-----------------
#
terraform console
-----------------
var.fruits_set
var.fruits_list
var.fruits_map
type(var.fruits_set)
type(var.fruits_list)
type(var.fruits_map)
#
for item in var.fruits_set: item
[for item in var.fruits_set: item]
type([for item in var.fruits_set: item])
{for item in var.fruits_set: item}
{for key,value in var.fruits_set: key => value}
type({for key,value in var.fruits_set: key => value})
#
for item in var.fruits_list: item
[for item in var.fruits_list: item]
type([for item in var.fruits_list: item])
{for item in var.fruits_list: item}
{for key,value in var.fruits_list: key => value}
{for i, v in var.fruits_list: i => v}
type({for i, v in var.fruits_list: i => v})
#
for item in var.fruits_map: item
[for item in var.fruits_map: item]
type([for item in var.fruits_map: item])
{for item in var.fruits_map: item}
{for key,value in var.fruits_map: key => value}
{for k, v in var.fruits_map: k => v}
type({for k, v in var.fruits_map: k => v})
exit
-----------------
리턴타입 없이 조회를 했을 경우 error 발생
object type은 key와 value가 필요한데 현재 value(='item') 만을 지정해주었기 때문에 에러가 발생하는 것.object type으로 변환하기 위해 key와 value를 만들어 설정함.type 함수를 사용해 리턴타입이 어떤 타입인지를 확인할 수 있다.
#
terraform init -upgrade && terraform apply -auto-approve
terraform state list
terraform state show data.archive_file.dotfiles
ls *.zip
unzip dotfiles.zip
ls *.txt
cat a.txt ; echo
main.tf 파일 수정 : 리소스 내에 반복 선언 구성을 dynamic 블록으로 재구성
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"
}
}
}
실행 후 확인 : 동일한 결과가 기대되어 변경 사항이 없다!
#
terraform apply -auto-approve
terraform state list
terraform state show data.archive_file.dotfiles
ls *.zip