
Access key, Secret key 생성

aws profile 생성
-- profile 옵션으로 별도 지정 필요하며 defaut profile 설정도 변경 aws configure --profile devprovider aws provider 및 resource 작성
Ubuntu AMI 조회 사이트 에서 적절한 Region 검색 후 resource.ami 작성
instance.tf 파일내용
provider "aws" {
profile = "dev"
region = "us-east-1"
}
resource "aws_instance" "example" {
ami = "ami-0d729a60" //작성 부분
instance_type = "t2.micro"
}
terraform init 명령어 실행
*.tf 구성파일에 정의된 required_providers 블락을 읽고, 필요한 모든 Provider를 외부에서 찾아 다운로드 하는 등 필요한 tool 등을 다운로드하고 준비하는 과정PS C:\Users\USER\PycharmProjects\terraform-course\first-steps> terraform.exe init
instance.tf 파일 내용 작성 후 terraform apply 명령어 입력

PS C:\Users\USER\PycharmProjects\terraform-course\first-steps> aws ec2 describe-instances --instance-
ids i-01a0d116cbcdb5aae --region us-east-1 --query 'Reservations[].Instances[].{State:State.Name,Ty
pe:InstanceType,AZ:Placement.AvailabilityZone,PublicIp:PublicIpAddress,PrivateIp:PrivateIpAddress,SG:SecurityGroups[*].GroupName,Name:Tags[?Key==`Name`]|[0].Value}' --profile dev --output table
-------------------------------------------------------------------------------
| DescribeInstances |
+------------+-------+---------------+---------------+-----------+------------+
| AZ | Name | PrivateIp | PublicIp | State | Type |
+------------+-------+---------------+---------------+-----------+------------+
| us-east-1c| None | 172.31.0.142 | 54.221.25.36 | running | t2.micro |
+------------+-------+---------------+---------------+-----------+------------+
|| SG ||
|+---------------------------------------------------------------------------+|
|| default ||
|+---------------------------------------------------------------------------+|resource 전체속성보기
PS C:\Users\USER\PycharmProjects\terraform-course\first-steps> terraform state show aws_instance.example
# aws_instance.example:
resource "aws_instance" "example" {
ami = "ami-0d729a60"
arn = "arn:aws:ec2:us-east-1:035596491080:instance/i-01a0d116cbcdb5aae"
associate_public_ip_address = true
availability_zone = "us-east-1c"
disable_api_stop = false
disable_api_termination = false
ebs_optimized = false
force_destroy = false
get_password_data = false
hibernation = false
host_id = null
iam_instance_profile = null
id = "i-01a0d116cbcdb5aae"
instance_initiated_shutdown_behavior = "stop"
instance_lifecycle = null
instance_state = "running"
instance_type = "t2.micro"
ipv6_address_count = 0
ipv6_addresses = []
key_name = null
monitoring = false
outpost_arn = null
password_data = null
placement_group = null
placement_group_id = null
placement_partition_number = 0
primary_network_interface_id = "eni-0f8891c26d364b63e"
private_dns = "ip-172-31-0-142.ec2.internal"
private_ip = "172.31.0.142"
public_dns = "ec2-54-221-25-36.compute-1.amazonaws.com"
public_ip = "54.221.25.36"
region = "us-east-1"
secondary_private_ips = []
security_groups = [
"default",
]
source_dest_check = true
spot_instance_request_id = null
subnet_id = "subnet-c956f3bf"
tags_all = {}
tenancy = "default"
user_data_replace_on_change = false
vpc_security_group_ids = [
"sg-1e810d67",
]
capacity_reservation_specification {
capacity_reservation_preference = "open"
}
cpu_options {
amd_sev_snp = null
core_count = 1
threads_per_core = 1
}
credit_specification {
cpu_credits = "standard"
}
enclave_options {
enabled = false
}
maintenance_options {
auto_recovery = "default"
}
metadata_options {
http_endpoint = "enabled"
http_protocol_ipv6 = "disabled"
http_put_response_hop_limit = 1
http_tokens = "optional"
instance_metadata_tags = "disabled"
}
primary_network_interface {
delete_on_termination = true
network_interface_id = "eni-0f8891c26d364b63e"
}
private_dns_name_options {
enable_resource_name_dns_a_record = false
enable_resource_name_dns_aaaa_record = false
hostname_type = "ip-name"
}
root_block_device {
delete_on_termination = true
device_name = "/dev/sda1"
encrypted = false
iops = 0
kms_key_id = null
tags = {}
tags_all = {}
throughput = 0
volume_id = "vol-0f0762fe771e33353"
volume_size = 8
volume_type = "standard"
}
}
생성된 인스턴스 삭제도 명령어 입력으로 삭제진행
terraform destroy
.terraform.lock.hcl : Provider 버전 잠금 파일(Lock 파일)
terraform init 실행할 때 required_providers 블락에서 설정된 버전 제약 조건을 확인하고 가장 적합하고 실제 사용된 특정 버전을 선택 후 그 버전을 파일에 기록하여 고정Terraform AWS 인스턴스 설정
resource 블락안에 하드코딩된 AMI 대신 다른 이미지 사용이 필요한 경우 data resource 블락 선언 data.aws_ami.ubuntu 블록 전체는 AWS에서 특정 조건을 만족하는 AMI를 검색을 위한 것이며 data.aws_ami.ubuntu.id 를 설정 시 최신 Ubuntu 이미지를 사용

AMI Ownership verification으로 어떤 계정이 소유한 AMI를 찾을지 owners 부분에 ID를 지정
provider "aws" {
profile = "dev"
region = "us-east-1"
}
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"] # Canonical, ubuntu.com 의 ubuntu소유자
# AWS 계정 ID default parition 설정
}
resource "aws_instance" "example" {
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"
tags = {
Name = "example"
}
}

기존 instance.tf파일에 data {} 내용을 별도 datasource.tf 파일생성 후 작성
instance.tf
provider "aws" {
profile = "dev"
region = "us-east-1"
}
resource "aws_instance" "example" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
tags = {
Name = "example"
}
}
datasource.tf
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"]
// Canonical, ubuntu.com 의 ubuntu소유자
// https://documentation.ubuntu.com/aws/aws-how-to/instances/find-ubuntu-images/ 링크에 나와 있는 Ownership verification AWS 계정 ID
}
variable "instance_type" {
type = string
default = "t2.micro"
}instance_type="t3.micro"동작확인
terraform plan -var-file dev.tfvars 명령어 실행

type map 포맷 설명
variable.tf 파일 내용variable "instance_type" {
type = map
default = {
"example" = "t2.micro"
"other_instance" = "t4g.micro"
}
}
instance.tf 파일 내용resource "aws_instance" "example" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type["example"] // 또는 var.instance_type.example
tags = {
Name = "example"
}
}
terraform plan 명령어 입력 후 실행 전 debugPS C:\Users\USER\PycharmProjects\terraform-course\first-steps> terraform plan
data.aws_ami.ubuntu: Reading...
data.aws_ami.ubuntu: Read complete after 1s [id=ami-0a03ce9a6035af491]
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:
...
instance_initiated_shutdown_behavior = (known after apply)
+ instance_lifecycle = (known after apply)
+ instance_state = (known after apply)
+ instance_type = "t2.micro"



output "public_ip" {
value = aws_instance.example.public_ip
}
terraform output 명령어로 관련 내용 확인Usage: terraform [global options] state <subcommand> [options] [args]
Subcommands:
identities List the identities of resources in the state
list List resources in the state
mv Move an item in the state
pull Pull current state and output to stdout
push Update remote state from a local state file
replace-provider Replace provider in the state
rm Remove instances from the state
show Show a resource in the stateresource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type["example"]
tags = {
Name = "example"
}
}aws_instance.example에서 aws_instance.web 이름 변경을 위해 resource 블락을 수정 후 terraform apply 명령어 입력하는 경우 aws_instance.web 이라는 새롭게 인스턴스가 생성되면서 Override된다. terraform state mv명령어로 변경할 수 있다.

VPC 생성을위한 tf 파일작성
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["${var.aws_region}a", "${var.aws_region}b", "${var.aws_region}c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_nat_gateway = false
enable_vpn_gateway = false
tags = {
Terraform = "true"
Environment = "dev"
}
}
output "public_subnets" {
value = module.vpc.public_subnets
}
public_subnet 조회 후 인스턴스 생성
public_subnet Outputs 결과
terraform apply -target module.vpc 명령어 입력

instance.tf 파일 수정
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type["example"]
subnet_id = module.vpc.public_subnets[0]
tags = {
Name = "example"
}
}
subnet_id 할당시 Subnet에 지정한 리소스가 없는 경우 자동으로 다음 Subnet 값을 확인해서 인스턴스를 생성한다.terraform apply 입력 후 출력내용

+ public_ip = (known after apply) 으로 출력되는 이유는 아래 AWS VPC모듈 default input 설정이 false이므로 public_ip 자동할당하려면 true 설정 값 추가 필요
vpc.tf : + map_public_ip_on_launch = trueinstance.tf : + associate_public_ip_address = trueterraform apply 명령어 입력


VPC 접속 허용하기 위한 여러 Security Group 중 하나를 선택 적용
instance.tf파일
terraform {
required_providers {
ad = {
source = "hashicorp/ad"
version = "0.5.0"
}
}
}
provider "aws" {
profile = "dev"
region = "us-east-1"
}
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type["example"]
subnet_id = module.vpc.public_subnets[0]
associate_public_ip_address = true
vpc_security_group_ids = [aws_security_group.allow_ssh.id] // ex) security_group_id : sg-12345
key_name = aws_key_pair.mykey.key_name // ssh-key 값
tags = {
Name = "example"
}
}
resource "aws_security_group" "allow_ssh" {
name = "allow_ssh"
description = "Allow SSH inbound traffic and all outbound traffic"
vpc_id = module.vpc.vpc_id
ingress {
description = "SSH from VPC"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
egress {
description = "All outbound traffic"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
tags = {
Name = "allow_ssh"
}
}
resource "aws_key_pair" "mykey" {
key_name = "mykey-demo"
public_key = "ssh-ed25519 <KEY VALUE>"
}
vpc.tf 모듈에 aws vpc를 정의했기 때문에 aws terraform registry outputs 내용을 참고해서 vpc_id 를 입력한다.
terraform apply 명령어 입력 뒤 생성된 인스턴스 ssh key를 통해서 터미널 접속 확인




resource "aws_key_pair" "mykey" {
key_name = "mykey-demo"
public_key = file("${path.module}/mykey.pub")
}${path.module} : 현재 작업 중인 .tf 파일이 있는 절대경로 변수AWS인스턴스가 생성되고 그 위에 소프트웨어 설치하는 방법은 여러가지있는데 한가지는 Ansible이용하거나 SSH접속해서 직접 설치하거나 등등
테라폼에서는 2가지 방법이 있는데 하나는 인스턴스 생성 후 관련 스크립트를 실행하는 것 관령 링크
user_data 는 인스턴스가 생성될 때 제공하는것이며 해당 코드는 인스턴스가 시작될 때 실행됩니다. 재부팅할 때는 동작하지 않고 인스턴스가 생성될 때만 동작해서 user_data를 변경해서 적용하고 싶은 경우 인스턴스를 삭제 후 재생성해야 한다.templates 디렉터리 생성 후 web.tpl 파일 생성
web.tpl
#!/bin/bash
apt-get update
apt-get install -y nginx
rm /var/www/html/index.nginx-debian.html
aws s3 sync s3://${S3_BUCKET_NAME} /var/www/html/
instance.tf
resource "aws_instance" "web" {
...
user_data = templatefile("${path.module}/templates/web.tpl", {
"region" = var.aws_region
"bucketname" = aws_s3_bucket_name.mybucket.bucket
...
})
...
}
user_data : AWS EC2 인스턴스 생성 시 최초 부팅단계에서 실행할 스크립트를 지정하는 속성templatefile(path, vars) : TemplateFile을 읽어와서 제공된 변수들로 파일 내용의 일부를 대체한 후, 최종 결과 문자열을 반환.설명

Local State -> Remote State 스토리지 변경
AWS S3 버킷생성


terraform.tf 파일
terraform {
backend "s3" {
bucket = "ganplank-s3"
key = "first-steps"
region = "us-east-1"
}
}
provider "aws" {
region = "us-east-1"
}
instance.tf 파일 내용 추가
resource "aws_instance" "web" {
...
key_name = aws_key_pair.mykey.key_name // ssh-key 값
user_data = templatefile("${path.module}/templates/web.tpl", {
"region" = var.aws_region
"S3_BUCKET_NAME" = aws_s3_bucket.mybucket.bucket
})
...
resource "aws_s3_bucket" "mybucket" {
bucket = "ganplank-s3"
}
생성결과



terraform apply 적용되면 기존 local storage에서 관리되었던 terraform.tfstate 파일은 빈값이되면서 AWS S3버킷에 terraform.tfstate 파일이 새롭게 생성되어짐을 확인할 수 있다.terraform init -migrate-state 옵션을 추가해 명령어 입력을 수행해야한다. 기존 state는 그대로 두고 현재 설정을 기준으로 다시 초기화하고 싶다면 -reconfigure 옵션을 사용한다.DynamoDB State Locking


terraform apply -target aws_key_pair.mykey 명령어 실행

DynamoDB 해당 항목 편집을 눌러보면 속성에 AWS S3 버킷 이름으로 Lock ID 가 작성되어있는 것을 볼 수 있다.


terraform apply 후 yes 입력 받는 대기화면에서 새로운 다른 터미널을 열어서 terraform apply 입력 시 lock 이 걸려있다는 메시지와 함께 강제 해제 방법도 알려주고있다.
terraform apply -lock=flase 옵션으로 lock 을 무시하고 실행하는 방법과 terraform force-unlock <Lock ID> 위 화면 Lock info 출력되는 ID 값으로 lock 을 강제 해제시킬 수 있다.terraofrm init -migrate-state 입력 후 terraoform destory 한다.계정 ID를 얻기 쉬워서 별도 계정 ID를 하드코딩하지 않아도된다. 계정 ID를 불러오기위해 aws_caller 사용하고자 한다면 하드코딩할 수 있는데 하드코딩하는 데이터소스가 많아지면 모든 AWS호출을 해야하기 때문에 terraform apply 속도가 느려질 수 있다.
datasource.tf 파일data "aws_caller_identity" "current" {}
data "aws_region" "current" {}outputs.tfoutput "account_id" {
value = data.aws_caller_identity.current.account_id
}terraform apply 명령어 입력 후 Outputs 확인
terraform apply 명령어로 ec2 생성해서 vpc_id 확인
terraform import aws_s3_bucket.mybucket <bucket name> 명령어로 backend 로 import 시킨다.datasource.tf 파일을 통해 ec2 생성 후 다른 프로젝트에서 해당 remote state를 읽어서 정보를 활용하는 방법이 있다.
datasource로 remote state를 읽으면 구성 간에 데이터를 공유할 것이기 때문에 주의가 필요하다.
Output을 읽기위해 전체 state 파일에 접근할 필요가 있고 수동으로 사용자가 디렉터리 들어가서 전체 파일을 읽을 수 있으면 Outputs 이상의 많은 정보를 확인할 수 있다.
remote state에서 outputs값만 SSM Parameter에 따로 publish해서, 그걸 aws_ssm_parameter data source로 읽게 만들고 IAM으로 접근제어를 할 수 있다.

위 Basic example에 Key:Value를 작성할 수 있다. vpc_id를 parameter store 어딘가에 작성하고 aws_ssm_parameter 로 읽을 수 있다.
remote state 파일을 읽으면 노출되지 말아야하는 정보들도 제공되어 공유하고 싶은값만 AWS SSM Parameter Store에 저장하고 다른 프로젝트에서는 data "aws_ssm_prameter"를 통해 값을 읽게 할 수 있다.
주의할 점은 값이 parameter store에 암호화되어 저장되더라도 remote state에서는 항상 암호화되어 저장되지 않기 때문에 상태파일에 아무도 접근하지 못하도록 제어하고 AWS S3 암호화를 사용하여 전체파일이 암호화되도록 해야한다.
위 설정 예시를 참고해서 datasource-demo 디렉터리(다른 프로젝트)를 생성 후 datasource.tf 파일에 아래 내용 작성
기존 terraform.tf에 backend "s3"{} 블락 안에 bucket, key, region 내용을 가져와서 remote state를 읽을 수 있는 위치를 정의하고 읽은 내용 중 vpc_id 를 출력할 수 있도록 output "vpc_id" {} 작성
data "terraform_remote_state" "first-steps" {
backend = "s3"
config = {
bucket = "ganplank-s3-20251111-mybucket"
key = "first-steps/terraform.tfstate"
region = "us-east-1"
}
}
output "vpc_id" {
value = data.terraform_remote_state.first-steps.outputs.vpc_id
}
terraform apply 입력 시 이미 remote state 파일을 읽고 그대로 remote state 파일을 해당 디렉터리 안에 파일을 생성

data.terraform_remote_state.first-steps: Reading... 로그에서 첫 번째 프로젝트의 remote backend(AWS S3)에서 Terraform state 파일을 읽는것을 확인할 수 있따.
Changes to Outputs: + vpc_id = "..."새 프로젝트 입장에서보면 원래는 output "vpc_id"가 state에 없었고, 이번에는 처음 생기는 거라서 + 표시 됨, 서비스 영향없이 output 만 추가
결론적으로 첫 번째 프로젝트에서 output으로 내보낸 vpc_id를 두번째 프로젝트가 읽어서 자기 output으로 다시 노출시킴
기존 프로젝트에서 만들어둔 VPC의 vpc_id 값을 읽어와서 이 새 프로젝트의 state에 output 으로 저장했다.
"resources": [
{
"mode": "data",
"type": "terraform_remote_state",
"name": "first-steps",
"provider": "provider[\"terraform.io/builtin/terraform\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
"backend": "s3",
"config": {
"value": {
"bucket": "ganplank-s3-20251111-mybucket",
"key": "first-steps/terraform.tfstate",
"region": "us-east-1"
},
remote state 읽고 output 하는 과정에서 value 변수에 들어가는 remote_state.first-steps.outputs.vpc_id 값이 길어서 사용하기 어려운데 Alias같은 개념으로 local Variable을 사용할 수 있다.
locals {
vpc_id = data.terraform_remote_state.first-steps.outputs.vpc_id
}
output "vpc_id" {
value = local.vpc_id
}

awscc는 Terraform을 사용하여 AWS 인프라를 프로비저닝할 떄 사용하는 도구, 기존의 표준 AWS Provider와는 보완적인 관계1. Auto-Generated2. 신속한 리소스 지원terraform validate : 구문 오류 확인
terraform plan : plan을 먼저 실행하고 저장한 후 나중에 실행하는 방식으로 CI/CD 환경에서 많이 사용한다고 한다.
terraform fmt : 구성 파일을 표준 스타일로 재포맷
terraform get : 구성 모듈을 불러오기
terraform init -upgrade : terraform.lock.hcl 파일에 명시되어있는 최신 버전으로 업그레이드
terraform graph : 의존성 보기, 예를들어 아래 resource 처럼 보안 그룹은 모듈 VPC의 vpc_id를 가지고 있으니까 보안그룹 생성하기전에 먼저 vpc를 생성해야 한다.
resource "aws_security_group" "allow_ssh" {
name = "allow_ssh"
description = "Allow SSH inbound traffic and all outbound traffic"
vpc_id = module.vpc.vpc_id
terraform import : AS3 스토리지 생성할 때 이미 만들어진 AS3 스토리지를 사용할 경우 사용한다.
terraform login : terraform cloud에 로긍니하기 위한 것, logout 마찬가지이다
terraform output : applt 했을 때 관련 output 출력
terraform provider : 어떤 provider 사용하는지 확인
terraform refresh : 원격에서 상태가 변경되었는지 확인 예를들어 AWS 변경이 있을 시 refresh를 통해 terraform state를 갱신하여 remote state와 일치시킨다.
terraform show : 현재 저장된 plan 을 본다.
terraform tainit : 리소스 인스턴스를 오나전ㅇ히 기능하지 않는 것으로 표시