[t101-3기] Terraform 스터디 2주차
본 시리즈는 T101(테라폼으로 시작하는 IaC) 3기 스터디 진행 내용입니다. 테라폼으로 시작하는 IaC 책을 기반으로 내용 정리하였습니다.
데이터 소스란?
데이터 소스를 테라폼으로 정의되지 않은 외부 리소스 또는 저장된 정보를 테라폼 내에서 참조할 때 사용한다
- 데이터 소스 블록은 ‘data’ 로 시작하고 구후, ‘데이터 소스 유형을 정의하게 된다. ← Resource 블록 정의와 유사 - [참고: local 프로바이더 - data sources]
- 데이터 소스 유형은 첫 번째 _를 기준으로 앞은 프로바이더 이름, 뒤는 프로바이더에서 제공하는 리소스 유형을 의미한다.
- 데이터 소스 유형을 선언한 뒤에는 고유한 이름을 붙인다. 리소스의 이름과 마찬가지로 이름은 동일한 유형에 대한 식별자 역할을 하므로 중복될 수 없다.
- 이름 뒤에는 데이터 소스 유형에 대한 구성 인수들은 { } 안에 선언한다. 인수가 필요하지 않은 유형도 있지만, 그때에도 { } 는 입력한다
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
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
}
[도전과제1]
위 리전 내에서 사용 가능한 가용영역 목록 가져오기를 사용한 VPC 리소스 생성 실습 진행, 또는 관련된 데이터 소스를 사용한 실습한 내용을 글로 작성. 리전 내에서 사용 가능한 가용영역 목록 가져오기를 사용한 vpc와 subnet 만들기
# 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
}
하지만 아래와 같은 에러가 발생한다. 읽어보면 aws_subnet을 위해서는 vpc_id라는 argument값이 필요한데, 정의 되지 않아서 에러가 발생한다는 것
~/De/practice/terraform/t1/3.5 master <3>2 +3 !5 ?243 terraform plan
╷
│ Error: Missing required argument
│
│ on main.tf line 5, in resource "aws_subnet" "primary":
│ 5: resource "aws_subnet" "primary" {
│
│ The argument "vpc_id" is required, but no definition was found.
╵
╷
│ Error: Missing required argument
│
│ on main.tf line 9, in resource "aws_subnet" "secondary":
│ 9: resource "aws_subnet" "secondary" {
│
│ The argument "vpc_id" is required, but no definition was found.
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet
이를 해결해주기 위해 어떤 argument가 필요한지 위 링크에서 찾아본다
data "aws_availability_zones" "available" {
state = "available"
}
#vpc
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
instance_tenancy = "default"
tags = {
Name = "main"
}
}
#subnet
resource "aws_subnet" "primary" {
availability_zone = data.aws_availability_zones.available.names[0]
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
tags = {
Name = "Main"
}
}
resource "aws_subnet" "second" {
availability_zone = data.aws_availability_zones.available.names[0]
vpc_id = aws_vpc.main.id
cidr_block = "10.0.2.0/24"
tags = {
Name = "Main"
}
}
입력 변수 Variable
변수 선언
# variable 블록 선언의 예
variable "<이름>" {
<인수> = <값>
}
variable "image_id" {
type = string
}
- 테라폼 예약 변수 이름으로 사용 불가능 : source, version, providers, count, for_each, lifecycle, depends_on, locals
변수 유형
- 기본 유형
- string : 글자 유형
- number : 숫자 유형
- bool : true 또는 false
- any : 명시적으로 모든 유형이 허용됨을 표시
- 집합 유형
- list (<유형>): 인덱스 기반 집합유형>
- map (<유형>): 값 = 속성 기반 집합이며 키값 기준 정렬유형>
- set (<유형>): 값 기반 집합이며 정렬 키값 기준 정렬유형>
- object ({<인수 이름="">=<유형>, …})유형>인수>
- tuple ([<유형>, …])유형>
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" }]
}
유효성 검사
입력되는 변수 타입 지정 이외, 사용자 지정 유효성 검사가 가능
- 변수 블록 내에 validation 블록에서 조건인 condition에 지정되는 규칙이 true 또는 false를 반환해야 하며, error_message는 condition 값의 결과가 false 인 경우 출력되는 메시지를 정의한다.
- regex 함수는 대상의 문자열에 정규식을 적용하고 일치하는 문자열을 반환하는데, 여기에 can 함수를 함께 사용하면 정규식에 일치하지 않는 경우의 오류를 검출한다.
- validation 블록은 중복으로 선언할 수 있다.
변수 참조
variable은 코드 내에서 var.<이름>으로 참조된다.이름>
variable "my_password" {}
resource "local_file" "abc" {
content = var.my_password
filename = "${path.module}/abc.txt"
}
민감한 변수 취급
입력 변수의 민감 여부 선언 가능
[도전과제2]
위 3개 코드 파일 내용에 리소스의 이름(myvpc, mysubnet1 등)을 반드시! 꼭! 자신의 닉네임으로 변경해서 배포 실습해보세요!
vpc.tf
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_vpc" "yuranvpc" {
cidr_block = "10.10.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "t101-study"
}
}
resource "aws_subnet" "yuransubnet1" {
vpc_id = aws_vpc.yuranvpc.id
cidr_block = "10.10.1.0/24"
availability_zone = "ap-northeast-2a"
tags = {
Name = "t101-subnet1"
}
}
resource "aws_subnet" "yuransubnet2" {
vpc_id = aws_vpc.yuranvpc.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.yuranvpc.id
tags = {
Name = "t101-igw"
}
}
resource "aws_route_table" "myrt" {
vpc_id = aws_vpc.yuranvpc.id
tags = {
Name = "t101-rt"
}
}
resource "aws_route_table_association" "myrtassociation1" {
subnet_id = aws_subnet.yuransubnet1.id
route_table_id = aws_route_table.myrt.id
}
resource "aws_route_table_association" "myrtassociation2" {
subnet_id = aws_subnet.yuransubnet2.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.yuranvpc.id
}
sg.tf
resource "aws_security_group" "mysg" {
vpc_id = aws_vpc.yuranvpc.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
}
ec2.tf
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.yuransubnet1.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-yuranec2"
}
}
output "myec2_public_ip" {
value = aws_instance.myec2.public_ip
description = "The public IP of the Instance"
}
[도전과제3]
도전과제3 : 입력변수를 활용해서 리소스(어떤 리소스든지 상관없음)를 배포해보고, 해당 코드를 정리해주세요! 위 ec2.tf 파일 중 ec2_instance_type 값을 입력 변수로 받기
variable "ec2_instance_type" {
type = string
description = "The type of EC2 instance to launch"
}
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 = "var.ec2_instance_type"
vpc_security_group_ids = ["${aws_security_group.mysg.id}"]
subnet_id = aws_subnet.yuransubnet1.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-yuranec2"
}
}
output "myec2_public_ip" {
value = aws_instance.myec2.public_ip
description = "The public IP of the Instance"
}
local
코드 내에서 사용자가 지정한 값 또는 속성 값을 가공해 참조 가능한 local (지역 값)은 외부에서 입력되지 않고, 코드 내에서만 가공되어 동작하는 값을 선언한다.
local
은 입력 변수와 달리 선언된 모듈 내에서만 접근 가능하고, 변수처럼 실행 시에 입력받을 수 없다.
로컬은 사용자가 테라폼 코드를 구현할 때 값이나 표현식을 반복적으로 사용할 수 있는 편의를 제공한다. 하지만 빈번하게 여러 곳에서 사용되는 경우 실제 값에 대한 추적이 어려워져 유지 관리 측면에서 부담이 발생할 수 있으므로 주의해야 한다.
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" # 중복 선언되었으므로 오류가 발생한다.
}
- 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}"
}
[도전과제4]
local를 활용해서 리소스(어떤 리소스든지 상관없음)를 배포해보고, 해당 코드를 정리해주세요!
provider "aws" {
region = "ap-northeast-2"
}
locals {
name = "yuran"
}
resource "aws_vpc" "yuranvpc" {
cidr_block = "10.10.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "${local.name}-study"
}
}
resource "aws_subnet" "yuransubnet1" {
vpc_id = aws_vpc.yuranvpc.id
cidr_block = "10.10.1.0/24"
availability_zone = "ap-northeast-2a"
tags = {
Name = "${local.name}-subnet1"
}
}
resource "aws_subnet" "yuransubnet2" {
vpc_id = aws_vpc.yuranvpc.id
cidr_block = "10.10.2.0/24"
availability_zone = "ap-northeast-2c"
tags = {
Name = "${local.name}-subnet2"
}
}
댓글남기기