⭐ 가시다(gasida) 님이 진행하는 Terraform T101 4기 실습 스터디 게시글입니다.
책 '테라폼으로 시작하는 IaC' 를 기준으로 정리하였습니다.
게시글 상 소스코드, 사진에서 **굵게** 혹은 '''코드쉘''' 에 대한 부분이 들어가있을수도 있습니다.
3.5 Terraform 데이터 소스 구성
Data source Block: 이미 존재하는 외부 리소스를 참조하기 위해 사용하는 블록.
- 리소스를 생성하지 않고도, 기존 리소스의 속성을 가져와서 다른 리소스에 사용할 수 있다.
Data source block Format
data "{provider name}_{provider_type} "unique name" {
filename = "${path.module}/abc.txt"
}
정의할 때 사용가능한 메타인수
- depends_on: 종속성 선언, 구성요소와 생성시점에 대한 정의
- count: 선언된 개수에 따라 리소스 생성
- for_each: map or set 타입의 데이터 배열의 값을 기준으로 리소스를 생성
- lifecycle: 리소스의 생명주기 관리
실습 환경구성
mkdir 3.5 && cd 3.5
touch main.tf
실습
#vi main.tf
data "local_file" "abc" {
filename = "${path.module}/abc.txt"
}
#:wq!
terraform state list
#data.local_file.abc
#테라폼 콘솔에서 확인
terraform console
> data.local_file.abc
{
"content" = <<-EOT
t101 study - 2week
EOT
"content_base64" = "dDEwMSBzdHVkeSAtIDJ3ZWVrCg=="
"content_base64sha256" = "GTJyISrUCoVCp4KIUl1e1FHeyGeu09qTsFZLsNeCTz8="
"content_base64sha512" = "tp0cRC/xBlJH4txY6Skb/FmexTrlNXq7OLAJXEYJwEvW+obgAeSYPtmPhvYhgwsXTWJ2jpTJkkiqclybnVu8MA=="
"content_md5" = "983a604da76dd282581c6d411f3b8291"
"content_sha1" = "75a47c30031f5fa02f6c4b5170e9539324f95c87"
"content_sha256" = "193272212ad40a8542a78288525d5ed451dec867aed3da93b0564bb0d7824f3f"
"content_sha512" = "b69d1c442ff1065247e2dc58e9291bfc599ec53ae5357abb38b0095c4609c04bd6fa86e001e4983ed98f86f621830b174d62768e94c99248aa725c9b9d5bbc30"
"filename" = "./abc.txt"
"id" = "75a47c30031f5fa02f6c4b5170e9539324f95c87"
}
>
3.5 Terraform 데이터 소스 속성 참조
데이터 소스로 읽는 대상을 참조하는 방식은 Resource 블록과 구별되게 data. 를 붙인다.
#DataSource 블록
data "{Resource}" "{Unique name}" {
{somethng}
}
#접근
data.{Resource}.{Unique name}.{속성}
#Resource 블록
resource "{Resource"} "{Unique name}" {
{something}
}
#접근
{Resource}.{Unique name}.{속성}
e.g.
# Declare the data source
data "aws_availability_zones" "available" {
state = "available"
}
resource "aws_subnet" "primary" {
availability_zone = data.aws_availability_zones.available.names[0]
# e.g. ap-northeast-2a
}
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"
}
terraform init -upgrade && terraform plan && terraform apply -auto-approve
Initializing the backend...
Initializing provider plugins...
- Finding latest version of hashicorp/local...
- Finding latest version of hashicorp/aws...
- Using previously-installed hashicorp/local v2.5.1
- Installing hashicorp/aws v5.54.1...
- Installed hashicorp/aws v5.54.1 (signed by HashiCorp)
Terraform has made some changes to the provider dependency selections recorded
in the .terraform.lock.hcl file. Review those changes and commit them to your
version control system if they represent changes you intended to make.
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
data.aws_availability_zones.seoul: Reading...
data.aws_availability_zones.seoul: Read complete after 0s [id=ap-northeast-2]
No changes. Your infrastructure matches the configuration.
Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.
data.aws_availability_zones.seoul: Reading...
data.aws_availability_zones.seoul: Read complete after 0s [id=ap-northeast-2]
No changes. Your infrastructure matches the configuration.
Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
#없는 이름으로 접근
terraform state show data.aws_availability_zones.available
No instance found for the given address!
This command requires that the address references one specific instance.
To view the available instances, use "terraform state list". Please modify
the address to reference a specific instance.
#정의된 이름으로 접근
terraform state show data.aws_availability_zones.seoul
# data.aws_availability_zones.seoul:
data "aws_availability_zones" "seoul" {
group_names = [
"ap-northeast-2",
]
id = "ap-northeast-2"
names = [
"ap-northeast-2a",
"ap-northeast-2b",
"ap-northeast-2c",
"ap-northeast-2d",
]
state = "available"
zone_ids = [
"apne2-az1",
"apne2-az2",
"apne2-az3",
"apne2-az4",
]
}
테라폼 콘솔에서 확인
terraform console
> data.aws_availability_zones.seoul.id
"ap-northeast-2"
> data.aws_availability_zones.seoul.names
tolist([
"ap-northeast-2a",
"ap-northeast-2b",
"ap-northeast-2c",
"ap-northeast-2d",
])
추가실습 : 생성 종속성 확인
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"
}
- 리소스블록(local_file abc) 을 생성
- 리소스블록(local_file abc) 를 참조 → 데이터블록(local_file abc) 를 생성
- 데이터블록(local_file abc) 를 참조 → 리소스블록(local_file abc) 를 생성
확인
terraform apply -auto-approve
terraform graph > graph.dot
#cat graph.dot
digraph G {
rankdir = "RL";
node [shape = rect, fontname = "sans-serif"];
"data.local_file.abc" [label="data.local_file.abc"];
"local_file.abc" [label="local_file.abc"];
"local_file.def" [label="local_file.def"];
"data.local_file.abc" -> "local_file.abc";
"local_file.def" -> "data.local_file.abc";
}
3.6 Terraform 입력 변수(Input Variable)
사용목적 : 동일한 소스코드에 입력변수만 다르게 구성하여, 서로 다른 인프라를 다양하게 구성하기 위함이다.
실습 환경
mkdir 3.6 && cd 3.6
touch main.tf
변수블록 : Variable 블록으로 구성
- 변수 블록 뒤 이름값은 모듈 내 모든 변수선언에서 고유함.
- 이름으로 다른코드 내에서 참조함
#Variable Block 선언
variable "{name"} {
{something}
}
#e.g.
variable "image_id" }
type = string
}
사용불가능한 이름 : 테라폼 예약변수
- source, version, providers, count, for_each, lifecycle, depends_on, locals
변수 정의시 사용가능 한 메타인수
- default: 변수 값을 전달하는 방법을 지정하지 않은경우에 디폴트값 사용됨
- type: 변수에 허용되는 값 유형 정의(string, number, bool …). 지정하지 않으면 무조건 any 로 간주
- description: 입력 변수에 대한 설명
- validation: 변수 선언의 제약조건을 추가. 유효성 검사 규칙을 정의
- senstive: 민감한 변수 값임을 선언. plan, apply 할때 값을 노출하지않음
- nullable: 변수에 값이 없어도 됨을 지정함
변수유형
기본유형: string, number, bool, any
집합유형: list(인덱스 기반), map(값=속성 기반), set(값 기반), object({인수이름=유형}기반), tupe({유형}기반)
- list, set 은 선언하는 형태가 비슷. 참조방식이 list(인덱스), set(키) 기반이다.
- map, set 은 선언된 값이 정렬되는 특징을 가진다.
e.g.
#number
variable "number_example" {
description = "An example of a number variable in Terraform"
type = number
default = 42
}
#list
variable "list_example" {
description = "An example of a list in Terraform"
type = list
default = ["a", "b", "c"]
}
#map(string)
variable "map_example" {
description = "An example of a map in Terraform"
type = map(string)
default = {
key1 = "value1"
key2 = "value2"
key3 = "value3"
}
}
유효성 검사 validation 블록 : variable block 내부에서 사용
- 조건인 condition 에 지정되는 규칙이 true/false 를 반환한다.
- error_message 는 condition 값의 결과가 false 인 경우, 출력되는 메세지를 정의한다.
- regex 함수 : 대상의 문자열에 정규식 적용, 문자열 반환. 오류검출 가능
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 #variable블록이라서 var.image_id로 접근
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-\"."
}
}
terraform apply -auto-approve
var.image_id
The id of the machine image (AMI) to use for the server.
Enter a value: jjongguet
╷
│ Error: Invalid value for variable
│
│ on main.tf line 1:
│ 1: variable "image_id" {
│ ├────────────────
│ │ var.image_id is "jjongguet"
│
│ The image_id value must starting with "ami-".
│
│ This was checked by the validation rule at main.tf:10,3-13.
╵
terraform apply -auto-approve
var.image_id
The id of the machine image (AMI) to use for the server.
Enter a value: ami-123
No changes. Your infrastructure matches the configuration.
Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
변수참조형식: var.{name} 으로 참조
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
Initializing the backend...
Initializing provider plugins...
- Finding latest version of hashicorp/local...
- Using previously-installed hashicorp/local v2.5.1
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
var.my_password
Enter a value: t1014 #입력
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# local_file.abc will be created
+ resource "local_file" "abc" {
+ content = "t1014"
+ content_base64sha256 = (known after apply)
+ content_base64sha512 = (known after apply)
+ content_md5 = (known after apply)
+ content_sha1 = (known after apply)
+ content_sha256 = (known after apply)
+ content_sha512 = (known after apply)
+ directory_permission = "0777"
+ file_permission = "0777"
+ filename = "./abc.txt"
+ id = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
local_file.abc: Creating...
local_file.abc: Creation complete after 0s [id=884f7d5228262e00449433a17fe5ab11c0221abd]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
#확인
terraform state list
local_file.abc
terraform state show local_file.abc
# local_file.abc:
resource "local_file" "abc" {
content = "t1014" #입력한 값
content_base64sha256 = "PQBjZEdN9uugfTQpT6fLo6+YwFSuMhoHapv162Bo6jg="
content_base64sha512 = "Nk9R6u75Z0rPGp68vH2zKEOEWjXlnW/WoqLSoHesExfD+x9i5r8DYGh2Vmo3QFWMVqw9riwQ3cXPZrhNeZc91Q=="
content_md5 = "23c25a0e06972acea0fc9843f3c8b8cd"
content_sha1 = "884f7d5228262e00449433a17fe5ab11c0221abd"
content_sha256 = "3d006364474df6eba07d34294fa7cba3af98c054ae321a076a9bf5eb6068ea38"
content_sha512 = "364f51eaeef9674acf1a9ebcbc7db32843845a35e59d6fd6a2a2d2a077ac1317c3fb1f62e6bf03606876566a3740558c56ac3dae2c10ddc5cf66b84d79973dd5"
directory_permission = "0777"
file_permission = "0777"
filename = "./abc.txt"
id = "884f7d5228262e00449433a17fe5ab11c0221abd"
}
민감한 변수: senstive
- 기본 값 추가로 입력항목은 발생하지 않지만, 출력창에서 확인되진 않는다.
main.tf수정
variable "my_password" {
default = "password"
sensitive = true
}
resource "local_file" "abc" {
content = var.my_password
filename = "${path.module}/abc.txt"
}
#적용
terraform apply -auto-approve
local_file.abc: Refreshing state... [id=884f7d5228262e00449433a17fe5ab11c0221abd]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement
Terraform will perform the following actions:
# local_file.abc must be replaced
-/+ resource "local_file" "abc" {
# Warning: this attribute value will be marked as sensitive and will not
# display in UI output after applying this change.
~ content = (sensitive value) # forces replacement #안보이는거 확인
~ content_base64sha256 = "PQBjZEdN9uugfTQpT6fLo6+YwFSuMhoHapv162Bo6jg=" -> (known after apply)
~ content_base64sha512 = "Nk9R6u75Z0rPGp68vH2zKEOEWjXlnW/WoqLSoHesExfD+x9i5r8DYGh2Vmo3QFWMVqw9riwQ3cXPZrhNeZc91Q==" -> (known after apply)
~ content_md5 = "23c25a0e06972acea0fc9843f3c8b8cd" -> (known after apply)
~ content_sha1 = "884f7d5228262e00449433a17fe5ab11c0221abd" -> (known after apply)
~ content_sha256 = "3d006364474df6eba07d34294fa7cba3af98c054ae321a076a9bf5eb6068ea38" -> (known after apply)
~ content_sha512 = "364f51eaeef9674acf1a9ebcbc7db32843845a35e59d6fd6a2a2d2a077ac1317c3fb1f62e6bf03606876566a3740558c56ac3dae2c10ddc5cf66b84d79973dd5" -> (known after apply)
~ id = "884f7d5228262e00449433a17fe5ab11c0221abd" -> (known after apply)
# (3 unchanged attributes hidden)
}
Plan: 1 to add, 0 to change, 1 to destroy.
local_file.abc: Destroying... [id=884f7d5228262e00449433a17fe5ab11c0221abd]
local_file.abc: Destruction complete after 0s
local_file.abc: Creating...
local_file.abc: Creation complete after 0s [id=5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8]
Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
#확인
echo "local_file.abc.content" | terraform console
(sensitive value)
cat abc.txt ; echo
password
변수의 입력 방식과 우선순위
- variable 의 목적은 코드내용을 수정하지 않고, 테라폼의 모듈특성을 사용하여 코드의 재사용성을 높이는데에 있음
- 입력변수라는 명칭에 맞게, 프로비저닝 실행 시에 원하는 값으로 변수에 정의할 수 있다.
- 선언되는 방식에 따라, 변수의 우선순위가 존재함.
main.tf수정
variable "my_var" {}
resource "local_file" "abc" {
content = var.my_var
filename = "${path.module}/abc.txt"
}
우선순위수준
- 우선순위숫자가 낮을수록 우선순위도 낮다
우선순위 구분
- 우선순위1 : 실행 후 입력
- 우선순위2:
variable.default
- 우선순위3: 시스템환경변수 Prefix 의
TF_VAR_
- 우선순위4: 변수파일
terraform.tfvars
- 우선순위5:
*.auto.tfvars
에 정의된 변수 - 우선순위6:
*.auto.tfvars.json
에 정의된 변수 - 우선순위7: CLI실행시
-var
혹은-var-file
로 지정한 파일
⇒ CLI를 제외하고, 어디에서든 동일한 변수를 사용하고싶다면 *.auto.tfvars
와 *.auto.tfvars.json
파일을 사용해야한다.
스터디 실습1) VPC + 보안그룹 + EC2 배포
목표: default VPC 대신, 직접 VPC를 만들고, 해당 VPC 내에 EC2를 1대 배포
mkdir my-vpc-ec2 && cd my-vpc-ec2
touch vpc.tf
vpc.tf파일생성
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_vpc" "myvpc" {
cidr_block = "10.10.0.0/16"
tags = {
Name = "t101-study"
}
}
#배포
terraform init && terraform plan && terraform apply -auto-approve
AWS 콘솔에서 확인: DefaultVPC + 방금 추가한 VPC 가 생성되었다.
- DNS호스트이름 : 비활성화 확인
vpc.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"
}
}
#배포
terraform init && terraform plan && terraform apply -auto-approve
#배포리소스 확인
terraform state list
##aws_vpc.myvpc
- Change 항목에 있음을 확인가능함
AWS콘솔에서 확인
- DNS호스트 이름 : 비활성화 → 활성화됨 으로 변경확인가능.
다음실습(aws_subnet 추가실습) 을 하기 위해 aws_subnet 리소스블록의 공식독스 확인
- Resource: aws_subnet 공식독스: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet
공식독스에서 aws_subnet 리소스를 정의하기 위해서는 아래의형태로 정의하는걸 명시하고있습니다.
resource "aws_subnet" "main" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
tags = {
Name = "Main"
}
}
resource.aws_subnet.main.vpc_id 는 aws_vpc.main.id 값을 참조하여 사용하는데
aws_vpc 리소스의 공식독스(https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc) 에서 해당리소스에 해당하는 값이 무엇인지알 수있습니다.
aws_vpc.main.id 에서 aws_vpc.main 은 리소스블록에서 aws_vpc 이며 이름이 main인 블록을 지정합니다.
이때 .id 에 대해서는 어디서확인가능하냐면, 공식독스 하단의 Attribute Reference에서 확인가능합니다.
- .id 는 VPC의 ID값입니다.
vpc.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"
}
}
resource "aws_subnet" "mysubnet1" {
vpc_id = aws_vpc.myvpc.id
cidr_block = "10.10.1.0/24"
availability_zone = "ap-northeast-2a"
tags = {
Name = "t101-subnet1"
}
}
resource "aws_subnet" "mysubnet2" {
vpc_id = aws_vpc.myvpc.id
cidr_block = "10.10.2.0/24"
availability_zone = "ap-northeast-2c"
tags = {
Name = "t101-subnet2"
}
}
output "aws_vpc_id" {
value = aws_vpc.myvpc.id
}
#배포
terraform plan && terraform apply -auto-approve
#배포확인
terraform state list
aws_subnet.mysubnet1
aws_subnet.mysubnet2
aws_vpc.myvpc
#기타확인
terraform output
aws_vpc_id = "vpc-0770614c1a044a8fa"
#기타확인2
terraform output aws_vpc_id
"vpc-0770614c1a044a8fa"
종속성확인
terraform graph > graph.dot
vpc.tf코드수정: IGW 인터넷게이트웨이 추가
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"
}
}
resource "aws_subnet" "mysubnet1" {
vpc_id = aws_vpc.myvpc.id
cidr_block = "10.10.1.0/24"
availability_zone = "ap-northeast-2a"
tags = {
Name = "t101-subnet1"
}
}
resource "aws_subnet" "mysubnet2" {
vpc_id = aws_vpc.myvpc.id
cidr_block = "10.10.2.0/24"
availability_zone = "ap-northeast-2c"
tags = {
Name = "t101-subnet2"
}
}
resource "aws_internet_gateway" "myigw" {
vpc_id = aws_vpc.myvpc.id
tags = {
Name = "t101-igw"
}
}
output "aws_vpc_id" {
value = aws_vpc.myvpc.id
}
#배포
terraform plan && terraform apply -auto-approve
#배포확인
terraform state list
aws_internet_gateway.myigw
aws_subnet.mysubnet1
aws_subnet.mysubnet2
aws_vpc.myvpc
#특정리소스 배포상태확인
terraform state show aws_internet_gateway.myigw
# aws_internet_gateway.myigw:
resource "aws_internet_gateway" "myigw" {
arn = "arn:aws:ec2:ap-northeast-2:909418839780:internet-gateway/igw-0ab3457cd56fa54c6"
id = "igw-0ab3457cd56fa54c6"
owner_id = "909418839780"
tags = {
"Name" = "t101-igw"
}
tags_all = {
"Name" = "t101-igw"
}
vpc_id = "vpc-0770614c1a044a8fa"
}
AWS 콘솔에서 확인 가능
vpc.tf코드수정: IGW 인터넷게이트웨이로 전달하는 디폴트 라우팅정보추가
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"
}
}
resource "aws_subnet" "mysubnet1" {
vpc_id = aws_vpc.myvpc.id
cidr_block = "10.10.1.0/24"
availability_zone = "ap-northeast-2a"
tags = {
Name = "t101-subnet1"
}
}
resource "aws_subnet" "mysubnet2" {
vpc_id = aws_vpc.myvpc.id
cidr_block = "10.10.2.0/24"
availability_zone = "ap-northeast-2c"
tags = {
Name = "t101-subnet2"
}
}
resource "aws_internet_gateway" "myigw" {
vpc_id = aws_vpc.myvpc.id
tags = {
Name = "t101-igw"
}
}
resource "aws_route_table" "myrt" {
vpc_id = aws_vpc.myvpc.id
tags = {
Name = "t101-rt"
}
}
resource "aws_route_table_association" "myrtassociation1" {
subnet_id = aws_subnet.mysubnet1.id
route_table_id = aws_route_table.myrt.id
}
resource "aws_route_table_association" "myrtassociation2" {
subnet_id = aws_subnet.mysubnet2.id
route_table_id = aws_route_table.myrt.id
}
resource "aws_route" "mydefaultroute" {
route_table_id = aws_route_table.myrt.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.myigw.id
}
output "aws_vpc_id" {
value = aws_vpc.myvpc.id
}
#배포
terraform plan && terraform apply -auto-approve
#배포확인
terraform state list
aws_internet_gateway.myigw
aws_route.mydefaultroute
aws_route_table.myrt
aws_route_table_association.myrtassociation1
aws_route_table_association.myrtassociation2
aws_subnet.mysubnet1
aws_subnet.mysubnet2
aws_vpc.myvpc
#특정 배포리소스 확인
terraform state show aws_route.mydefaultroute
# aws_route.mydefaultroute:
resource "aws_route" "mydefaultroute" {
carrier_gateway_id = null
core_network_arn = null
destination_cidr_block = "0.0.0.0/0"
destination_ipv6_cidr_block = null
destination_prefix_list_id = null
egress_only_gateway_id = null
gateway_id = "igw-0ab3457cd56fa54c6"
id = "r-rtb-07b7e60d5c523e3221080289494"
instance_id = null
instance_owner_id = null
local_gateway_id = null
nat_gateway_id = null
network_interface_id = null
origin = "CreateRoute"
route_table_id = "rtb-07b7e60d5c523e322"
state = "active"
transit_gateway_id = null
vpc_endpoint_id = null
vpc_peering_connection_id = null
}
#종속성확인
terraform graph > graph.dot
AWS콘솔 : VPC - 라우팅테이블 생성확인
보안그룹(security_group)/EC2배포
sg.tf파일생성(공식DOCS: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group)
- aws_security_group.mysg.vpc_id 필드는 aws_vpc.myvpc.id 로 참조하고있기때문에, 종속성이 존재할것을 짐작할 수 있다.
resource "aws_security_group" "mysg" {
vpc_id = aws_vpc.myvpc.id
name = "T101 SG"
description = "T101 Study SG"
}
resource "aws_security_group_rule" "mysginbound" {
type = "ingress"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.mysg.id
}
resource "aws_security_group_rule" "mysgoutbound" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.mysg.id
}
output "aws_security_group_id" {
value = aws_security_group.mysg.id
}
#배포
terraform plan && terraform apply -auto-approve
#배포확인
terraform state list
aws_internet_gateway.myigw
aws_route.mydefaultroute
aws_route_table.myrt
aws_route_table_association.myrtassociation1
aws_route_table_association.myrtassociation2
aws_security_group.mysg
aws_security_group_rule.mysginbound
aws_security_group_rule.mysgoutbound
aws_subnet.mysubnet1
aws_subnet.mysubnet2
aws_vpc.myvpc
ec2.tf파일생성(공식DOCS: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance)
data "aws_ami" "my_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" "myec2" {
depends_on = [
aws_internet_gateway.myigw
]
ami = data.aws_ami.my_amazonlinux2.id
associate_public_ip_address = true
instance_type = "t2.micro"
vpc_security_group_ids = ["${aws_security_group.mysg.id}"]
subnet_id = aws_subnet.mysubnet1.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.myec2.public_ip
description = "The public IP of the Instance"
}
#목록확인
ls *.tf
#배포
terraform plan && terraform apply -auto-approve
#배포리소스 확인
terraform state list
data.aws_ami.my_amazonlinux2
aws_instance.myec2
aws_internet_gateway.myigw
aws_route.mydefaultroute
aws_route_table.myrt
aws_route_table_association.myrtassociation1
aws_route_table_association.myrtassociation2
aws_security_group.mysg
aws_security_group_rule.mysginbound
aws_security_group_rule.mysgoutbound
aws_subnet.mysubnet1
aws_subnet.mysubnet2
aws_vpc.myvpc
#테라폼 데이터소스 값 확인
terraform console
#배포한 블록으로 접근
> data.aws_ami.my_amazonlinux2
{
"architecture" = "x86_64"
"arn" = "arn:aws:ec2:ap-northeast-2::image/ami-0bc18c0a337801100"
"block_device_mappings" = toset([
{
"device_name" = "/dev/xvda"
"ebs" = tomap({
"delete_on_termination" = "true"
"encrypted" = "false"
"iops" = "0"
"snapshot_id" = "snap-0beaa36b89191979e"
"throughput" = "0"
"volume_size" = "8"
"volume_type" = "standard"
})
"no_device" = ""
"virtual_name" = ""
},
])
"boot_mode" = ""
"creation_date" = "2024-06-11T01:09:54.000Z"
"deprecation_time" = "2025-07-01T00:00:00.000Z"
"description" = "Amazon Linux 2 AMI 2.0.20240610.1 x86_64 HVM ebs"
"ena_support" = true
"executable_users" = tolist(null) /* of string */
"filter" = toset([
{
"name" = "name"
"values" = toset([
"amzn2-ami-hvm-*-x86_64-ebs",
])
},
{
"name" = "owner-alias"
"values" = toset([
"amazon",
])
},
])
"hypervisor" = "xen"
"id" = "ami-0bc18c0a337801100"
"image_id" = "ami-0bc18c0a337801100"
"image_location" = "amazon/amzn2-ami-hvm-2.0.20240610.1-x86_64-ebs"
"image_owner_alias" = "amazon"
"image_type" = "machine"
"imds_support" = ""
"include_deprecated" = false
"kernel_id" = ""
"most_recent" = true
"name" = "amzn2-ami-hvm-2.0.20240610.1-x86_64-ebs"
"name_regex" = tostring(null)
"owner_id" = "137112412989"
"owners" = tolist([
"amazon",
])
"platform" = ""
"platform_details" = "Linux/UNIX"
"product_codes" = toset([])
"public" = true
"ramdisk_id" = ""
"root_device_name" = "/dev/xvda"
"root_device_type" = "ebs"
"root_snapshot_id" = "snap-0beaa36b89191979e"
"sriov_net_support" = "simple"
"state" = "available"
"state_reason" = tomap({
"code" = "UNSET"
"message" = "UNSET"
})
"tags" = tomap({})
"timeouts" = null /* object */
"tpm_support" = ""
"usage_operation" = "RunInstances"
"virtualization_type" = "hvm"
}
> ^C #종료
#종속성확인
terraform graph > graph.dot
#배포된 리소스로 접근
terraform output -raw myec2_public_ip
3.38.150.249%
#curl 명령어로 확인
MYIP=$(terraform output -raw myec2_public_ip)
while true; do curl --connect-timeout 1 http://$MYIP/ ; echo "------------------------------"; date; sleep 1; done
<h1>RegionAz(apne2-az1) : Instance ID(i-095edeccb2f741c9b) : Private IP(10.10.1.6) : Web Server</h1>
------------------------------
Wed Jun 19 05:51:48 KST 2024
<h1>RegionAz(apne2-az1) : Instance ID(i-095edeccb2f741c9b) : Private IP(10.10.1.6) : Web Server</h1>
------------------------------
Wed Jun 19 05:51:49 KST 2024
<h1>RegionAz(apne2-az1) : Instance ID(i-095edeccb2f741c9b) : Private IP(10.10.1.6) : Web Server</h1>
------------------------------
#실습종료
terraform destroy -auto-approve
3.7 Terraform 지역값(local)
- 코드 내에서 사용자가 지정한 값, 또는 속성을 가공해 참조가능한 local(지역값)은 외부에서 입력x
- 코드내에서만 가공되어 동작하는 값을 선언함
- variable(입력변수) 와 달리 선언된 모듈 내에서만 접근 가능하고, 실행시에 입력받을 수 없다.
- local은 사용자가 테라폼코드를 구현할 때 값, 표현식을 반복적으로 사용할 수 있는 편의를 제공함.
- 블록으로 정의할때엔
locals
를 사용함
#실습환경 설정
mkdir 3.7 && cd 3.7
touch main.tf
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 {
content = "content2" # 중복 선언되었으므로 오류가 발생한다.
}
#디렉토리 초기화
terraform init
Initializing the backend...
Terraform encountered problems during initialisation, including problems
with the configuration, described below.
The Terraform configuration must be valid before initialization so that
Terraform can determine which modules and providers need to be installed.
╷
│ Error: Duplicate local value definition
│
│ on main.tf line 16, in locals:
│ 16: content = "content2" # 중복 선언되었으므로 오류가 발생한다.
│
│ A local value named "content" was already defined at main.tf:7,3-42. Local value names must be unique within a
│ module.
╵
╷
│ Error: Duplicate local value definition
│
│ on main.tf line 16, in locals:
│ 16: content = "content2" # 중복 선언되었으므로 오류가 발생한다.
│
│ A local value named "content" was already defined at main.tf:7,3-42. Local value names must be unique within a
│ module.
╵
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]
}
#배포
terraform init
terraform plan && terraform apply -auto-approve
#배포리소스 확인
terraform state list
#아무것도 확인되지않음
local 참조
- local.{name} 으로 참조가능하다.
- 테라폼 구성 파일을 여러개 생성해 작업하는 경우, 서로 다른 파일에 선언되어있더라도 다른파일에서 참조가능하다.
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}"
}
배포실행
- main.tf의 content 는 local.content 를 참조한다.
- 서로다른 테라폼 구성파일에 있지만, 실행시점에선 마치 하나의 구성파일처럼 표기되는것을 확인가능하다.
#
ls *.tf
#배포
terraform init -upgrade
terraform apply -auto-approve
#배포확인
terraform state list
#파이프라인 | 으로 인자넘겨서 확인
echo "local.content" | terraform console
"hello terraform"
#종속성확인
terraform graph > graph.dot
terraform.tfvars에 정의된 변수 우선순위 실습
- local 인자보다 tfvars에 정의된 변수의 우선순위가 높아서 hello terraform → t101-study terraform 으로 변경됨
echo 'prefix="t101-study"' > terraform.tfvars
cat terraform.tfvars
#배포
terraform apply -auto-approve
#결과
cat abc.txt ; echo
"t101-study terraform"
스터디 실습2) AWS IAM User 생성실습
환경구성
mkdir local-test && cd local-test
touch iamuser.tf
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
#종속성 확인
terraform graph > graph.dot
# iam 사용자 리스트 확인
aws iam list-users | jq
AWS콘솔에서 확인 :
# 특정리소스 삭제
terraform destroy -auto-approve -target=aws_iam_user.myiamuser1
#확인
terraform state list
aws_iam_user.myiamuser2
#실습리소스 제거
terraform destroy -auto-approve
terraform state list
3.8 Terraform 출력 output
- 출력값은 테라폼코드의 프로비저닝 수행 후, 결과 속성값을 확인하는 용도로 사용된다.
- 테라폼 모듈끼리, 워크스페이스 간 데이터접근요소로도 활용가능하다.
output 선언(공식DOCS: https://developer.hashicorp.com/terraform/language/values/outputs)
output "instance_ip_addr" {
value = "http://${aws_instance.server.private_ip}"
}
주의할점
- output 결과에서 리소스 생성 후 결정되는 속성값은 프로비저닝이 완료되어야 결과를 확인할 수 있다.
- terraform plan 단계에서는 적용될 값이 출력하지 않는다.
output활용
폴더구성
mkdir 3.8 && cd 3.8
main.tf파일생성
- abs.path: 파일시스템 경로를 포함하는 문자열을 가져와서 절대경로로 반환
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)
}
#하단 file_id = (know after apply) 에 주목
terraform init && terraform plan
Initializing the backend...
Initializing provider plugins...
- Finding latest version of hashicorp/local...
- Installing hashicorp/local v2.5.1...
- Installed hashicorp/local v2.5.1 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated
with the following symbols:
+ create
Terraform will perform the following actions:
# local_file.abc will be created
+ resource "local_file" "abc" {
+ content = "abc123"
+ content_base64sha256 = (known after apply)
+ content_base64sha512 = (known after apply)
+ content_md5 = (known after apply)
+ content_sha1 = (known after apply)
+ content_sha256 = (known after apply)
+ content_sha512 = (known after apply)
+ directory_permission = "0777"
+ file_permission = "0777"
+ filename = "./abc.txt"
+ id = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ file_abspath = "/Users/jjongguet/t1014/3.8/abc.txt"
+ file_id = (known after apply)
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these
actions if you run "terraform apply" now.
#하단 file_id 값이 노출됨에 확인
terraform apply -auto-approve
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated
with the following symbols:
+ create
Terraform will perform the following actions:
# local_file.abc will be created
+ resource "local_file" "abc" {
+ content = "abc123"
+ content_base64sha256 = (known after apply)
+ content_base64sha512 = (known after apply)
+ content_md5 = (known after apply)
+ content_sha1 = (known after apply)
+ content_sha256 = (known after apply)
+ content_sha512 = (known after apply)
+ directory_permission = "0777"
+ file_permission = "0777"
+ filename = "./abc.txt"
+ id = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ file_abspath = "/Users/jjongguet/t1014/3.8/abc.txt"
+ file_id = (known after apply)
local_file.abc: Creating...
local_file.abc: Creation complete after 0s [id=6367c48dd193d56ea7b0baad25b19455e529f5ee]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
file_abspath = "/Users/jjongguet/t1014/3.8/abc.txt"
file_id = "6367c48dd193d56ea7b0baad25b19455e529f5ee"
#종속성 확인
terraform graph > graph.dot
#파일 경로 확인
terraform state list
echo "local_file.abc" | terraform console
echo "local_file.abc.filename" | terraform console
3.9 Terraform 반복문
- list 형태의 값 목록, k-v 형태의 문자열집합 데이터가 있는 경우
- 동일한 내용에 대해 테라폼 구성 정의를 반복적으로 하지 않을수 있다.
count: 반복문
실습환경구성
mkdir 3.9 && cd 3.9
touch main.tf
- 리소스 or 모듈 블록에 count 값이 정수인 인수가 포함된 경우 ⇒ 선언된 정수 값만큼 리소스, 모듈을 생성하게 된다.
- count 에서 생성되는 참조값 : count.index, 기준(0)
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
}
실행후 확인: 5개의 파일이 생성되어야하지만, 파일명이 동일한 상태 ⇒ 하나의 파일만 존재함
#
terraform init && terraform apply -auto-approve
terraform state list
echo "local_file.abc" | terraform console
echo "local_file.abc[0]" | terraform console
echo "local_file.abc[4]" | terraform console
terraform state show 'local_file.abc[0]'
terraform state show 'local_file.abc[4]'
ls *.txt
#
terraform output
terraform output filename
terraform output fileid
terraform output filecontent
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
}
배포확인
#
terraform apply -auto-approve
terraform state list
ls *.txt
echo "local_file.abc" | terraform console
echo "local_file.abc[0].content" | terraform console
echo "local_file.abc[4].content" | terraform console
terraform state show 'local_file.abc[0]'
terraform state show 'local_file.abc[4]'
#
terraform output
terraform output filename
terraform output fileid
terraform output filecontent
# graph 확인 > graph.dot 파일 선택 후 오른쪽 상단 DOT 클릭
terraform graph > graph.dot
main.tf파일수정: list형태의 배열을 활용한 반복문 동작구성
- count에 부여되는 정수값을 외부변수에 식별되도록 구성할 수 있다.
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"
}
배포확인
#
terraform apply -auto-approve
terraform state list
ls *.txt
diff abc-a.txt abc-b.txt
diff abc-a.txt def-c.txt
cat abc-a.txt abc-b.txt abc-c.txt
cat def-a.txt def-b.txt def-c.txt
echo "local_file.abc" | terraform console
echo "local_file.abc[0].content" | terraform console
echo "local_file.abc[2].content" | terraform console
terraform state show 'local_file.abc[0]'
terraform state show 'local_file.abc[2]'
# graph 확인 > graph.dot 파일 선택 후 오른쪽 상단 DOT 클릭
terraform graph > graph.dot
스터디 실습3 IAM사용자 3명 생성 : 반복문(1)
공식DOCS : 사용자 1명 생성코드
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_iam_user" "example" {
name = "neo"
}
불가능한 경우1 : for loop
- terraform 에는 for loop 가 없다
# 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"
}
}
불가능한 경우2: count
- 3개의 IAM 사용자 이름이 중복으로 설정 → 오류발생
resource "aws_iam_user" "example" {
count = 3
name = "neo"
}
가능한 방식
- name 에 count.index 를 함께 붙여서 지정하는 방식
iam.tf생성
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_iam_user" "myiam" {
count = 3
name = "myuser.${count.index}"
}
테라폼 배포
#
terraform init && 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
# apply
terraform apply -auto-approve
# 확인
terraform state list
aws_iam_user.myiam[0]
aws_iam_user.myiam[1]
aws_iam_user.myiam[2]
echo "aws_iam_user.myiam" | terraform console
echo "aws_iam_user.myiam[0]" | terraform console
terraform state show 'aws_iam_user.myiam[0]'
terraform state show 'aws_iam_user.myiam[2]'
aws iam list-users | jq
...
스터디 실습3 IAM사용자 3명 생성 : 입력변수(2)
환경구성
touch variables.tf
variables.tf
variable "user_names" {
description = "Create IAM users with these names"
type = list(string)
default = ["gasida", "akbun", "hyungwook"]
}
iam.tf수정
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_iam_user" "myiam" {
count = length(var.user_names)
name = var.user_names[count.index]
}
테라폼배포
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
output.tf추가
touch 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"
}
배포
terraform apply -auto-approve
#속성값 확인
terraform state list
teraform output
terraform output first_arn
terraform output all_arns