IaC/Terraform
4주차(2)_Terraform Module Output, Add-On
- -
CloudNet@ 팀의 가시다님께서 Leading 하시는 Terraform T101 Study 내용 요약
해당 Terraform Study 는 Terraform Up and Running 책을 기반으로 진행 중입니다.
테라폼 모듈에서 원하는 값을 Output 으로 출력하고, 별개의 리소스 모듈을 붙이는 방법을 알아보자.
코드는 이전 포스팅에서 이어진다.
1. Preview

- Child Module 에서 VPC, Web 리소스를 별개의 모듈로 생성 후, Root Module 에서 서로 연결 !!
1. Module Output
1.1. Child Module Outputs.tf 생성
- Web 모듈에서 VPC 모듈의 RDS 주소와 포트를 사용하고 싶기 때문에,
VPC 모듈에서 Output 으로 RDS 주소와 포트를 변수처리해주자 - 해당 작업은 Child Module 에서 진행한다 !!
output "address" { value = aws_db_instance.scott_rds.address description = "Connect to the database at this endpoint" } output "port" { value = aws_db_instance.scott_rds.port description = "The port the database is listening on" } output "vpcid" { value = aws_vpc.scott_vpc.id description = "My VPC Id" }
위 Output 은 Child Module 에서 생성해주었다.
Root Module 에서 해당 Output 을 사용하기 위해서는 재정의해줘야할 필요가 있다.
1.2. Root Module Outputs.tf 생성
- Root Module 에서는 Child Module 에서 처리한 변수값을 실제 값으로 가져온다.
output "address" { value = module.mysql.address } output "port" { value = module.mysql.port } output "vpcid" { value = module.mysql.vpcid }
- Root Module 에서 가져오는 모듈이름을 mysql 이라고 지정해주었고, Child 모듈에서 지정한 변수 이름을 가져오면된다.
모듈에서 Output 사용 구문은 다음과 같다.
module.<MODULE_NAME>.<OUTPUT_NAME>
그럼 출력 변수를 Web 리소스 모듈에 적용해보자
2. Module Add-on
우리는 앞서 'terraform_remote_state' 라는 데이터 소스를 배웠다.
이를 활용하면 모듈 또한 리소스 별로 분리시켜 관리할 수 있는 장점이 생긴다.
2.1. Child Module Main.tf
- 우선 Child Module 의 main.tf 파일은 다음과 같다.
data "terraform_remote_state" "db" { backend = "s3" config = { bucket = var.s3_vpc_location key = var.s3_vpc_key region = var.s3_vpc_region } } resource "aws_subnet" "scott_subnet1" { vpc_id = data.terraform_remote_state.db.outputs.vpcid cidr_block = var.subnet1_cidr availability_zone = "ap-northeast-2a" tags = { Name = "${var.env}-terraform-subnet1" } } resource "aws_subnet" "scott_subnet2" { vpc_id = data.terraform_remote_state.db.outputs.vpcid cidr_block = var.subnet2_cidr availability_zone = "ap-northeast-2c" tags = { Name = "${var.env}-terraform-subnet2" } } resource "aws_internet_gateway" "scott_igw" { vpc_id = data.terraform_remote_state.db.outputs.vpcid tags = { Name = "${var.env}-terraform-igw" } } resource "aws_route_table" "scott_rt" { vpc_id = data.terraform_remote_state.db.outputs.vpcid tags = { Name = "${var.env}-terraform-rt" } } resource "aws_route_table_association" "scott_rt_association1" { subnet_id = aws_subnet.scott_subnet1.id route_table_id = aws_route_table.scott_rt.id } resource "aws_route_table_association" "scott_rt_association2" { subnet_id = aws_subnet.scott_subnet2.id route_table_id = aws_route_table.scott_rt.id } resource "aws_route" "scott_default_route" { route_table_id = aws_route_table.scott_rt.id destination_cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.scott_igw.id } resource "aws_security_group" "scott_sg" { vpc_id = data.terraform_remote_state.db.outputs.vpcid name = "${var.env}-sg-web" description = "Terraform Study SG - Web" } resource "aws_security_group_rule" "scott_sg_inbound" { type = "ingress" from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] security_group_id = aws_security_group.scott_sg.id } resource "aws_security_group_rule" "scott_sg_outbound" { type = "egress" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] security_group_id = aws_security_group.scott_sg.id } data "aws_ami" "scott_amazonlinux2" { most_recent = true filter { name = "owner-alias" values = ["amazon"] } filter { name = "name" values = ["amzn2-ami-hvm-*-x86_64-ebs"] } owners = ["amazon"] } resource "aws_launch_configuration" "scott_launch_config" { name = "${var.env}-terraform-launch-config" image_id = data.aws_ami.scott_amazonlinux2.id instance_type = var.ec2_type security_groups = [aws_security_group.scott_sg.id] associate_public_ip_address = true # Render the User Data script as a template user_data = templatefile("${path.root}/11_user-data.sh", { server_port = 80 db_address = data.terraform_remote_state.db.outputs.address db_port = data.terraform_remote_state.db.outputs.port }) # Required when using a launch configuration with an auto scaling group. lifecycle { create_before_destroy = true } } resource "aws_autoscaling_group" "scott_asg" { name = "${var.env}-terraform-asg" health_check_type = "ELB" target_group_arns = [aws_lb_target_group.scott_albtg.arn] launch_configuration = aws_launch_configuration.scott_launch_config.name vpc_zone_identifier = [aws_subnet.scott_subnet1.id, aws_subnet.scott_subnet2.id] min_size = var.asg_min_ec2 max_size = var.asg_max_ec2 tag { key = "Name" value = "${var.env}-terraform-asg" propagate_at_launch = true } } resource "aws_lb" "scott_alb" { name = "${var.env}-terraform-alb" load_balancer_type = "application" subnets = [aws_subnet.scott_subnet1.id, aws_subnet.scott_subnet2.id] security_groups = [aws_security_group.scott_sg.id] tags = { Name = "${var.env}-terraform-alb" } } resource "aws_lb_listener" "scott_http" { load_balancer_arn = aws_lb.scott_alb.arn port = 80 protocol = "HTTP" # By default, return a simple 404 page default_action { type = "fixed-response" fixed_response { content_type = "text/plain" message_body = "404: page not found - Terraform Study" status_code = 404 } } } resource "aws_lb_target_group" "scott_albtg" { name = "t101-alb-tg" port = 80 protocol = "HTTP" vpc_id = data.terraform_remote_state.db.outputs.vpcid health_check { path = "/" protocol = "HTTP" matcher = "200-299" interval = 5 timeout = 3 healthy_threshold = 2 unhealthy_threshold = 2 } } resource "aws_lb_listener_rule" "scott_albrule" { listener_arn = aws_lb_listener.scott_http.arn priority = 100 condition { path_pattern { values = ["*"] } } action { type = "forward" target_group_arn = aws_lb_target_group.scott_albtg.arn } }
이전에 File Layout 을 이용해서 tfstate 파일을 격리했을 때와 비슷한 구조이다.
주의해야할 부분을 다시 살펴보자
terrafrom_remote_state 부분에 버킷에 대한 Config 를 모두 변수처리해주었다.
- 해당 부분은 Root Module 에서 지정해주기 위함이다.
data "terraform_remote_state" "db" { backend = "s3" config = { bucket = var.s3_vpc_location key = var.s3_vpc_key region = var.s3_vpc_region } }
EC2 User-data 파일의 위치를 path.root 로 지정해주었다.
- path.root 를 통해, Root Module 위치에 있는 User-data 파일을 사용하기 위함이다.
resource "aws_launch_configuration" "scott_launch_config" { name = "${var.env}-terraform-launch-config" image_id = data.aws_ami.scott_amazonlinux2.id instance_type = var.ec2_type security_groups = [aws_security_group.scott_sg.id] associate_public_ip_address = true # Render the User Data script as a template user_data = templatefile("${path.root}/11_user-data.sh", { server_port = 80 db_address = data.terraform_remote_state.db.outputs.address db_port = data.terraform_remote_state.db.outputs.port }) # Required when using a launch configuration with an auto scaling group. lifecycle { create_before_destroy = true } }
2.2. Child Module Variables.tf
- 다음과 같이 Variables 파일을 생성하자
variable "s3_vpc_location" { description = "S3 Backend Location" type = string } variable "s3_vpc_key" { description = "S3 Backend Key" type = string } variable "s3_vpc_region" { description = "S3 Backend Region" type = string } variable "env" { description = "Environment" type = string } variable "subnet1_cidr" { description = "Subnet3 CIDR Block" type = string } variable "subnet2_cidr" { description = "Subnet4 CIDR Block" type = string } variable "ec2_type" { description = "EC2 Instance Type" type = string } variable "asg_max_ec2" { type = number } variable "asg_min_ec2" { type = number }
2.3. Child Moudle Outputs.tf 생성
- ALB DNS 를 Output 변수로 출력하기 위해 다음과 같은 파일을 생성하자.
output "alb_dns" { value = aws_lb.scott_alb.dns_name description = "The DNS Address of the ALB" }
2.4. Root Module main.tf 생성
- 이제 해당 변수를 입력하여 실제 생성할 Root Module 의 main.tf 를 생성해보자
provider "aws" { region = "ap-northeast-2" } terraform { backend "s3" { bucket = "scott-t101study-tfstate-files" key = "stage/web/terraform.tfstate" region = "ap-northeast-2" dynamodb_table = "terraform-locks-week3-files" } } module "web" { source = "../../02_module/02_web/" s3_vpc_location = "scott-t101study-tfstate-files" s3_vpc_key = "stage/mysql/terraform.tfstate" s3_vpc_region = "ap-northeast-2" env = "stg" subnet1_cidr = "10.10.1.0/24" subnet2_cidr = "10.10.2.0/24" ec2_type = "t2.micro" asg_max_ec2 = 5 asg_min_ec2 = 2 }
- 앞서 terraform_remote_state 변수로 지정한 s3 Config 가 입력이 되어있다.
2.5. User-data 생성
- user-data 위치를 root module로 지정했기 때문에 (${path.root}), user-data 파일을 생성해주자
#!/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 cat > index.html <<EOF <h1>Terraform Study</h1> <p>My RDS DB address: ${db_address}</p> <p>My RDS DB port: ${db_port}</p> EOF nohup ./busybox httpd -f -p ${server_port} &
2.6. Root Module Output 생성
- Child Module 에서 ALB DNS 를 출력 변수 처리했기 때문에, Root Module 에서도 동일하게 변수 처리해주자
output "alb_dns" { description = "DNS Address of the ALB" value = module.web.alb_dns }
3. 생성 확인
모든 것이 준비되었다면 리소스를 실제 배포해보고 ALB DNS 주소로 들어가보자


실습 진행 후에는 반드시 리소스를 삭제하자!
이번 포스팅에서는 Module 을 활용할 때, Output 으로 변수를 출력하는 법과 별개의 리소스 모듈을 연결하는 작업을 해보았다.
다음 포스팅에서는 반복되는 리소스 생성에 용이한 테라폼 반복문에 대해서 알아보겠다 !! (ex. 서브넷 등)
Contents