안녕하세요 늑대양 입니다
Terraform 관련 기초 스터디(Terraform 101 Study)를 참여하게되어 해당 내용들을 주차별로 정리하고자 합니다.
해당 스터디는 CloudNet@ 팀의 Gasida 님이 호스트를 맡아주셨으며,
https://gasidaseo.notion.site/CloudNet-Blog-c9dfa44a27ff431dafdd2edacc8a1863
스터디 메인 material 은 "테라폼으로 시작하는 IaC" 도서를 기준으로 진행됩니다.
https://www.yes24.com/Product/Goods/119179333
아래의 내용은 2주차 - 기본 사용 2/3 내용을 다루고 있습니다.
2주차 - 기본 사용 2/3
3.5 데이터 소스
데이터 소스는 테라폼으로 정의되지 않은 외부 리소스 또는 저장된 정보를 테라폼 내에서 참조할 때 사용한다
데이터 소스 구성
- 데이터 소스 블록은 data 로 시작, 이후 ‘데이터 소스 유형’을 정의 ← Resource 블록 정의와 유사
- 데이터 소스 유형은 첫 번째 _를 기준으로 앞은 프로바이더 이름, 뒤는 프로바이더에서 제공하는 리소스 유형을 의미
- 데이터 소스 유형을 선언한 뒤에는 고유한 이름을 붙인다. 리소스의 이름과 마찬가지로 이름은 동일한 유형에 대한 식별자 역할을 하므로 중복될 수 없음
- 이름 뒤에는 데이터 소스 유형에 대한 구성 인수들은 { } 안에 선언한다. 인수가 필요하지 않은 유형도 있지만, 그때에도 { } 는 입력 필요
# main.tf
data "local_file" "abc" {
filename = "${path.module}/abc.txt"
}
- 데이터 소스를 정의할 때 사용 가능한 메타인수는 다음과 같다.
- depends_on : 종속성을 선언하며, 선언된 구성요소와의 생성 시점에 대해 정의
- count : 선언된 개수에 따라 여러 리소스를 생성
- for_each : map 또는 set 타입의 데이터 배열의 값을 기준으로 여러 리소스를 생성
- lifecycle : 리소스의 수명주기 관리
데이터 소스 속성 참조
# Terraform Code
data "<리소스 유형>" "<이름>" {
<인수> = <값>
}
# 데이터 소스 참조
data.<리소스 유형>.<이름>.<속성>
# Declare the data source
# AWS 환경에서 사용 가능한 AZ 정의
data "aws_availability_zones" "available" {
state = "available"
}
# 정의한 AZ 데이터 소스 의 첫 번째 값 속성 참조
resource "aws_subnet" "primary" {
availability_zone = data.aws_availability_zones.available.names[0]
# e.g. ap-northeast-2a
}
# 정의한 AZ 데이터 소스 의 두 번째 값 속성 참조
resource "aws_subnet" "secondary" {
availability_zone = data.aws_availability_zones.available.names[1]
# e.g. ap-northeast-2b
}
# main.tf
data "aws_availability_zones" "seoul" {
state = "available"
}
# main.tf
resource "local_file" "abc" {
content = "123!"
filename = "${path.module}/abc.txt"
}
data "local_file" "abc" {
filename = local_file.abc.filename
}
resource "local_file" "def" {
content = data.local_file.abc.content
filename = "${path.module}/def.txt"
}
# graph 확인
terraform graph > graph.dot
3.6 입력 변수 Variable
입력 변수는 인프라를 구성하는 데 필요한 속성 값을 정의해 코드의 변경 없이 여러 인프라를 생성하는 데 목적이 있다.
테라폼에서는 이것을 입력 변수 Input Variables 로 정의한다.
변수 선언 방식
# variable 블록 선언의 예
variable "<이름>" {
<인수> = <값>
}
variable "image_id" {
type = string
}
- 변수는 variable로 시작되는 블록으로 구성
- 변수 블록 뒤의 이름 값은 동일 모듈 내 모든 변수 선언에서 고유해야 하며, 해당 이름으로 다른 코드 내에서 참조
- 테라폼 예약 변수 이름으로 사용 불가능 :
- source
- version
- providers
- count
- for_each
- lifecycle
- depends_on
- locals
- 변수 정의 시 사용 가능한 메타인수
- default : 변수 값을 전달하는 여러 가지 방법을 지정하지 않으면 기본값이 전달됨, 기본값이 없으면 대화식으로 사용자에게 변수에 대한 정보를 물어봄
- type : 변수에 허용되는 값 유형 정의, string number bool list map set object tuple 와 유형을 지정하지 않으면 any 유형으로 간주
- description : 입력 변수의 설명
- validation : 변수 선언의 제약조건을 추가해 유효성 검사 규칙을 정의 - 링크
- sensitive : 민감한 변수 값임을 알리고 테라폼의 출력문에서 값 노출을 제한 (암호 등 민감 데이터의 경우) - 링크
- nullable : 변수에 값이 없어도 됨을 지정 - Link
변수 유형
- 기본 유형
- string : 글자 유형
- number : 숫자 유형
- bool : true 또는 false
- any : 명시적으로 모든 유형이 허용됨을 표시
- 집합 유형
- list (<유형>): 인덱스 기반 집합
- map (<유형>): 값 = 속성 기반 집합이며 키값 기준 정렬
- set (<유형>): 값 기반 집합이며 정렬 키값 기준 정렬
- object ({<인수 이름>=<유형>, …})
- tuple ([<유형>, …])
- list 와 set 은 선언하는 형태가 비슷하지만 참조 방식이 인덱스와 키로 각각 차이가 존재
- map 과 set 의 경우 선언된 값이 정렬되는 특징을 가짐
# 조건 결합 사용 가능
variable "list_numeric_example" {
description = "An example of a numeric list in Terraform"
type = list(number)
default = [1, 2, 3]
}
# map(string)
variable "map_example" {
description = "An example of a map in Terraform"
type = map(string)
default = {
key1 = "value1"
key2 = "value2"
key3 = "value3"
}
}
# object 또는 tuple 제약 조건을 사용하여 보다 복잡한 구조적 유형(structural type) 작성 가능
variable "object_example" {
description = "An example of a structural type in Terraform"
type = object({
name = string
age = number
tags = list(string)
enabled = bool
})
default = {
name = "value1"
age = 42
tags = ["a", "b", "c"]
enabled = true
}
}
유효성 검사
- 입력되는 변수 타입 지정 이외, 사용자 지정 유효성 검사가 가능
- 변수 블록 내에 validation 블록에서 조건인 condition에 지정되는 규칙이 true 또는 false를 반환해야 하며, error_message는 condition 값의 결과가 false 인 경우 출력되는 메시지를 정의
- regex 함수는 대상의 문자열에 정규식을 적용하고 일치하는 문자열을 반환하는데, 여기에 can 함수를 함께 사용하면 정규식에 일치하지 않는 경우의 오류를 검출
- validation 블록은 중복으로 선언 가능
# main.tf
variable "image_id" {
type = string
description = "The id of the machine image (AMI) to use for the server."
validation {
condition = length(var.image_id) > 4
error_message = "The image_id value must exceed 4."
}
validation {
# regex(...) fails if it cannot find a match
condition = can(regex("^ami-", var.image_id))
error_message = "The image_id value must starting with \"ami-\"."
}
}
변수 참조
# main.tf
variable "my_password" {}
resource "local_file" "abc" {
content = var.my_password
filename = "${path.module}/abc.txt"
}
#
terraform init -upgrade && terraform apply -auto-approve
var.my_password
Enter a value: qwe123
...
# 확인
terraform state list
terraform state show local_file.abc
cat abc.txt ; echo
# 해당 파일에 다른 내용으로 변경해보기
terraform apply -auto-approve
var.my_password
Enter a value: t101mypss
...
# 확인
cat abc.txt ; echo
민감한 변수 취급
# main.tf
variable "my_password" {
default = "password"
sensitive = true
}
resource "local_file" "abc" {
content = var.my_password
filename = "${path.module}/abc.txt"
}
변수 입력 방식과 우선 순위
https://dev.classmethod.jp/articles/terraform-cli-vars-file-terraform-tfvars/
- variable의 목적은 코드 내용을 수정하지 않고 테라폼의 모듈적 특성을 통해 입력되는 변수로 재사용성을 높이는 데 있음
- 특히 입력 변수라는 명칭에 맞게 사용자는 프로비저닝 실행 시에 원하는 값으로 변수에 정의 가능
- 선언되는 방식에 따라 변수의 우선순위가 있으므로, 이를 적절히 사용해 로컬 환경과 빌드 서버 환경에서의 정의를 다르게 하거나, 프로비저닝 파이프라인을 구성하는 경우 외부 값을 변수에 지정 가능
- 우선 순위 수준 (숫자가 작을 수록 우선순위는 낮아짐)
- [우선순위 수준 1] 실행 후 입력
- [우선순위 수준 2] variable 블록의 default 값
- [우선순위 수준 3] 환경 변수 (TF_VAR 변수 이름)
- [우선순위 수준 4] terraform.tfvars에 정의된 변수 선언
- [우선순위 수준 5] *.auto.tfvars에 정의된 변수 선언
- [우선순위 수준 6] *.auto.tfvars.json에 정의된 변수 선언
- [우선순위 수준 7] CLI 실행 시 -var 인수에 지정 또는 -var-file로 파일 지정
실습1 . VPC +보안그룹 + EC2 배포
목표 : default VPC 대신 직접 VPC를 만들고, 해당 VPC내에 EC2 1대를 배포
VPC 배포
# vpc.tf
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_vpc" "myvpc" {
cidr_block = "10.10.0.0/16"
tags = {
Name = "t101-study"
}
}
# main.tf
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_vpc" "myvpc" {
cidr_block = "10.10.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "t101-study"
}
}
# vpc.tf
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_vpc" "wolf-sheep-vpc" {
cidr_block = "10.10.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "t101-study"
}
}
resource "aws_subnet" "wolf-sheep-subnet1" {
vpc_id = aws_vpc.wolf-sheep-vpc.id
cidr_block = "10.10.1.0/24"
availability_zone = "ap-northeast-2a"
tags = {
Name = "t101-subnet1"
}
}
resource "aws_subnet" "wolf-sheep-subnet2" {
vpc_id = aws_vpc.wolf-sheep-vpc.id
cidr_block = "10.10.2.0/24"
availability_zone = "ap-northeast-2c"
tags = {
Name = "t101-subnet2"
}
}
output "aws_vpc_id" {
value = aws_vpc.wolf-sheep-vpc.id
}
# main.tf
# igw 추가
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_vpc" "wolf-sheep-vpc" {
cidr_block = "10.10.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "t101-study"
}
}
resource "aws_subnet" "wolf-sheep-subnet1" {
vpc_id = aws_vpc.wolf-sheep-vpc.id
cidr_block = "10.10.1.0/24"
availability_zone = "ap-northeast-2a"
tags = {
Name = "t101-subnet1"
}
}
resource "aws_subnet" "wolf-sheep-subnet2" {
vpc_id = aws_vpc.wolf-sheep-vpc.id
cidr_block = "10.10.2.0/24"
availability_zone = "ap-northeast-2c"
tags = {
Name = "t101-subnet2"
}
}
resource "aws_internet_gateway" "wolf-sheep-igw" {
vpc_id = aws_vpc.wolf-sheep-vpc.id
tags = {
Name = "t101-igw"
}
}
output "aws_vpc_id" {
value = aws_vpc.wolf-sheep-vpc.id
}
# vpc.tf
# IGW 인터넷 게이트웨이로 전달하는 디폴트 라우팅 정보 추가
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_vpc" "wolf-sheep-vpc" {
cidr_block = "10.10.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "t101-study"
}
}
resource "aws_subnet" "wolf-sheep-subnet1" {
vpc_id = aws_vpc.wolf-sheep-vpc.id
cidr_block = "10.10.1.0/24"
availability_zone = "ap-northeast-2a"
tags = {
Name = "t101-subnet1"
}
}
resource "aws_subnet" "wolf-sheep-subnet2" {
vpc_id = aws_vpc.wolf-sheep-vpc.id
cidr_block = "10.10.2.0/24"
availability_zone = "ap-northeast-2c"
tags = {
Name = "t101-subnet2"
}
}
resource "aws_internet_gateway" "wolf-sheep-igw" {
vpc_id = aws_vpc.wolf-sheep-vpc.id
tags = {
Name = "t101-igw"
}
}
resource "aws_route_table" "wolf-sheep-rt" {
vpc_id = aws_vpc.wolf-sheep-vpc.id
tags = {
Name = "t101-rt"
}
}
resource "aws_route_table_association" "wolf-sheep-rtassociation1" {
subnet_id = aws_subnet.wolf-sheep-subnet1.id
route_table_id = aws_route_table.wolf-sheep-rt.id
}
resource "aws_route_table_association" "wolf-sheep-rtassociation2" {
subnet_id = aws_subnet.wolf-sheep-subnet2.id
route_table_id = aws_route_table.wolf-sheep-rt.id
}
resource "aws_route" "wolf-sheep-defaultroute" {
route_table_id = aws_route_table.wolf-sheep-rt.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.wolf-sheep-igw.id
}
output "aws_vpc_id" {
value = aws_vpc.wolf-sheep-vpc.id
}
보안그룹/EC2 배포
# sg.tf
resource "aws_security_group" "wolf-sheep-sg" {
vpc_id = aws_vpc.wolf-sheep-vpc.id
name = "T101 SG"
description = "T101 Study SG"
}
resource "aws_security_group_rule" "wolf-sheep-sginbound" {
type = "ingress"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.wolf-sheep-sg.id
}
resource "aws_security_group_rule" "wolf-sheep-sgoutbound" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.wolf-sheep-sg.id
}
output "aws_security_group_id" {
value = aws_security_group.wolf-sheep-sg.id
}
# ec2.tf
data "aws_ami" "wolf-sheep-amazonlinux2" {
most_recent = true
filter {
name = "owner-alias"
values = ["amazon"]
}
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-ebs"]
}
owners = ["amazon"]
}
resource "aws_instance" "wolf-sheep-ec2" {
depends_on = [
aws_internet_gateway.wolf-sheep-igw
]
ami = data.aws_ami.wolf-sheep-amazonlinux2.id
associate_public_ip_address = true
instance_type = "t2.micro"
vpc_security_group_ids = ["${aws_security_group.wolf-sheep-sg.id}"]
subnet_id = aws_subnet.wolf-sheep-subnet1.id
user_data = <<-EOF
#!/bin/bash
wget https://busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/busybox-x86_64
mv busybox-x86_64 busybox
chmod +x busybox
RZAZ=$(curl http://169.254.169.254/latest/meta-data/placement/availability-zone-id)
IID=$(curl 169.254.169.254/latest/meta-data/instance-id)
LIP=$(curl 169.254.169.254/latest/meta-data/local-ipv4)
echo "<h1>RegionAz($RZAZ) : Instance ID($IID) : Private IP($LIP) : Web Server</h1>" > index.html
nohup ./busybox httpd -f -p 80 &
EOF
user_data_replace_on_change = true
tags = {
Name = "t101-myec2"
}
}
output "myec2_public_ip" {
value = aws_instance.wolf-sheep-ec2.public_ip
description = "The public IP of the Instance"
}
# 실습 완료 후, 리소스 삭제
terraform destroy -auto-approve
3.7 local 지역 값
https://developer.hashicorp.com/terraform/language/values/locals
- 코드 내에서 사용자가 지정한 값 또는 속성 값을 가공해 참조 가능한 local (지역 값)은 외부에서 입력되지 않고, 코드 내에서만 가공되어 동작하는 값을 선언
- ‘local’은 입력 변수와 달리 선언된 모듈 내에서만 접근 가능하고, 변수처럼 실행 시에 입력받을 수 없음
- 로컬은 사용자가 테라폼 코드를 구현할 때 값이나 표현식을 반복적으로 사용할 수 있는 편의를 제공한다. 하지만 빈번하게 여러 곳에서 사용되는 경우 실제 값에 대한 추적이 어려워져 유지 관리 측면에서 부담이 발생할 수 있으므로 주의 필요
local 선언
# main.tf
variable "prefix" {
default = "hello"
}
locals {
name = "terraform"
content = "${var.prefix} ${local.name}"
my_info = {
age = 20
region = "KR"
}
my_nums = [1, 2, 3, 4, 5]
}
- 로컬이 선언되는 블록은 locals로 시작
- 선언되는 인수에 표현되는 값은 상수만이 아닌 리소스의 속성, 변수의 값들도 조합해 정의 가능
- 동일한 tf 파일 내에서 여러 번 선언하는 것도 가능하고 여러 파일에 걸쳐 만드는 것도 가능
- 다만 locals에 선언한 로컬 변수 이름은 전체 루트 모듈 내에서 유일해야 함
- 정의되는 속성 값은 지정된 값의 형태에 따라 다양한 유형으로 정의 가능
local 참조
- 선언된 local 값은 local.<이름>으로 참조 가능
- 테라폼 구성 파일을 여러 개 생성해 작업하는 경우 서로 다른 파일에 선언되어 있더라도 다른 파일에서 참조 가능
# main.tf
variable "prefix" {
default = "hello"
}
locals {
name = "terraform"
}
resource "local_file" "abc" {
content = local.content
filename = "${path.module}/abc.txt"
}
# sub.tf
locals {
content = "${var.prefix} ${local.name}"
}
실습2. AWS IAM User 생성
# iamuser.tf
provider "aws" {
region = "ap-northeast-2"
}
locals {
name = "mytest"
team = {
group = "dev"
}
}
resource "aws_iam_user" "myiamuser1" {
name = "${local.name}1"
tags = local.team
}
resource "aws_iam_user" "myiamuser2" {
name = "${local.name}2"
tags = local.team
}
# 배포 및 확인
terraform init && terraform apply -auto-approve
terraform state list
terraform state show aws_iam_user.myiamuser1
terraform state show aws_iam_user.myiamuser2
# 특정 사용자(state) 삭제
terraform destroy -auto-approve -target=aws_iam_user.myiamuser1
# 리소스 삭제
terraform state list
terraform destroy -auto-approve
terraform state list
3.8 출력 output
https://developer.hashicorp.com/terraform/language/values/outputs
- 출력 값은 주로 테라폼 코드의 프로비저닝 수행 후의 결과 속성 값을 확인하는 용도로 사용
- 또한 프로그래밍 언어에서 코드 내 요소 간에 제한된 노출을 지원하듯, 테라폼 모듈 간, 워크스페이스 간 데이터 접근 요소로도 활용 가능
- 예를 들면 자바의 getter와 비슷한 역할이다. 출력 값의 용도는 다음과 같이 정의 가능
- 루트 모듈에서 사용자가 확인하고자 하는 특정 속성 출력
- 자식 모듈의 특정 값을 정의하고 루트 모듈에서 결과를 참조
- 서로 다른 루트 모듈의 결과를 원격으로 읽기 위한 접근 요소
output 선언
output "instance_ip_addr" {
value = "http://${aws_instance.server.private_ip}"
}
- 출력되는 값은 value의 값이며 테라폼이 제공하는 조합과 프로그래밍적인 기능들에 의해 원하는 값을 출력 가능
- 주의할 점은 output 결과에서 리소스 생성 후 결정되는 속성 값은 프로비저닝이 완료되어야 최종적으로 결과를 확인할 수 있고 terraform plan 단계에서는 적용될 값이 출력하지 않는다는 점
- 변수 정의 시 사용 가능한 메타인수는 다음과 같음
- description : 출력 값 설명
- sensitive : 민감한 출력 값임을 알리고 테라폼의 출력문에서 값 노출을 제한
- depends_on : value에 담길 값이 특정 구성에 종속성이 있는 경우 생성되는 순서를 임의로 조정
- precondition : 출력 전에 지정된 조건을 검증
output 활용 & abspath 함수
https://developer.hashicorp.com/terraform/language/functions/abspath
# main.tf
# abspath: 파일 시스템 경로를 포함하는 문자열을 가져와 절대 경로로 변환하는 함수
resource "local_file" "abc" {
content = "abc123"
filename = "${path.module}/abc.txt"
}
output "file_id" {
value = local_file.abc.id
}
output "file_abspath" {
value = abspath(local_file.abc.filename)
}
abs 함수
https://developer.hashicorp.com/terraform/language/functions/abs
3.9 반복문 (1/2)
list 형태의 값 목록이나 Key-Value 형태의 문자열 집합인 데이터가 있는 경우 동일한 내용에 대해 테라폼 구성 정의를 반복적으로 하지 않고 관리 가능
count 반복문
- 리소스 또는 모듈 블록에 count 값이 정수인 인수가 포함된 경우 선언된 정수 값만큼 리소스나 모듈을 생성하게 된다.
- count에서 생성되는 참조값은 count.index이며, 반복하는 경우 0부터 1씩 증가해 인덱스가 부여된다.
# 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
}
# main.tf 수정
resource "local_file" "abc" {
count = 5
content = "This is filename abc${count.index}.txt"
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
}
# main.tf
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"
}
- 실행 후 확인 : local_file.abc와 local_file.def는 var.name에 선언되는 값에 영향을 받아 동일한 갯수만큼 생성
- local_file.def의 경우 local_file.abc와 개수가 같아야 content에 선언되는 인수 값에 오류가 없을 것이므로 서로 참조되는 리소스와 모듈의 반복정의에 대한 공통의 영향을 주는 변수로 관리 가능
- count로 생성되는 리소스의 경우 <리소스 타입>.<이름>[<인덱스 번호>], 모듈의 경우 module.<모듈 이름>[<인덱스 번호>]로 해당 리소스의 값을 참조
- 단, 모듈 내에 count 적용이 불가능한 선언이 있으므로 주의 필요
- 예를 들어 provider 블록 선언부가 포함되어 있는 경우에는 count 적용이 불가능하다 → provider 분리
- 또한 외부 변수가 list 타입인 경우 중간에 값이 삭제되면 인덱스가 줄어들어 의도했던 중간 값에 대한 리소스만 삭제되는 것이 아니라 이후의 정의된 리소스들도 삭제되고 재생성
실습3. 반복문
실습1. IAM 사용자 3명 생성
# iam.tf
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_iam_user" "myiam" {
count = 3
name = "myuser.${count.index}"
}
# state 확인 및 리소스 삭제
terraform destroy -auto-approve
aws iam list-users | jq
실습2. count 입력 변수를 통해, IAM 사용자 생성
# variables.tf
variable "user_names" {
description = "Create IAM users with these names"
type = list(string)
default = ["gasida", "wolf", "sheep"]
}
- 테라폼에서 count 와 함께 배열 조회 구문과 length 함수를 사용해서 사용자들 생성 가능
- 배열 조회 구문 Array lookup syntax
- ARRAY[<INDEX>]
- 예를 들어 다음은 var.user_names 의 인덱스 1에서 요소를 찾는 방법
- var.user_names[1]
- length (내장) 함수 built-on function
- length(<ARRAY>)
- 주어진 ARRAY 의 항목 수를 반환하는 함수. 문자열 및 맵을 대상으로도 동작
- 배열 조회 구문 Array lookup syntax
# iam.tf 수정
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_iam_user" "myiam" {
count = length(var.user_names)
name = var.user_names[count.index]
}
- aws_iam_user.myiam 은 이제 IAM 사용자의 배열이므로 표준 구문을 사용하여 해당 리소스인 <PROVIDER>_<TYPE>.<NAME>.<ATTRIBUTE> 에서 속성을 읽은 대신 배열에서 인덱스를 지정해서 IAM 사용자를 명시 필요
- <PROVIDER>_<TYPE>.<NAME>[INDEX].ATTRIBUTE
# output.tf
output "first_arn" {
value = aws_iam_user.myiam[1].arn
description = "The ARN for the first user"
}
output "all_arns" {
value = aws_iam_user.myiam[*].arn
description = "The ARNs for all users"
}
- IAM 사용자 한명의 ARN과 사용자 전체의 ARN 을 출력 변수 outputs 으로 제공
- output.tf : IAM 사용자 전체의 ARN을 원하면 인덱스 대신 스플랫 splat 연산자인 * 를 사용
실습3. count 제약사항 - 인라인 블록 반복 X, 배열 중간 값을 변경할 때, 의도하지 않은 결과 발생
# variables.tf 수정
# 중간에 있는 "wolf" 삭제
variable "user_names" {
description = "Create IAM users with these names"
type = list(string)
default = ["gasida", "sheep"]
}
- 배열의 중간에 항목을 제거하면 모든 항목이 1칸씩 앞으로 당겨짐
- 테라폼이 인덱스 번호를 리소스 식별자로 보기 때문에 ‘인덱스 1에서는 계정 생성, 인덱스2에서는 계정 삭제한다’라고 해석
- 즉 count 사용 시 목록 중간 항목을 제거하면 테라폼은 해당 항목 뒤에 있는 모든 리소스를 삭제한 다음 해당 리소스를 처음부터 다시 만듬
# 실습 완료 후, 리소스 삭제
terraform destroy -auto-approve
aws iam list-users | jq
실습4. count 실습 (악분 님 제공)
https://github.com/sungwook-practice/t101-study/tree/main/count_vs_foreach
개인적으로 대부분의 상황에서 count 보다 for_each 를 선호..
# main.tf
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
tags = {
Name = "terraform VPC"
}
}
resource "aws_subnet" "main" {
count = length(var.subnets)
vpc_id = aws_vpc.main.id
cidr_block = var.subnets[count.index].cidr
availability_zone = var.subnets[count.index].az
tags = var.subnets[count.index].tags
}
output "myvpc_id" {
value = aws_vpc.main.id
}
# terraform.tfvars
vpc_cidr = "192.168.0.0/16"
subnets = [
{
cidr = "192.168.1.0/24",
az = "ap-northeast-2a",
tags = {
Name = "public-subnet"
Environment = "dev"
}
},
{
cidr = "192.168.2.0/24",
az = "ap-northeast-2c",
tags = {
Name = "private-subnet"
Environment = "dev"
}
}
]
# variables.tf
variable "vpc_cidr" {
type = string
}
variable "subnets" {
type = list(object({
cidr = string
az = string
tags = map(string)
}))
}
지금까지 Terraform 101 Study 2주차 내용이었습니다
테라폼을 학습하시는 분들에게 도움이 되었으면 좋을 것 같습니다 🤗
긴 글 읽어주셔서 감사드립니다
좋은 주말 보내세요
감사합니다 😍
'Terraform' 카테고리의 다른 글
Terraform 101 Study - 4주차 (0) | 2024.07.07 |
---|---|
Terraform 101 Study - 3주차 (0) | 2024.06.30 |
Terraform 101 Study - 1주차 (0) | 2024.06.16 |
[Terraform] 2장. 왜 테라폼인가? (77~82p) (0) | 2021.07.13 |
[Terraform] 2장. 왜 테라폼인가? (61~76p) (0) | 2021.07.13 |