가시다님의 T101 [4기] 스터디 내용을 정리한 포스트 입니다.
블로그의 실습 내용들은 ‘테라폼으로 시작하는 IaC’ 책을 기준하여 정리하였습니다.
데이터 소스는 테라폼으로 정의되지 않은 외부 리소스 또는 저장된 정보를 테라폼 내에서 참조할 때 사용한다
- vpc 생성
- subnet 생성
- 데이터 소스 블록으로 "aws_available_zones"를 정보를 불러와 primary와 secondary 2개의 subnet을 생성
#data source block으로 가용한 zone 정보를 불러옴
data "aws_availability_zones" "available" {
state = "available"
}
$terraform apply --auto-approve
#console을 통해 불러온 정보를 확인
$terraform console
> data.aws_availability_zones.available.names
tolist([
"ap-northeast-2a",
"ap-northeast-2b",
"ap-northeast-2c",
"ap-northeast-2d",
])
subnet으로 "ap-northeast-2a"와 "ap-northeast-2b"를 사용하고 코드에는 데이타 소스 인수를 통해 값을 불러오도록 한다.
data "aws_availability_zones" "available" {
state = "available"
}
resource "aws_vpc" "kgetall_vpc" {
cidr_block = "10.10.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "kgetall_vcp"
}
}
resource "aws_subnet" "kgetall_subnet_primary" {
vpc_id = resource.aws_vpc.kgetall_vpc.id
availability_zone = data.aws_availability_zones.available.names[0]
cidr_block = "10.10.1.0/24"
}
resource "aws_subnet" "kgetall_subnet_secondary" {
vpc_id = resource.aws_vpc.kgetall_vpc.id
availability_zone = data.aws_availability_zones.available.names[1]
cidr_block = "10.10.2.0/24"
}

입력 변수는 인프라를 구성하는 데 필요한 속성 값을 정의해 코드의 변경 없이 여러 인프라를 생성하는 데 목적이 있다.
테라폼에서는 이것을 입력 변수 Input Variables 로 정의한다.
변수는 variable로 시작되는 블록으로 구성된다. 변수 블록 뒤의 이름 값은 동일 모듈 내 모든 변수 선언에서 고유해야 하며, 이 이름으로 다른 코드 내에서 참조된다.
# variable 블록 선언의 예
variable "<이름>" {
<인수> = <값>
}
variable "image_id" {
type = string
}
variable "string" {
type = string
description = "var String"
default = "myString"
}
variable "number" {
type = number
default = 123
}
variable "boolean" {
default = true
}
variable "list" {
default = [
"google",
"vmware",
"amazon",
"microsoft"
]
}
output "list_index_0" {
value = var.list.0
}
output "list_all" {
value = [
for name in var.list : upper(name)
]
}
variable "map" { # Sorting
default = {
aws = "amazon",
azure = "microsoft",
gcp = "google"
}
}
variable "set" { # Sorting
type = set(string)
default = [
"google",
"vmware",
"amazon",
"microsoft"
]
}
variable "object" {
type = object({ name = string, age = number })
default = {
name = "abc"
age = 12
}
}
variable "tuple" {
type = tuple([string, number, bool])
default = ["abc", 123, true]
}
variable "ingress_rules" { # optional ( >= terraform 1.3.0)
type = list(object({
port = number,
description = optional(string),
protocol = optional(string, "tcp"),
}))
default = [
{ port = 80, description = "web" },
{ port = 53, protocol = "udp" }]
}
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-\"."
}
}
#
terraform apply -auto-approve
var.image_id
The id of the machine image (AMI) to use for the server.
Enter a value: ami
...
#
terraform apply -auto-approve
var.image_id
The id of the machine image (AMI) to use for the server.
Enter a value: ami-
...
#
terraform apply -auto-approve
var.image_id
The id of the machine image (AMI) to use for the server.
Enter a value: ami-12345678
...
terraform apply -auto-approve
variable은 코드 내에서 var.<이름>으로 참조된다.
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
입력 변수의 민감 여부 선언 가능
variable "my_password" {
default = "password"
sensitive = true
}
resource "local_file" "abc" {
content = var.my_password
filename = "${path.module}/abc.txt"
}
# 출력부분에 내용 안보임!
terraform apply -auto-approve
...
~ content = (sensitive value)
...
terraform state show local_file.abc
echo "local_file.abc.content" | terraform console
(sensitive value)
# 결과물 파일 확인
cat abc.txt ; echo
# terraform.tfstate 파일 확인 : VSCODE에서 terraform.tfstate 클릭 후 확인
cat terraform.tfstate | grep '"content":'
"content": "password",

코드 내에서 사용자가 지정한 값 또는 속성 값을 가공해 참조 가능한 local (지역 값)은 외부에서 입력되지 않고, 코드 내에서만 가공되어 동작하는 값을 선언한다.
‘local’은 입력 변수와 달리 선언된 모듈 내에서만 접근 가능하고, 변수처럼 실행 시에 입력받을 수 없다.
모듈 내에서 선언 및 접근됨을 확인해본다.
variable "prefix" {
default = "hello"
}
locals {
name = "terraform"
}
resource "local_file" "abc" {
content = local.content
filename = "${path.module}/abc.txt"
}
locals {
content = "${var.prefix} ${local.name}"
}
ls *.tf
terraform init -upgrade
terraform apply -auto-approve
terraform state list
terraform state show local_file.abc
echo "local.content" | terraform console
"hello terraform"

그러면 local은 어떻게 사용하면 좋을까요? 또 많아 질 경우 관리에 어려움이 발생할 것 같은데, 이럴때는 어떻게 해야 될까요?
로컬 변수가 많아질 경우 구조화된 로컬 변수, 별도의 파일로 분리
locals {
app_config = {
instance_type = "t2.micro"
ami_id = "ami-12345678"
tags = {
environment = "production"
team = "devops"
}
}
}
resource "aws_instance" "example" {
ami = local.app_config.ami_id
instance_type = local.app_config.instance_type
tags = local.app_config.tags
}
// locals.tf
locals {
instance_type = "t2.micro"
ami_id = "ami-12345678"
common_tags = {
environment = "production"
team = "devops"
}
}
// main.tf
resource "aws_instance" "example" {
ami = local.ami_id
instance_type = local.instance_type
tags = local.common_tags
}
locals {
database_instance_type = "db.t2.micro"
web_server_instance_type = "t2.micro"
}
resource "aws_instance" "db_instance" {
instance_type = local.database_instance_type
// 기타 설정
}
resource "aws_instance" "web_server" {
instance_type = local.web_server_instance_type
// 기타 설정
}
locals {
database_instance_type = "db.t2.micro"
web_server_instance_type = "t2.micro"
}
resource "aws_instance" "db_instance" {
instance_type = local.database_instance_type
// 기타 설정
}
resource "aws_instance" "web_server" {
instance_type = local.web_server_instance_type
// 기타 설정
}
출력 값은 주로 테라폼 코드의 프로비저닝 수행 후의 결과 속성 값을 확인하는 용도로 사용된다.
또한 프로그래밍 언어에서 코드 내 요소 간에 제한된 노출을 지원하듯, 테라폼 모듈 간, 워크스페이스 간 데이터 접근 요소로도 활용할 수 있다.
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)
}
$terraform init && terraform plan && terraform apply -auto-approve
...
Outputs:
file_abspath = "/Users/kimj50/tf101/week2/3.8/abc.txt"
file_id = "6367c48dd193d56ea7b0baad25b19455e529f5ee"
$echo "local_file.abc" | terraform console
{
"content" = "abc123"
"content_base64" = tostring(null)
"content_base64sha256" = "bKE9UspwyIPg8LsQHkJaiehiTeUdstI5JZOvaoQRgJA="
"content_base64sha512" = "xwtd2ev7b1HQnUEytxcMnSB1CnhS8AaA9lZY8DEOgQBW5nY8NMmgCw6UAHb1RJXBafwjAszrMSA5JxxDRpUH3A=="
"content_md5" = "e99a18c428cb38d5f260853678922e03"
"content_sha1" = "6367c48dd193d56ea7b0baad25b19455e529f5ee"
"content_sha256" = "6ca13d52ca70c883e0f0bb101e425a89e8624de51db2d2392593af6a84118090"
"content_sha512" = "c70b5dd9ebfb6f51d09d4132b7170c9d20750a7852f00680f65658f0310e810056e6763c34c9a00b0e940076f54495c169fc2302cceb312039271c43469507dc"
"directory_permission" = "0777"
"file_permission" = "0777"
"filename" = "./abc.txt"
"id" = "6367c48dd193d56ea7b0baad25b19455e529f5ee"
"sensitive_content" = (sensitive value)
"source" = tostring(null)
}
$echo "local_file.abc.filename" | terraform console
"./abc.txt
$terraform output -raw file_abspath ; echo
/Users/kimj50/tf101/week2/3.8/abc.txt
list 형태의 값 목록이나 Key-Value 형태의 문자열 집합인 데이터가 있는 경우 동일한 내용에 대해 테라폼 구성 정의를 반복적으로 하지 않고 관리할 수 있다.
반복문에 지정된 정수 값 만큼 반복해서 리소스나 모듈을 생성한다. 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
filename = "${path.module}/def-${var.names[count.index]}.txt"
}

#vpc+보안그룹+EC2배포
provider "aws" {
region = "ap-northeast-2"
}
#vpc
resource "aws_vpc" "kgetall_vpc" {
cidr_block = "10.10.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "t101-study"
}
}
#subnets
data "aws_availability_zones" "available" {
state = "available"
}
resource "aws_subnet" "kgetall_subnet1" {
vpc_id = resource.aws_vpc.kgetall_vpc.id
cidr_block = "10.10.1.0/24"
availability_zone = data.aws_availability_zones.available.names[0]
tags = {
Name = "t101-subnet1"
}
}
resource "aws_subnet" "kgetall_subnet2" {
vpc_id = resource.aws_vpc.kgetall_vpc.id
cidr_block = "10.10.2.0/24"
availability_zone = data.aws_availability_zones.available.names[2]
tags = {
Name = "t101-subnet2"
}
}
#IGW
resource "aws_internet_gateway" "kgetall_gw" {
vpc_id = resource.aws_vpc.kgetall_vpc.id
tags = {
Name = "t101-igw"
}
}
#route table
resource "aws_route_table" "kgetall_rt" {
vpc_id = resource.aws_vpc.kgetall_vpc.id
tags = {
Name = "t101-rt"
}
}
resource "aws_route_table_association" "kgetall_rtassociation1" {
subnet_id = aws_subnet.kgetall_subnet1.id
route_table_id = aws_route_table.kgetall_rt.id
}
resource "aws_route_table_association" "kgetall_rtassociation2" {
subnet_id = aws_subnet.kgetall_subnet2.id
route_table_id = aws_route_table.kgetall_rt.id
}
resource "aws_route" "kgetall_r" {
route_table_id = aws_route_table.kgetall_rt.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.kgetall_gw.id
}
resource "aws_security_group" "kgetall_sg" {
vpc_id = aws_vpc.kgetall_vpc.id
name = "T101 SG"
description = "T101 Study SG"
}
resource "aws_security_group_rule" "kgetall_sginbound" {
type = "ingress"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.kgetall_sg.id
}
resource "aws_security_group_rule" "kgetall_sgoutbound" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.kgetall_sg.id
}
data "aws_ami" "kgetall_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" "kgetall_ec2" {
depends_on = [ aws_internet_gateway.kgetall_gw ]
ami = data.aws_ami.kgetall_amazonlinux2.id
associate_public_ip_address = true
instance_type = "t2.micro"
vpc_security_group_ids = ["${aws_security_group.kgetall_sg.id}"]
subnet_id = aws_subnet.kgetall_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.kgetall_ec2.public_ip
description = "The public IP of the Instance"
}


provider "aws" {
region = "ap-northeast-2"
}
locals {
name = "mytest"
team = {
group = "dev"
}
}
resource "aws_iam_user" "kgetall_iamuser1" {
name = "${local.name}1"
tags = local.team
}
resource "aws_iam_user" "kgetall_iamuser2" {
name = "${local.name}2"
tags = local.team
}
#aws iam list-users | jq
{
"Users": [
{
"Path": "/",
"UserName": "admin",
"UserId": "AIDAYLLVX3DDJGFHWBODO",
"Arn": "arn:aws:iam::574161934534:user/admin",
"CreateDate": "2024-03-03T13:26:44+00:00",
"PasswordLastUsed": "2024-06-17T00:45:14+00:00"
},
{
"Path": "/",
"UserName": "mytest1",
"UserId": "AIDAYLLVX3DDPCUMMUB7M",
"Arn": "arn:aws:iam::574161934534:user/mytest1",
"CreateDate": "2024-06-17T04:47:32+00:00"
},
{
"Path": "/",
"UserName": "mytest2",
"UserId": "AIDAYLLVX3DDKTUVSIQFA",
"Arn": "arn:aws:iam::574161934534:user/mytest2",
"CreateDate": "2024-06-17T04:47:32+00:00"
}
]
}
생성된 iamuser1, iamuser2가 확인됨

iamuser1 유저만 삭제한다.
$terraform destroy --auto-approve -target=aws_iam_user.kgetall_iamuser1
admin과 mytest2 user만 남아 있다.
$aws iam list-users | jq
{
"Users": [
{
"Path": "/",
"UserName": "admin",
"UserId": "AIDAYLLVX3DDJGFH",
"Arn": "arn:aws:iam::574161934534:user/admin",
"CreateDate": "2024-03-03T13:26:44+00:00",
"PasswordLastUsed": "2024-06-17T00:45:14+00:00"
},
{
"Path": "/",
"UserName": "mytest2",
"UserId": "AIDAYLLVX3DDKTUVSIQFA",
"Arn": "arn:aws:iam::574161934534:user/mytest2",
"CreateDate": "2024-06-17T04:47:32+00:00"
}
]
}
- count에 정수를 지정해서 수행하도록 함
resource "aws_iam_user" "iamuser" {
count = 3
name = "user${count.index}"
}
"user0", "user1", "user2" 3명의 IAM User가 생성됨

- 리스트 변수에 생성할 IAM User 이름 3개를 지정하고 length 함수를 활용해 count 에 인덱스 수 만큼 정수값을 지정하여 IAM User 3명을 생성함
variable "names" {
type = list(string)
default = ["admin1", "op1", "op2"]
}
resource "aws_iam_user" "kgetall_iamuser" {
count = length(var.names)
name = "${var.names[count.index]}"
}
"admin1", "op1", "op2" 3명의 IAM User가 생성됨

- 리스트 변수에 생성할 IAM User를 3개("admin1", "op1", "op2")를 생성했는데 중간에 "op1"을 삭제하게되면 뒤에 있던 모든 리소스가 생성되고 다음 해당 리소스를 다시 만들게되는 된다.
variable "names" {
type = list(string)
default = ["admin1", "op2"] #op1을 삭제
}
resource "aws_iam_user" "kgetall_iamuser" {
count = length(var.names)
name = "${var.names[count.index]}"
}
배열의 중간에 "op1" 항목을 제거하면 뒤의 "op2" 항목이 1칸 앞으로 당겨짐

- AWS VPC Subnet 테라폼 코드 작성
- 요구사항 : subnet cidr를 변수로 입력
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
tags = {
Name = "terraform VPC"
}
}
resource "aws_subnet" "main" {
vpc_id = aws_vpc.main.id
cidr_block = var.subnet_cidr
}
output "myvpc_id" {
value = aws_vpc.main.id
}
variable "vpc_cidr" {
type = string
}
variable "subnet_cidr" {
type = string
}
vpc_cidr = "192.168.0.0/16"
subnet_cidr = "192.168.1.0/24"

요구사항 : count를 사용해서 subnet cidr 변수 값을 여러 개 입력 한다.
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
tags = {
Name = "terraform VPC"
}
}
resource "aws_subnet" "main" {
count = length(var.subnet_cidr)
vpc_id = aws_vpc.main.id
cidr_block = element(var.subnet_cidr, count.index)
}
output "myvpc_id" {
value = aws_vpc.main.id
}
variable "vpc_cidr" {
type = string
}
variable "subnet_cidr" {
type = list(string)
}
vpc_cidr = "192.168.0.0/16"
subnet_cidr = ["192.168.1.0/24", "192.168.2.0/24"]

요구사항 : 서브넷이 배치되는 AZ 설정이 필요. AZ는 변수로 입력
variable "vpc_cidr" {
type = string
}
variable "subnet_cidr" {
type = list(string)
}
variable "subnet_az" {
type = list(string)
}
vpc_cidr = "192.168.0.0/16"
subnet_cidr = ["192.168.1.0/24", "192.168.2.0/24"]
subnet_az = ["ap-northeast-2a", "ap-northeast-2c"]
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
tags = {
Name = "terraform VPC"
}
}
resource "aws_subnet" "main" {
count = length(var.subnet_cidr)
vpc_id = aws_vpc.main.id
cidr_block = element(var.subnet_cidr, count.index)
availability_zone = element(var.subnet_az, count.index)
}
output "myvpc_id" {
value = aws_vpc.main.id
}

요구사항 : subnet tag 설정이 필요
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
tags = {
Name = "terraform VPC"
}
}
resource "aws_subnet" "main" {
count = length(var.subnet_cidr)
vpc_id = aws_vpc.main.id
cidr_block = element(var.subnet_cidr, count.index)
availability_zone = element(var.subnet_az, count.index)
tags = {
Name = "terraform VPC-${count.index}"
}
}
output "myvpc_id" {
value = aws_vpc.main.id
}

요구사항 : subnet tag 를 설정하는 변수 생성
variable "vpc_cidr" {
type = string
}
variable "subnet_cidr" {
type = list(string)
}
variable "subnet_az" {
type = list(string)
}
#추가
variable "subnet_tag" {
type = list(map(string))
}
vpc_cidr = "192.168.0.0/16"
subnet_cidr = ["192.168.1.0/24", "192.168.2.0/24"]
subnet_az = ["ap-northeast-2a", "ap-northeast-2c"]
#추가
subnet_tag = [
{
Name = "public-subnet"
Environment = "dev"
},
{
Name = "private-subnet"
Environment = "dev"
}
]
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
tags = {
Name = "terraform VPC"
}
}
resource "aws_subnet" "main" {
count = length(var.subnet_cidr)
vpc_id = aws_vpc.main.id
cidr_block = element(var.subnet_cidr, count.index)
availability_zone = element(var.subnet_az, count.index)
#추가
tags = element(var.subnet_tag, count.index)
}
output "myvpc_id" {
value = aws_vpc.main.id
}

요구사항 : subnet 설정 변수를 한 변수로 설정하도록 리팩토링
variable "vpc_cidr" {
type = string
}
variable "subnets" {
type = list(object({
cidr = string
az = string
tag = map(string)
}))
}
vpc_cidr = "192.168.0.0/16"
subnets = [
{
az = "ap-northeast-2a"
cidr = "192.168.1.0/24"
tag = {
Name = "public-subnet"
Environment = "dev"
}
},
{
az = "ap-northeast-2c"
cidr = "192.168.2.0/24"
tag = {
Name = "private-subnet"
Environment = "dev"
}
}
]
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].tag
}
output "myvpc_id" {
value = aws_vpc.main.id
}

요구사항 : 오류 확인 후 코드 수정
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
}
resource "aws_instance" "server" {
ami = "ami-0e8bd0820b6e1360b"
instance_type = "t4g.nano"
subnet_id = aws_subnet.main[1].id
# index 접근 방법 오류 해결 코드
# subnet_id = index(aws_subnet.main.*.cidr_block, "192.168.2.0/24")
tags = {
Name = "Terraform demo"
}
}
output "myvpc_id" {
value = aws_vpc.main.id
}
배포 실행
4개의 서브넷과 1개의 인스턴스가 생성되었고, 인스턴스는 현재 "aws_subnet.main[1]"(192.168.2.0/24) 서비스넷에 위치해 있다.

[장애 재현] terraform.tfvars 수정 : 아래 부분 주석 처리
vpc_cidr = "192.168.0.0/16"
subnets = [
# (유투브 7번째 시나리오) terrafprm apply 이 후, 첫 번째 요소를 주석하세요
#{
# cidr = "192.168.1.0/24",
# az = "ap-northeast-2a",
# tags = {
# Name = "public-subnet"
# Environment = "dev"
# }
#},
# (유투브 8번째 시나리오) terrafprm apply 이 후, 주석을 해제하고 terraform apply해보세요
# {
# cidr = "192.168.5.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"
}
},
{
cidr = "192.168.3.0/24",
az = "ap-northeast-2a",
tags = {
Name = "public-subnet"
Environment = "dev"
}
},
{
cidr = "192.168.4.0/24",
az = "ap-northeast-2c",
tags = {
Name = "private-subnet"
Environment = "dev"
}
}
]

요구사항 : 오류 확인 후 코드 수정
기존 subnets 변수의 인덱스 값에 "192.168.5.0/24" 서브넷 값을 중간에 추가함.
192.168.5.0/24 영역을 주석 해제 함
vpc_cidr = "192.168.0.0/16"
subnets = [
{
cidr = "192.168.1.0/24",
az = "ap-northeast-2a",
tags = {
Name = "public-subnet"
Environment = "dev"
}
},
#8번째 시나리오) terrafprm apply 이 후, 주석 해제함
{
cidr = "192.168.5.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"
}
},
{
cidr = "192.168.3.0/24",
az = "ap-northeast-2a",
tags = {
Name = "public-subnet"
Environment = "dev"
}
},
{
cidr = "192.168.4.0/24",
az = "ap-northeast-2c",
tags = {
Name = "private-subnet"
Environment = "dev"
}
}
]
- apply 과정에서 에러 발생함
기존 "192.168.2.0/24" ~ "192.168.4.0/24"이 삭제되고 "192.168.5.0/24"이후로 생성되는 과정에서 충돌이 발생하여 에러가 발생되는 것으로 추정됨

시나리오7~8의 경우 반복문을 index로 접근했기 때문에 발생되는 이슈들이다. 위 이슈들이 발생되지 않도록 for_each를 적용해 보겠습니다.
기존 list type에서 map type으로 변경함
variable "vpc_cidr" {
type = string
}
variable "subnets" {
type = map(object({
cidr = string
az = string
tags = map(string)
}))
}
vpc_cidr = "192.168.0.0/16"
subnets = {
public-subnet1 = {
cidr = "192.168.1.0/24",
az = "ap-northeast-2a",
tags = {
Name = "public-subnet"
Environment = "dev"
}
},
private-subnet1 = {
cidr = "192.168.2.0/24",
az = "ap-northeast-2c",
tags = {
Name = "private-subnet"
Environment = "dev"
}
},
public-subnet2 = {
cidr = "192.168.3.0/24",
az = "ap-northeast-2a",
tags = {
Name = "public-subnet"
Environment = "dev"
}
},
private-subnet2 = {
cidr = "192.168.4.0/24",
az = "ap-northeast-2c",
tags = {
Name = "private-subnet"
Environment = "dev"
}
},
}
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
tags = {
Name = "terraform VPC"
}
}
resource "aws_subnet" "main" {
for_each = var.subnets
vpc_id = aws_vpc.main.id
cidr_block = each.value["cidr"]
availability_zone = each.value["az"]
tags = each.value["tags"]
}
resource "aws_instance" "server" {
ami = "ami-0e8bd0820b6e1360b"
instance_type = "t4g.nano"
subnet_id = aws_subnet.main["private-subnet1"].id
tags = {
Name = "Terraform demo"
}
}

"시나리오7"과 동일한 장애 상황 재현하기 위해서
사용하지 않는 "192.168.1.0/24"을 삭제함
vpc_cidr = "192.168.0.0/16"
subnets = {
# public-subnet1 = {
# cidr = "192.168.1.0/24",
# az = "ap-northeast-2a",
# tags = {
# Name = "public-subnet"
# Environment = "dev"
# }
# },
private-subnet1 = {
cidr = "192.168.2.0/24",
az = "ap-northeast-2c",
tags = {
Name = "private-subnet"
Environment = "dev"
}
},
public-subnet2 = {
cidr = "192.168.3.0/24",
az = "ap-northeast-2a",
tags = {
Name = "public-subnet"
Environment = "dev"
}
},
private-subnet2 = {
cidr = "192.168.4.0/24",
az = "ap-northeast-2c",
tags = {
Name = "private-subnet"
Environment = "dev"
}
},
}
기존 시나리오7의 결과와는 다르게 "192.168.1.0/24" 서브넷만 삭제과 인스턴스가 삭제/생성되거나 다른 서브넷과 변경이 없는 것을 확인할 수 있다.

"시나리오8"과 동일한 장애 상황 재현하기 위해서
"192.168.5.0/24" 서브넷을 추가함
vpc_cidr = "192.168.0.0/16"
subnets = {
public-subnet1 = {
cidr = "192.168.1.0/24",
az = "ap-northeast-2a",
tags = {
Name = "public-subnet"
Environment = "dev"
}
},
#서브넷 추가함
public-subnet3 = {
cidr = "192.168.5.0/24",
az = "ap-northeast-2a",
tags = {
Name = "public-subnet"
Environment = "dev"
}
},
private-subnet1 = {
cidr = "192.168.2.0/24",
az = "ap-northeast-2c",
tags = {
Name = "private-subnet"
Environment = "dev"
}
},
public-subnet2 = {
cidr = "192.168.3.0/24",
az = "ap-northeast-2a",
tags = {
Name = "public-subnet"
Environment = "dev"
}
},
private-subnet2 = {
cidr = "192.168.4.0/24",
az = "ap-northeast-2c",
tags = {
Name = "private-subnet"
Environment = "dev"
}
},
}
추가된 서브넷 "192.168.5.0/24"만 리소스에 추가되었으며 그 외 다른 리소스는 변경이 없는 것을 볼 수 있습니다.
