Overview
Terraform은 인프라를 코드로 관리할 수 있게 해주는 강력한 도구이며, 변수(variable)를 잘 활용하면 코드의 재사용성, 유연성, 가독성을 대폭 향상시킬 수 있다.
이번 글에서는 Terraform에서 변수의 정의와 적용 방식, .tfvars를 통한 값 지정 방법, 그리고 우선순위 및 실제 EC2 예제를 통해 변수 기반 인프라 관리 방법을 실습해보았다.
Terraform의 주요 명령어인 `init, plan, apply, state list, destroy` 명령어의 실행 흐름과 함께
Terraform이 어떻게 변수와 상태를 활용하여 인프라를 생성하고 추적하는지도 함께 다루었다.
📅 관련 글
2023.03.30 - [IaC/Infrastructure Provisioning] - 1. Terraform 기초 : 설치 및 .tf 파일 설명
2023.03.31 - [IaC/Infrastructure Provisioning] - 2. Terraform 변수 사용법(use-variable) - AWS
2023.04.03 - [IaC/Infrastructure Provisioning] - 4. Terraform의 다양한 Function(함수)
2023.04.04 - [IaC/Infrastructure Provisioning] - 5. Terraform의 다양한 Expression
Terraform 변수 사용법
Terraform에서는 변수(variables)를 사용하여 코드의 유연성을 높일 수 있다. 변수를 활용하면 같은 Terraform 코드로 다양한 환경에서 인프라를 구축할 수 있으며, 코드의 유지보수성을 향상시킬 수 있다.
먼저 소스코드 주소는 아래와 같다.
https://github.com/somaz94/terraform-study-aws/tree/main/use-variable
소스코드를 받아, 내 환경에 맞게 값을 넣어준다.
terraform.tvars 파일만 수정해주면 된다.
당연히 키페어와 aws configure은 설정한 상태여야 한다.
2023.03.31 - [AWS] - AWS CLI 정리
1. 변수 정의하기 (`variables.tf`)
Terraform에서 변수를 정의하는 파일은 `variables.tf` 이다. 각 변수는 variable 블록을 사용하여 정의할 수 있으며, 기본값, 타입, 설명 등을 지정할 수 있다.
variable "region" {
description = "The AWS region to create resources in"
default = "us-west-2"
}
variable "instance_type" {
description = "The EC2 instance type"
default = "t2.micro"
}
variable "environment" {
description = "The environment for the infrastructure (e.g., development, staging, production)"
type = string
default = "development" # Optional: Provide a default value if desired
}
variable "terraform" {
description = "This resource means that it was created with terraform (e.g., development, staging, production)"
type = string
default = "true" # Optional: Provide a default value if desired
}
variable "my_public_ip" {
description = "Your public IP address in CIDR notation (e.g., x.x.x.x/32)"
type = string
}
variable "aws_profile" {
description = "The AWS CLI profile to use for authentication"
type = string
default = "somaz" # Optional: Provide a default profile name if desired
}
variable "key_pair_name" {
description = "The name of the key pair to use for the EC2 instance"
type = string
}
2. 변수 값 지정하기 (`terraform.tfvars`)
변수 값은 `terraform.tfvars` 파일에 정의할 수 있다. 이를 통해 환경별로 다른 값을 쉽게 적용할 수 있다.
# terraform.tfvars
region = "ap-northeast-2"
instance_type = "t2.micro"
environment = "pratice"
my_public_ip = "x.x.x.x/32" # Replace x.x.x.x with your public IP address
aws_profile = "your_aws_profile" # Replace "your_aws_profile" with the name of the AWS profile you want to use
key_pair_name = "your_key_pair_name" # Replace "your_key_pair_name" with the name of the key pair you want to use
- 이 파일을 사용하면 Terraform 실행 시 자동으로 해당 값을 불러온다.
3. 변수 값 적용 우선순위
Terraform에서 변수 값을 적용하는 우선순위는 다음과 같다.
- `-var` 옵션을 사용한 명령어 입력
- `.tfvars` 파일 (예: `terraform.tfvars`)
- 환경 변수 (`TF_VAR_ ` 접두사를 사용)
- `variable` 블록 내 기본값 (default 값)
예제 명령어
terraform apply -var "region=us-east-1"
Terraform 명령어
Terraform에서 변수를 활용하여 인프라를 생성하는 주요 명령어를 정리하겠다.
1. `terraform init`
새 Terraform 프로젝트를 시작하거나 기존 프로젝트를 복제할 때 실행해야 하는 첫 번째 명령이다. 다음 작업을 수행하여 Terraform 작업 디렉터리를 초기화한다.
백엔드 초기화(Backend Initialization)
- Terraform 구성이 상태 파일을 저장하기 위한 백엔드를 지정하는 경우 terraform init가 백엔드를 설정하고 구성한다. 상태 파일은 Terraform에서 인프라 리소스를 추적하는 데 사용되므로 특히 팀과 함께 작업할 때 안정적이고 안전한 스토리지 솔루션을 갖추는 것이 중요하다.
공급자 다운로드(Provider Download)
- Terraform은 공급자를 사용하여 AWS, Azure, Google Cloud 등과 같은 다양한 서비스와 상호 작용한다. Terraform 구성에서 사용 중인 공급자를 정의하면 terraform init가 Terraform 레지스트리에서 필요한 공급자 플러그인을 다운로드한다. 작업 디렉토리 내의 .terraform 디렉토리에 이러한 플러그인을 캐시하므로 공급자 버전을 변경하거나 새 작업 디렉토리를 초기화하지 않는 한 플러그인을 다시 다운로드할 필요가 없다.
공급자 구성(Provider Configuration)
- terraform init는 또한 Terraform 구성 파일에서 정의한 설정을 기반으로 공급자를 구성한다. 여기에는 인증 세부 정보, 특정 API 버전 또는 공급자가 올바르게 작동하는 데 필요한 기타 설정이 포함될 수 있다.
$ ls
main.tf output.tf terraform.tfvars variables.tf version.tf
$ terraform init
Initializing the backend...
Initializing provider plugins...
- Finding latest version of hashicorp/aws...
- Installing hashicorp/aws v4.61.0...
- Installed hashicorp/aws v4.61.0 (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.
2. `terraform plan`
테라폼 코드로 실행 또는 변경될 인프라 내역을 보여준다.
$ terraform plan
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:
# aws_instance.ec2 will be created
+ resource "aws_instance" "ec2" {
+ ami = "ami-0a67d09dc01633721"
+ arn = (known after apply)
+ associate_public_ip_address = (known after apply)
+ availability_zone = (known after apply)
+ cpu_core_count = (known after apply)
+ cpu_threads_per_core = (known after apply)
+ disable_api_stop = (known after apply)
+ disable_api_termination = (known after apply)
+ ebs_optimized = (known after apply)
+ get_password_data = false
+ host_id = (known after apply)
+ host_resource_group_arn = (known after apply)
+ iam_instance_profile = (known after apply)
+ id = (known after apply)
+ instance_initiated_shutdown_behavior = (known after apply)
+ instance_state = (known after apply)
+ instance_type = "t2.micro"
+ ipv6_address_count = (known after apply)
+ ipv6_addresses = (known after apply)
+ key_name = (known after apply)
+ monitoring = (known after apply)
+ outpost_arn = (known after apply)
+ password_data = (known after apply)
+ placement_group = (known after apply)
+ placement_partition_number = (known after apply)
+ primary_network_interface_id = (known after apply)
+ private_dns = (known after apply)
+ private_ip = (known after apply)
+ public_dns = (known after apply)
+ public_ip = (known after apply)
+ secondary_private_ips = (known after apply)
+ security_groups = (known after apply)
+ source_dest_check = true
+ subnet_id = (known after apply)
+ tags_all = {
+ "Environment" = "pratice"
+ "Terraform" = "true"
}
+ tenancy = (known after apply)
+ user_data = (known after apply)
+ user_data_base64 = (known after apply)
+ user_data_replace_on_change = false
+ vpc_security_group_ids = (known after apply)
}
# aws_security_group.allow_ssh will be created
+ resource "aws_security_group" "allow_ssh" {
+ arn = (known after apply)
+ description = "Allow SSH inbound traffic"
+ egress = (known after apply)
+ id = (known after apply)
+ ingress = [
+ {
+ cidr_blocks = [
+ "xx.xx.xx.xx/32",
]
+ description = ""
+ from_port = 22
+ ipv6_cidr_blocks = []
+ prefix_list_ids = []
+ protocol = "tcp"
+ security_groups = []
+ self = false
+ to_port = 22
},
]
+ name = "allow_ssh"
+ name_prefix = (known after apply)
+ owner_id = (known after apply)
+ revoke_rules_on_delete = false
+ tags_all = {
+ "Environment" = "pratice"
+ "Terraform" = "true"
}
+ vpc_id = (known after apply)
}
Plan: 2 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ instance_id = (known after apply)
+ public_ip = (known after apply)
+ security_group_id = (known after apply)
+ security_group_ingress_rules = [
+ {
+ cidr_blocks = [
+ "xx.xx.xx.xx/32",
]
+ description = ""
+ from_port = 22
+ ipv6_cidr_blocks = []
+ prefix_list_ids = []
+ protocol = "tcp"
+ security_groups = []
+ self = false
+ to_port = 22
},
]
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
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.
3. `terraform apply`
Terraform 구성 파일에 설명된 대로 인프라의 원하는 상태에 도달하는 데 필요한 변경 사항을 적용하기 위해 Terraform에서 사용되는 명령이다.
구성 로드: Terraform은 정의한 리소스와 원하는 상태를 이해하기 위해 작업 디렉터리에서 구성 파일(예: .tf 파일)을 읽는다.
상태 새로 고침: Terraform은 리소스에 대한 최신 정보를 얻기 위해 원격 공급자(예: AWS, Azure, Google Cloud)를 쿼리하여 인프라의 현재 상태를 확인한다. 해당 정보는 Terraform 상태 파일(일반적으로 terraform.tfstate라고 함)에 저장된다.
실행 계획 생성: Terraform은 구성 파일의 원하는 상태를 상태 파일의 현재 상태와 비교한다. 그런 다음 Terraform이 원하는 상태에 도달하기 위해 각 리소스에서 수행할 작업(만들기, 업데이트 또는 삭제)을 나열하는 실행 계획을 만든다.
확인 메시지 표시: 변경 사항을 적용하기 전에 Terraform은 실행 계획을 표시하고 확인 메시지를 표시한다. 수행할 작업에 대한 요약이 표시되며 "예"를 입력하여 계속 진행할 것인지 확인해야 힌다.
변경 사항 적용: 실행 계획을 확인하면 Terraform이 원격 공급자에게 API 호출을 수행하여 변경 사항을 적용하기 시작한다. 필요에 따라 리소스를 생성, 업데이트 또는 삭제하고 인프라의 새 상태를 반영하도록 Terraform 상태 파일을 업데이트한다
$ terraform apply
aws_security_group.allow_ssh: Refreshing state... [id=sg-08e39387827a8c39d]
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:
# aws_instance.ec2 will be created
+ resource "aws_instance" "ec2" {
+ ami = "ami-03221589fd7c8f183"
+ arn = (known after apply)
+ associate_public_ip_address = (known after apply)
+ availability_zone = (known after apply)
+ cpu_core_count = (known after apply)
+ cpu_threads_per_core = (known after apply)
+ disable_api_stop = (known after apply)
+ disable_api_termination = (known after apply)
+ ebs_optimized = (known after apply)
+ get_password_data = false
+ host_id = (known after apply)
+ host_resource_group_arn = (known after apply)
+ iam_instance_profile = (known after apply)
+ id = (known after apply)
+ instance_initiated_shutdown_behavior = (known after apply)
+ instance_state = (known after apply)
+ instance_type = "t2.micro"
+ ipv6_address_count = (known after apply)
+ ipv6_addresses = (known after apply)
+ key_name = (known after apply)
+ monitoring = (known after apply)
+ outpost_arn = (known after apply)
+ password_data = (known after apply)
+ placement_group = (known after apply)
+ placement_partition_number = (known after apply)
+ primary_network_interface_id = (known after apply)
+ private_dns = (known after apply)
+ private_ip = (known after apply)
+ public_dns = (known after apply)
+ public_ip = (known after apply)
+ secondary_private_ips = (known after apply)
+ security_groups = (known after apply)
+ source_dest_check = true
+ subnet_id = (known after apply)
+ tags_all = {
+ "Environment" = "pratice"
+ "Terraform" = "true"
}
+ tenancy = (known after apply)
+ user_data = (known after apply)
+ user_data_base64 = (known after apply)
+ user_data_replace_on_change = false
+ vpc_security_group_ids = [
+ "sg-08e39387827a8c39d",
]
}
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ instance_id = (known after apply)
+ public_ip = (known after apply)
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
aws_instance.ec2: Creating...
aws_instance.ec2: Still creating... [10s elapsed]
aws_instance.ec2: Still creating... [20s elapsed]
aws_instance.ec2: Still creating... [30s elapsed]
aws_instance.ec2: Creation complete after 31s [id=i-0a0f6345fef2c9479]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
instance_id = "i-0a0f6345fef2c9479"
public_ip = "xx.xxx.xxx.xx"
security_group_id = "sg-08e39387827a8c39d"
security_group_ingress_rules = toset([
{
"cidr_blocks" = tolist([
"xx.xx.xx.xx/32",
])
"description" = ""
"from_port" = 22
"ipv6_cidr_blocks" = tolist([])
"prefix_list_ids" = tolist([])
"protocol" = "tcp"
"security_groups" = toset([])
"self" = false
"to_port" = 22
},
])
인스턴스 접속
$ ssh -i ~/.ssh/somazkey.pem ec2-user@13.125.247.89
The authenticity of host '13.125.247.89 (13.125.247.89)' can't be established.
ECDSA key fingerprint is SHA256:qB1CKVTSuAgErKIaM5liAptF0bEqx3/8HwuAFIsyyAc.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '13.125.247.89' (ECDSA) to the list of known hosts.
, #_
~\_ ####_ Amazon Linux 2023
~~ \_#####\
~~ \###|
~~ \#/ ___ https://aws.amazon.com/linux/amazon-linux-2023
~~ V~' '->
~~~ /
~~._. _/
_/ _/
_/m/'
[ec2-user@ip-172-31-39-50 ~]$ ls
[ec2-user@ip-172-31-39-50 ~]$
4. `terraform state list`
Terraform 상태 파일에서 현재 추적되는 리소스 목록을 표시하기 위해 Terraform에서 사용되는 명령이다. 상태 파일(일반적으로 `terraform.tfstate` 라고 함)은 Terraform 구성 파일에 정의된 리소스와 원격 공급자(예: AWS, Azure, Google Cloud)에서 생성된 실제 리소스 간의 매핑을 저장한다.
아래와 같이 `tfstate` 파일이 생겼다.
$ ls
main.tf output.tf terraform.tfstate terraform.tfvars variables.tf version.tf
terraform의 상태를 확인하는 명령어이다.
$ terraform state list
aws_instance.ec2
aws_security_group.allow_ssh
5. `terraform destroy`
현재 Terraform 구성에서 관리 중인 모든 리소스를 삭제하거나 제거하기 위해 Terraform에서 사용되는 명령이다. 이 명령은 일반적으로 인프라를 정리하고 Terraform에서 생성한 모든 리소스를 제거하려는 경우에 사용된다.
terraform destroy를 실행하면 Terraform이 다음을 수행합니다.
- Terraform 구성 파일과 현재 상태 파일을 분석한다.
- 주의 모든 자원을 파괴할 계획을 세운다.
- 리소스 destroy를 진행할 것인지 확인하는 메시지가 표시된다.
- destroy 계획을 실행하고 인프라에서 리소스를 제거한다.
$ terraform destroy
aws_security_group.allow_ssh: Refreshing state... [id=sg-08e39387827a8c39d]
aws_instance.ec2: Refreshing state... [id=i-0a0f6345fef2c9479]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
- destroy
Terraform will perform the following actions:
# aws_instance.ec2 will be destroyed
- resource "aws_instance" "ec2" {
- ami = "ami-03221589fd7c8f183" -> null
- arn = "arn:aws:ec2:ap-northeast-2:611841095956:instance/i-0a0f6345fef2c9479" -> null
- associate_public_ip_address = true -> null
- availability_zone = "ap-northeast-2c" -> null
- cpu_core_count = 1 -> null
- cpu_threads_per_core = 1 -> null
- disable_api_stop = false -> null
- disable_api_termination = false -> null
- ebs_optimized = false -> null
- get_password_data = false -> null
- hibernation = false -> null
- id = "i-0a0f6345fef2c9479" -> null
- instance_initiated_shutdown_behavior = "stop" -> null
- instance_state = "running" -> null
- instance_type = "t2.micro" -> null
- ipv6_address_count = 0 -> null
- ipv6_addresses = [] -> null
- monitoring = false -> null
- placement_partition_number = 0 -> null
- primary_network_interface_id = "eni-0d96819db9cba0ccf" -> null
- private_dns = "ip-172-31-32-143.ap-northeast-2.compute.internal" -> null
- private_ip = "172.31.32.143" -> null
- public_dns = "ec2-xx-xxx-xxx-xx.ap-northeast-2.compute.amazonaws.com" -> null
- public_ip = "xx.xxx.xxx.xx" -> null
- secondary_private_ips = [] -> null
- security_groups = [
- "allow_ssh",
] -> null
- source_dest_check = true -> null
- subnet_id = "subnet-04c0af235bda13016" -> null
- tags = {} -> null
- tags_all = {
- "Environment" = "pratice"
- "Terraform" = "true"
} -> null
- tenancy = "default" -> null
- user_data_replace_on_change = false -> null
- vpc_security_group_ids = [
- "sg-08e39387827a8c39d",
] -> null
- capacity_reservation_specification {
- capacity_reservation_preference = "open" -> null
}
- credit_specification {
- cpu_credits = "standard" -> null
}
- enclave_options {
- enabled = false -> null
}
- maintenance_options {
- auto_recovery = "default" -> null
}
- metadata_options {
- http_endpoint = "enabled" -> null
- http_put_response_hop_limit = 2 -> null
- http_tokens = "required" -> null
- instance_metadata_tags = "disabled" -> null
}
- private_dns_name_options {
- enable_resource_name_dns_a_record = false -> null
- enable_resource_name_dns_aaaa_record = false -> null
- hostname_type = "ip-name" -> null
}
- root_block_device {
- delete_on_termination = true -> null
- device_name = "/dev/xvda" -> null
- encrypted = false -> null
- iops = 3000 -> null
- tags = {} -> null
- throughput = 125 -> null
- volume_id = "vol-00f49a9b3ca06d1b3" -> null
- volume_size = 8 -> null
- volume_type = "gp3" -> null
}
}
# aws_security_group.allow_ssh will be destroyed
- resource "aws_security_group" "allow_ssh" {
- arn = "arn:aws:ec2:ap-northeast-2:611841095956:security-group/sg-08e39387827a8c39d" -> null
- description = "Allow SSH inbound traffic" -> null
- egress = [] -> null
- id = "sg-08e39387827a8c39d" -> null
- ingress = [
- {
- cidr_blocks = [
- "14.32.77.203/32",
]
- description = ""
- from_port = 22
- ipv6_cidr_blocks = []
- prefix_list_ids = []
- protocol = "tcp"
- security_groups = []
- self = false
- to_port = 22
},
] -> null
- name = "allow_ssh" -> null
- owner_id = "611841095956" -> null
- revoke_rules_on_delete = false -> null
- tags = {} -> null
- tags_all = {
- "Environment" = "pratice"
- "Terraform" = "true"
} -> null
- vpc_id = "vpc-0be33812ca98ab8fa" -> null
}
Plan: 0 to add, 0 to change, 2 to destroy.
Changes to Outputs:
- instance_id = "i-0a0f6345fef2c9479" -> null
- public_ip = "xx.xxx.xxx.xx" -> null
- security_group_id = "sg-08e39387827a8c39d" -> null
- security_group_ingress_rules = [
- {
- cidr_blocks = [
- "14.32.77.203/32",
]
- description = ""
- from_port = 22
- ipv6_cidr_blocks = []
- prefix_list_ids = []
- protocol = "tcp"
- security_groups = []
- self = false
- to_port = 22
},
] -> null
Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yes
aws_instance.ec2: Destroying... [id=i-0a0f6345fef2c9479]
aws_instance.ec2: Still destroying... [id=i-0a0f6345fef2c9479, 10s elapsed]
aws_instance.ec2: Still destroying... [id=i-0a0f6345fef2c9479, 20s elapsed]
aws_instance.ec2: Destruction complete after 29s
aws_security_group.allow_ssh: Destroying... [id=sg-08e39387827a8c39d]
aws_security_group.allow_ssh: Destruction complete after 1s
Destroy complete! Resources: 2 destroyed.
마무리
Terraform에서 변수를 잘 사용하는 것은 효율적인 인프라 코드 작성의 핵심이다.
변수 하나로 여러 환경에 동일한 코드를 적용할 수 있고, `.tfvars` 파일로 손쉽게 값만 바꿔 적용할 수 있어 유지보수 또한 쉬워진다.
특히 실무에서는 변수에 대한 기본 개념뿐만 아니라, `locals, outputs, input variables, modules` 등 고급 개념까지 익히는 것이 중요하다.
다음 글에서는 이러한 고급 변수 활용법과 실무 팁에 대해 이어서 다뤄보도록 하겠다.
Terraform을 꾸준히 연습하다 보면, 어느새 복잡한 인프라도 코드 한 줄로 깔끔하게 관리할 수 있게 될 것이다.
Reference
https://developer.hashicorp.com/terraform/language/state
https://developer.hashicorp.com/terraform/language/values/variables
'IaC > Infrastructure Provisioning' 카테고리의 다른 글
4. Terraform의 다양한 Function(함수) (0) | 2023.04.11 |
---|---|
3. Terraform 다양한 변수(variable, local, data...output, input) - AWS (0) | 2023.04.09 |
1. Terraform 기초 : 설치 및 .tf 파일 설명 (0) | 2023.04.04 |
Packer란? (0) | 2022.09.05 |
Vagrant란? (0) | 2022.08.25 |