1. 조건문 (3항 연산자)
Terraform은 3항 연산자 조건문을 지원하며, 간단한 조건문을 구현할 때 적합
<조건> ? <조건 만족> : <조건 불만족>
2. 내장 함수
테라폼은 프로그래밍 언어적인 특성을 가지고 있어, 값의 유형을 변경하거나 조합할 수 있는 내장 함수를 사용 가능
참고 : https://developer.hashicorp.com/terraform/language/functions
Challenge1,2
조건문을 활용하여 (각자 편리한) AWS 리소스를 배포하는 코드를 작성
내장 함수를 활용하여 (각자 편리한) 리소스를 배포하는 코드 작성
코드 참고 : https://github.com/junho102/Terraform-Study/tree/main/Week3/challenge1%2C2
resource "aws_vpc" "vpc" {
cidr_block = local.vpc_cidr
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "${local.project_name}-vpc-${local.env}-apne2"
}
}
resource "aws_subnet" "pub_subnet" {
count = length(local.pub_cidr)
vpc_id = aws_vpc.vpc.id
cidr_block = element(local.pub_cidr, count.index)
availability_zone = ((count.index) % 2) == 0 ? local.zone_id.names[0] : local.zone_id.names[2]
tags = {
Name = "${local.project_name}-snet-${local.env}-pub-${((count.index) % 2) == 0 ? "a" : "c"}-apne2"
}
}
resource "aws_subnet" "primary_pri_subnet" {
count = length(local.primary_pri_cidr)
vpc_id = aws_vpc.vpc.id
cidr_block = element(local.primary_pri_cidr, count.index)
availability_zone = ((count.index) % 2) == 0 ? local.zone_id.names[0] : local.zone_id.names[2]
tags = {
Name = "${local.project_name}-snet-${local.env}-pri-${((count.index) % 2) == 0 ? "a" : "c"}-apne2"
}
}
※ 코드설명
- 3항 연산자를 사용하여 count의 선언된 값을 나누어 나머지 값이 0이면 availability zone a 에 생성하고, 나머지가 0이 아니라면 availability zone c 에 서브넷을 생성
- 내장함수 element를 사용하여 list형식으로 저장된 local변수 pub_cidr의 cidr값을 순차적으로 가져오며 subnet 생성
3. local-exec & remote-exec 프로비저너 (w. file 프로비저너)
local-exec 프로비저너는 테라폼이 실행되는 환경에서 수행할 커맨드를 정의
remote-exec 프로비저너는 원격지 환경에서 실행할 커맨드와 스크립트를 정의
file 프로비저너는 테라폼을 실행하는 시스템에서 연결 대상으로 파일 또는 디렉토리를 복사하는데 사용
Challenge3
AWS EC2 배포시 remote-exec/file 프로비저너를 활용하는 코드를 작성
코드 참고 : https://github.com/junho102/Terraform-Study/tree/main/Week3/challenge3
resource "aws_instance" "apache" {
ami = data.aws_ami.latest_amazon_linux_2.id
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.apache_sg.id]
key_name = var.key_pair_name
tags = { Name = "${var.name}-apache-instance" }
}
resource "null_resource" "file" {
connection {
type = "ssh"
user = "ec2-user"
private_key = file("${path.module}/test.pem")
host = aws_instance.apache.public_ip
}
provisioner "file" {
source = "setup_apache.sh"
destination = "setup_apache.sh"
}
provisioner "remote-exec" {
inline = [
"chmod +x setup_apache.sh",
"./setup_apache.sh"
]
}
}
※ 코드설명
- remote-exec와 file 프로비저너를 사용하기 위해 ec2에 연결할 connection 정보를 정의
- file 프로비저너를 사용하여 테라폼을 실행하는 환경(Mac)에서 생성한 ec2에 apache 설치 및 index.html파일 정의하는 shell script파일을 복사
- remote-exec 프로비저너를 사용하여 ec2내에서 shell script파일의 실행권한을 주고 실행하는 명령어 정의
4. terraform_data 리소스 (null_resource 비교)
- Terraform 1.4 버전이 릴리즈되며, 기존 null_resource 를 대체하는 terraform_data 추가
- terraform_data는 null_resource는 별도의 프로바이더 구성이 필요하는점과 추가 프로바이더 없이 테라폼 자체에 포함된 기본 수명주기 관리자를 제공
- null_resource와 terraform_data는 주로 테라폼 프로비저닝 동작을 설계하며 사용자가 의도적으로 프로비저닝하는 동작을 조율해야 하는 상황이 발생하여, 프로바이더가 제공하는 리소스 수명주기 관리만으로는 이를 해결하기 어려울 때 사용
- terraform_data 리소스는 임의의 값(input인수를 통해)을 저장하고 추후 다른 리소스의 수명 주기 트리거(강제 재실행을 위한 - trigger_replace)를 구현하는데 사용 가능하며, 적절한 관리 리소스를 사용할 수 없을 때 프로비저너를 트리거하는데 사용 가능.
Challenge4
terraform_data 리소스와 trigger_replace를 사용한 테라폼 코드 작성
코드 참고 : https://github.com/junho102/Terraform-Study/tree/main/Week3/challenge4
resource "aws_network_interface" "ec2_private_ip" {
subnet_id = aws_subnet.pub_subnet.id
private_ips = ["192.168.1.11"]
security_groups = [aws_security_group.apache_instance_sg.id]
tags = {
Name = "private_network_interface"
}
}
resource "aws_eip" "apache_ec2_eip" {
domain = "vpc"
}
resource "aws_instance" "web" {
ami = data.aws_ami.latest_amazon_linux_2.id
instance_type = "t2.micro"
key_name = var.key_pair_name
network_interface {
network_interface_id = aws_network_interface.ec2_private_ip.id
device_index = 0
}
tags = { Name = "${var.env}-instance" }
}
resource "aws_eip_association" "eip_assoc" {
instance_id = aws_instance.web.id
allocation_id = aws_eip.apache_ec2_eip.id
}
resource "terraform_data" "ec2_trigger" {
triggers_replace = [aws_instance.web.private_ip]
provisioner "remote-exec" {
connection {
type = "ssh"
user = "ec2-user"
private_key = file("${path.module}/test.pem")
host = aws_eip.apache_ec2_eip.public_ip
}
inline = [
"sudo yum update -y",
"sudo yum install -y httpd",
"sudo systemctl enable httpd",
"sudo systemctl start httpd"
]
}
depends_on = [aws_eip_association.eip_assoc, aws_instance.web]
}
※ 코드설명
- terraform_data 리소스 내 remote-exec 프로비저너를 사용하여 ec2에 접속하여 apache를 설치
- terraform_data 리소스 내 triggers_replace를 활용하여 ec2의 private_ip가 바뀌었을때만 동작하도록 설정
※ 결과 확인
1. 처음 테라폼 코드를 실행시킬 때는 network_interface의 private_ip를 192.168.1.11로 설정하여 실행
2. terraform_data 리소스 블럭의 triggers_replace를 주석 처리 하여 테라폼 재실행
(아래와 같이 terraform_data 리소스 블럭의 remote-exec 프로비저너가 다시 실행되는 모습 확인 가능)
> terraform apply -auto-approve
data.aws_availability_zones.az: Reading...
aws_vpc.vpc: Refreshing state... [id=vpc-0bf92912f6a8cbe2b]
aws_eip.apache_ec2_eip: Refreshing state... [id=eipalloc-08123b33f6468c6bf]
data.aws_ami.latest_ubuntu_22_04: Reading...
data.aws_ami.latest_amazon_linux_2: Reading...
data.aws_availability_zones.az: Read complete after 0s [id=ap-northeast-2]
data.aws_ami.latest_ubuntu_22_04: Read complete after 0s [id=ami-0c0ea4662d3cca101]
data.aws_ami.latest_amazon_linux_2: Read complete after 0s [id=ami-0314c6b4d666713d7]
aws_internet_gateway.igw: Refreshing state... [id=igw-0a0216d3f5c6235bf]
aws_route_table.route_table_public: Refreshing state... [id=rtb-0847379a61cc54c4f]
aws_subnet.pub_subnet: Refreshing state... [id=subnet-0164714a54e2d3fb4]
aws_security_group.apache_instance_sg: Refreshing state... [id=sg-06d6207b06f4be11a]
aws_route.public_route: Refreshing state... [id=r-rtb-0847379a61cc54c4f1080289494]
aws_network_interface.ec2_private_ip: Refreshing state... [id=eni-0741b4d2b84d55f8b]
aws_route_table_association.public_asso_rt[0]: Refreshing state... [id=rtbassoc-05e30692a26bd21a7]
aws_instance.web: Refreshing state... [id=i-08c5cc8607b78d475]
aws_eip_association.eip_assoc: Refreshing state... [id=eipassoc-0eb1080611e176396]
terraform_data.ec2_trigger: Refreshing state... [id=730aff3b-3258-288c-d975-fc768362b2de]
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:
# terraform_data.ec2_trigger must be replaced
-/+ resource "terraform_data" "ec2_trigger" {
~ id = "730aff3b-3258-288c-d975-fc768362b2de" -> (known after apply)
- triggers_replace = [
- "192.168.1.11",
] -> null
}
Plan: 1 to add, 0 to change, 1 to destroy.
terraform_data.ec2_trigger: Destroying... [id=730aff3b-3258-288c-d975-fc768362b2de]
terraform_data.ec2_trigger: Destruction complete after 0s
terraform_data.ec2_trigger: Creating...
terraform_data.ec2_trigger: Provisioning with 'remote-exec'...
terraform_data.ec2_trigger (remote-exec): Connecting to remote host via SSH...
terraform_data.ec2_trigger (remote-exec): Host: 52.78.93.204
terraform_data.ec2_trigger (remote-exec): User: ec2-user
terraform_data.ec2_trigger (remote-exec): Password: false
terraform_data.ec2_trigger (remote-exec): Private key: true
terraform_data.ec2_trigger (remote-exec): Certificate: false
terraform_data.ec2_trigger (remote-exec): SSH Agent: true
terraform_data.ec2_trigger (remote-exec): Checking Host Key: false
terraform_data.ec2_trigger (remote-exec): Target Platform: unix
terraform_data.ec2_trigger (remote-exec): Connected!
terraform_data.ec2_trigger (remote-exec): Loaded plugins: extras_suggestions,
terraform_data.ec2_trigger (remote-exec): : langpacks, priorities,
terraform_data.ec2_trigger (remote-exec): : update-motd
terraform_data.ec2_trigger (remote-exec): No packages marked for update
terraform_data.ec2_trigger (remote-exec): Loaded plugins: extras_suggestions,
terraform_data.ec2_trigger (remote-exec): : langpacks, priorities,
terraform_data.ec2_trigger (remote-exec): : update-motd
terraform_data.ec2_trigger (remote-exec): Package httpd-2.4.57-1.amzn2.x86_64 already installed and latest version
terraform_data.ec2_trigger (remote-exec): Nothing to do
terraform_data.ec2_trigger: Creation complete after 3s [id=3cae2b7e-1c10-1f02-a77f-149f7761d033]
Apply complete! Resources: 1 added, 0 changed, 1 destroyed
3. terraform_data 리소스 블럭의 triggers_replace를 주석 처리를 해제하여 테라폼 재실행
(아래와 같이 terraform_data 리소스 블럭의 triggers_replace를 통해 aws_instance.web.private_ip가 변경될 때만
동작하기 때문에 remote-exec가 동작하지 않는 모습을 확인 가능)
> terraform apply -auto-approve
data.aws_availability_zones.az: Reading...
aws_eip.apache_ec2_eip: Refreshing state... [id=eipalloc-08123b33f6468c6bf]
data.aws_ami.latest_ubuntu_22_04: Reading...
aws_vpc.vpc: Refreshing state... [id=vpc-0bf92912f6a8cbe2b]
data.aws_ami.latest_amazon_linux_2: Reading...
data.aws_availability_zones.az: Read complete after 0s [id=ap-northeast-2]
data.aws_ami.latest_ubuntu_22_04: Read complete after 0s [id=ami-0c0ea4662d3cca101]
data.aws_ami.latest_amazon_linux_2: Read complete after 0s [id=ami-0314c6b4d666713d7]
aws_internet_gateway.igw: Refreshing state... [id=igw-0a0216d3f5c6235bf]
aws_route_table.route_table_public: Refreshing state... [id=rtb-0847379a61cc54c4f]
aws_subnet.pub_subnet: Refreshing state... [id=subnet-0164714a54e2d3fb4]
aws_security_group.apache_instance_sg: Refreshing state... [id=sg-06d6207b06f4be11a]
aws_route.public_route: Refreshing state... [id=r-rtb-0847379a61cc54c4f1080289494]
aws_route_table_association.public_asso_rt[0]: Refreshing state... [id=rtbassoc-05e30692a26bd21a7]
aws_network_interface.ec2_private_ip: Refreshing state... [id=eni-0741b4d2b84d55f8b]
aws_instance.web: Refreshing state... [id=i-08c5cc8607b78d475]
aws_eip_association.eip_assoc: Refreshing state... [id=eipassoc-0eb1080611e176396]
terraform_data.ec2_trigger: Refreshing state... [id=ad4dd631-d5f5-e8c8-75e6-288babf6bb57]
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.
5. moved Block을 통한 코드 리팩터링
moved block은 테라폼 코드에서 리소스의 이름은 변경되지만 이미 테라폼으로 프로비저닝 환경을 그대로 유지하고자 하는 경우 사용
Challenge5
moved block을 사용한 테라폼 코드 리팩터링 테스트
코드 참조 : https://github.com/junho102/Terraform-Study/tree/main/Week3/challenge5
resource "aws_vpc" "vpc" {
cidr_block = var.vpc_cidr
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "${var.env}-vpc"
}
}
resource "aws_subnet" "pub_subnet3" {
vpc_id = aws_vpc.vpc.id
cidr_block = var.pub_cidr[0]
availability_zone = data.aws_availability_zones.az.names[0]
tags = {
Name = "${var.env}-pub-${data.aws_availability_zones.az.names[0]}"
}
}
resource "aws_subnet" "pub_subnet4" {
vpc_id = aws_vpc.vpc.id
cidr_block = var.pub_cidr[1]
availability_zone = data.aws_availability_zones.az.names[2]
tags = {
Name = "${var.env}-pub-${data.aws_availability_zones.az.names[2]}"
}
}
moved {
from = aws_subnet.pub_subnet1
to = aws_subnet.pub_subnet3
}
moved {
from = aws_subnet.pub_subnet2
to = aws_subnet.pub_subnet4
}
※ 코드설명
- 기존 subnet 생성 리소스의 이름은 pub_subnet1과 pub_subnet2로 생성한 상태에서 진행
- aws_subnet 리소스 블럭의 이름을 각각 pub_subnet3, pub_subnet4로 변경
- moved 블럭을 사용하여 pub_subnet1이름은 pub_subnet3으로, pub_subnet2이름은 pub_subnet4로 변경
※ 결과 확인
> terraform apply -auto-approve
data.aws_availability_zones.az: Reading...
aws_vpc.vpc: Refreshing state... [id=vpc-0226ffc7069cc5471]
data.aws_availability_zones.az: Read complete after 0s [id=ap-northeast-2]
aws_subnet.pub_subnet3: Refreshing state... [id=subnet-061b4b028737002da]
aws_subnet.pub_subnet4: Refreshing state... [id=subnet-0002f8ffdf6295d89]
Terraform will perform the following actions:
# aws_subnet.pub_subnet1 has moved to aws_subnet.pub_subnet3
resource "aws_subnet" "pub_subnet3" {
id = "subnet-061b4b028737002da"
tags = {
"Name" = "test-pub-ap-northeast-2a"
}
# (16 unchanged attributes hidden)
}
# aws_subnet.pub_subnet2 has moved to aws_subnet.pub_subnet4
resource "aws_subnet" "pub_subnet4" {
id = "subnet-0002f8ffdf6295d89"
tags = {
"Name" = "test-pub-ap-northeast-2c"
}
# (16 unchanged attributes hidden)
}
Plan: 0 to add, 0 to change, 0 to destroy.
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
6. Terraform Provider alias
aws를 사용할 때 특정 리소스만 리전을 따로 설정하고 사용할 때 terraform의 provider에서 alias를 지정하여 사용
Challenge6
AWS의 S3 버킷을 2개의 리전에서 동작하는 테라폼 코드 작성
코드 참조 : https://github.com/junho102/Terraform-Study/tree/main/Week3/challenge6
provider "aws" {
region = "ap-northeast-2"
alias = "seoul_region"
}
provider "aws" {
region = "ap-northeast-1"
alias = "tokyo_region"
}
variable "name" {
default = "ljh"
}
resource "aws_s3_bucket" "seoul_s3_bucket" {
provider = aws.seoul_region
bucket = "${var.name}-seoul-s3"
}
resource "aws_s3_bucket" "tokyo_s3_bucket" {
provider = aws.tokyo_region
bucket = "${var.name}-tokyo-s3"
}
※ 코드설명
- provider block을 2개 생성하되, alias를 각각 seoul_region와 tokyo_region으로 설정
- s3를 생성하기 위한 aws_s3_bucket에 각각 provider를 원하는 리전의 alias로 지정
Challenge7
Azure 프로바이더를 사용하여 인스턴스 배포
코드 참조: https://github.com/junho102/Terraform-Study/tree/main/Week3/challenge7
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "=3.0.0"
}
}
}
provider "azurerm" {
features {}
# client_id = ""
# client_secret = ""
# tenant_id = ""
# subscription_id = ""
}
# Create resource group
resource "azurerm_resource_group" "sldt_rg" {
name = "${var.prefix}-rg"
location = "${var.region}"
}
# Create virtual network
resource "azurerm_virtual_network" "vnet" {
name = "${var.prefix}-vnet"
location = azurerm_resource_group.sldt_rg.location
address_space = [var.address_space]
resource_group_name = azurerm_resource_group.sldt_rg.name
}
# Create subnet
resource "azurerm_subnet" "public_subnet" {
name = "${var.prefix}-pub-subnet"
virtual_network_name = azurerm_virtual_network.vnet.name
resource_group_name = azurerm_resource_group.sldt_rg.name
address_prefixes = [var.pub_subnet_prefix]
service_endpoints = ["Microsoft.KeyVault", "Microsoft.Storage"]
}
# Create Network Security Group and rule(bastion)
resource "azurerm_network_security_group" "bastion_nsg" {
name = "${var.prefix}-bastion-nsg"
location = azurerm_resource_group.sldt_rg.location
resource_group_name = azurerm_resource_group.sldt_rg.name
security_rule {
name = "SSH"
priority = 1001
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
# Create network interface(bastion)
resource "azurerm_network_interface" "bastion_nic" {
name = "${var.prefix}-bastion-nic"
location = azurerm_resource_group.sldt_rg.location
resource_group_name = azurerm_resource_group.sldt_rg.name
ip_configuration {
name = "${var.prefix}-nic-configuration"
subnet_id = azurerm_subnet.public_subnet.id
private_ip_address_allocation = "Dynamic"
public_ip_address_id = azurerm_public_ip.bastion_pip.id
}
}
# Connect the security group to the network interface(bastion)
resource "azurerm_network_interface_security_group_association" "bastion_nic_sg_asso" {
network_interface_id = azurerm_network_interface.bastion_nic.id
network_security_group_id = azurerm_network_security_group.bastion_nsg.id
}
# Create public ip(bastion)
resource "azurerm_public_ip" "bastion_pip" {
name = "${var.prefix}-pip"
location = azurerm_resource_group.sldt_rg.location
resource_group_name = azurerm_resource_group.sldt_rg.name
allocation_method = "Static"
domain_name_label = "${var.prefix}-bastion-pip-${random_id.bastion_pip.hex}"
}
# Create virtual machine(bastion)
resource "azurerm_linux_virtual_machine" "bastion" {
name = "${var.prefix}-bastion-vm"
location = azurerm_resource_group.sldt_rg.location
resource_group_name = azurerm_resource_group.sldt_rg.name
network_interface_ids = [azurerm_network_interface.bastion_nic.id]
size = var.vm_size[0]
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
disk_size_gb = "30"
}
source_image_reference {
publisher = var.image_publisher
offer = var.image_offer
sku = var.image_sku
version = var.image_version
}
admin_username = var.vm_username
computer_name = "bastion"
admin_ssh_key {
username = var.vm_username
public_key = file("sshkey/sldt_rsa.pub")
}
tags = {
Name = "${var.prefix}-bastion-vm"
environment = var.environment
}
depends_on = [azurerm_network_interface_security_group_association.bastion_nic_sg_asso]
}
'Terraform' 카테고리의 다른 글
[테라폼] EKS 배포 및 Add on 설치 (0) | 2023.10.20 |
---|---|
[테라폼] Workflow & gitlab runner 프로비저닝 파이프라인 (0) | 2023.08.20 |
[Terraform] HCP Terraform(Terraform Cloud)의 계정 관리 (0) | 2023.08.10 |
[테라폼] State & Module (1) | 2023.07.29 |
[Terraform] Resource Block 구문 문법(count, for_each) (0) | 2023.07.10 |