Terraform


Terraform

IaC (Infra as Code) 인프라를 코드로 저장해두는 것

AWS CloudFormation과 같은 방식이지만, Azure나 AWS, 온프레미스 모두에서 활용이 가능함.

 

간단한 사용법

 

1. 적당한 EC2 인스턴스 생성

전부 디폴트로 했고, 키페어는 pem키로 생성

 

2. ssh로 EC2 인스턴스 접속

 

3. terraform 설치

Install | Terraform | HashiCorp Developer

 

Install | Terraform | HashiCorp Developer

Explore Terraform product documentation, tutorials, and examples.

developer.hashicorp.com

 

EC2 이므로 Linux > Amazon Linux 선택 후 설치 진행

[ec2-user@ip-172-31-44-144 ~]$ terraform version
Terraform v1.7.5
on linux_amd64

 

+ 자동완성 되게끔 하는 법

[ec2-user@ip-172-31-44-144 ~]$ touch ~/.zshrc
[ec2-user@ip-172-31-44-144 ~]$ terraform -install-autocomplete
[ec2-user@ip-172-31-44-144 ~]$ cat ~/.zshrc

autoload -U +X bashcompinit && bashcompinit
complete -o nospace -C /usr/bin/terraform terraform

[ec2-user@ip-172-31-44-144 ~]$ source .bashrc

# 자동완성 됨.

 

4. 테라폼 파일 생성 (vscode로 해도 된다. 하지만 나는 그냥 vim으로 작성할 것임) 연습

 

+ vim으로 하려면 vim-terraform plugin을 설치하는게 편집하는 데 편함.

git clone https://github.com/hashivim/vim-terraform.git ~/.vim/pack/plugins/start/vim-terraform

 

더보기

간단한 tf파일 작성하고

[ec2-user@ip-172-31-44-144 aws_project]$ cat test.tf
resource "aws_instance" "tf-EC2-1" {
  instance_type = "t2.micro"
  tags = {
          Name = "guaba_terraform"
        }
}

문법 체크해보면 안된다고 한다.

[ec2-user@ip-172-31-44-144 aws_project]$ terraform validate # 테라폼 문법체크
╷
│ Error: Missing required provider
│
│ This configuration requires provider registry.terraform.io/hashicorp/aws, but that provider isn't
│ available. You may be able to install it automatically by running:
│   terraform init
╵

terraform init을 해주자.

[ec2-user@ip-172-31-44-144 aws_project]$ terraform init

Initializing the backend...

Initializing provider plugins...
- Finding latest version of hashicorp/aws...
- Installing hashicorp/aws v5.43.0...
- Installed hashicorp/aws v5.43.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.

헤헤 문법 에러난다. ami 추가 해줘야 한다고 함.

[ec2-user@ip-172-31-44-144 aws_project]$ terraform validate
╷
│ Error: Missing required argument
│
│   with aws_instance.tf-EC2-1,
│   on test.tf line 1, in resource "aws_instance" "tf-EC2-1":
│    1: resource "aws_instance" "tf-EC2-1" {
│
│ "ami": one of `ami,launch_template` must be specified
╵

ami를 추가해줘야 한다.

EC2 > AMI 카탈로그에서 AMI를 확인 할 수 있다.

이제 문법 완성

[ec2-user@ip-172-31-44-144 aws_project]$ cat test.tf
terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "5.43.0"
    }
  }
}

provider "aws" {}

resource "aws_instance" "tf-EC2-1" {
  ami = "ami-0aed24d0abcdffd14" # Red Hat Enterprise Linux 9 (HVM), SSD Volume Type
  instance_type = "t2.micro"
  tags = {
          Name = "guaba_terraform"
        }
}

 

문법 체크.

[ec2-user@ip-172-31-44-144 aws_project]$ terraform validate
Success! The configuration is valid.

 

terraform plan으로 확인을 해보면 에러가 뜨게 되는데

[ec2-user@ip-172-31-44-144 aws_project]$ terraform plan

Planning failed. Terraform encountered an error while generating this plan.

╷
│ Error: No valid credential sources found
│
│   with provider["registry.terraform.io/hashicorp/aws"],
│   on test.tf line 10, in provider "aws":
│   10: provider "aws" {
│
│ Please see https://registry.terraform.io/providers/hashicorp/aws
│ for more information about providing credentials.
│
│ Error: failed to refresh cached credentials, no EC2 IMDS role found, operation error ec2imds:
│ GetMetadata, http response error StatusCode: 404, request to EC2 IMDS failed
│

 

aws cli 설치한 후 aws configure로 자격증명을 해주면

[ec2-user@ip-172-31-44-144 aws_project]$ 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.tf-EC2-1 will be created
  + resource "aws_instance" "tf-EC2-1" {
      + ami                                  = "ami-0aed24d0abcdffd14"
      + 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_lifecycle                   = (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
      + spot_instance_request_id             = (known after apply)
      + subnet_id                            = (known after apply)
      + tags                                 = {
          + "Name" = "guaba_terraform"
        }
      + tags_all                             = {
          + "Name" = "guaba_terraform"
        }
      + 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)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

───────────────────────────────────────────────────────────────────────────────────────────────────────

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.

apply로 실행을 했을 때 무엇이 생성 될 지 말해준다. 

 

5. 테라폼파일 적용 (terraform apply)

[ec2-user@ip-172-31-44-144 aws_project]$ terraform apply

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.tf-EC2-1 will be created
  + resource "aws_instance" "tf-EC2-1" {
      + ami                                  = "ami-0aed24d0abcdffd14"
      + 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_lifecycle                   = (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
      + spot_instance_request_id             = (known after apply)
      + subnet_id                            = (known after apply)
      + tags                                 = {
          + "Name" = "guaba_terraform"
        }
      + tags_all                             = {
          + "Name" = "guaba_terraform"
        }
      + 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)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

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.tf-EC2-1: Creating...
aws_instance.tf-EC2-1: Still creating... [10s elapsed]
aws_instance.tf-EC2-1: Still creating... [20s elapsed]
aws_instance.tf-EC2-1: Still creating... [30s elapsed]
aws_instance.tf-EC2-1: Creation complete after 31s [id=i-0a9aabf9b68afbd18]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

 

삭제는 terraform destroy

[ec2-user@ip-172-31-44-144 aws_project]$ terraform destroy
aws_instance.tf-EC2-1: Refreshing state... [id=i-0a9aabf9b68afbd18]

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.tf-EC2-1 will be destroyed
  - resource "aws_instance" "tf-EC2-1" {
      - ami                                  = "ami-0aed24d0abcdffd14" -> null
      - arn                                  = "arn:aws:ec2:ap-northeast-2:975050281973:instance/i-0a9aabf9b68afbd18" -> 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-0a9aabf9b68afbd18" -> 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-007a1bd82530a7db2" -> null
      - private_dns                          = "ip-172-31-32-208.ap-northeast-2.compute.internal" -> null
      - private_ip                           = "172.31.32.208" -> null
      - public_dns                           = "ec2-13-125-17-106.ap-northeast-2.compute.amazonaws.com" -> null
      - public_ip                            = "13.125.17.106" -> null
      - secondary_private_ips                = [] -> null
      - security_groups                      = [
          - "default",
        ] -> null
      - source_dest_check                    = true -> null
      - subnet_id                            = "subnet-0b5b3c57bacea9640" -> null
      - tags                                 = {
          - "Name" = "guaba_terraform"
        } -> null
      - tags_all                             = {
          - "Name" = "guaba_terraform"
        } -> null
      - tenancy                              = "default" -> null
      - user_data_replace_on_change          = false -> null
      - vpc_security_group_ids               = [
          - "sg-031bd9fd4b9b74f3d",
        ] -> null

      - capacity_reservation_specification {
          - capacity_reservation_preference = "open" -> null
        }

      - cpu_options {
          - core_count       = 1 -> null
          - threads_per_core = 1 -> 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_protocol_ipv6          = "disabled" -> null
          - http_put_response_hop_limit = 1 -> null
          - http_tokens                 = "optional" -> 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/sda1" -> null
          - encrypted             = false -> null
          - iops                  = 3000 -> null
          - tags                  = {} -> null
          - tags_all              = {} -> null
          - throughput            = 125 -> null
          - volume_id             = "vol-04f8e8efa77a3091d" -> null
          - volume_size           = 10 -> null
          - volume_type           = "gp3" -> null
        }
    }

Plan: 0 to add, 0 to change, 1 to destroy.

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.tf-EC2-1: Destroying... [id=i-0a9aabf9b68afbd18]
aws_instance.tf-EC2-1: Still destroying... [id=i-0a9aabf9b68afbd18, 10s elapsed]
aws_instance.tf-EC2-1: Still destroying... [id=i-0a9aabf9b68afbd18, 20s elapsed]
aws_instance.tf-EC2-1: Still destroying... [id=i-0a9aabf9b68afbd18, 30s elapsed]
aws_instance.tf-EC2-1: Destruction complete after 40s

Destroy complete! Resources: 1 destroyed.

5. 진짜 테라폼 파일 생성

테라폼으로 구성할 vpc 구성도

EC2, VPC, Subnet 4개 (private 2개 public 2개), IGW, Routing Table, NAT 게이트웨이(제외), EIP(제외)

더보기
terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "5.43.0"
    }
  }
}

# 프로바이더 설정
provider "aws" {
  region = "ap-northeast-2"
}

# EC2 인스턴스 생성
resource "aws_instance" "tf-EC2-1" {
  ami = "ami-0aed24d0abcdffd14"
  instance_type = "t2.micro"
  tags = {
          Name = "guaba_terraform"
        }
}

# VPC 생성 (VPC 갯수 제한있으니 연습할때는 조심)
resource "aws_vpc" "fuuuu_vpc" {
  cidr_block = "10.120.0.0/16"
  tags = {
          name = "fuuuu_vpc"
        }
}

# 서브넷 생성
resource "aws_subnet" "fuuuu_pub_subnet_1" {
  vpc_id = aws_vpc.fuuuu_vpc.id
  cidr_block = "10.120.1.0/24"
  availability_zone = "ap-northeast-2a"
  tags = {
    Name = "fuuuu_pub_subnet_1"
  }
}

resource "aws_subnet" "fuuuu_pri_subnet_1" {
  vpc_id = aws_vpc.fuuuu_vpc.id
  cidr_block = "10.120.2.0/24"
  availability_zone = "ap-northeast-2a"
  tags = {
    Name = "fuuuu_pri_subnet_1"
  }
}

resource "aws_subnet" "fuuuu_pub_subnet_2" {
  vpc_id = aws_vpc.fuuuu_vpc.id
  cidr_block = "10.120.3.0/24"
  availability_zone = "ap-northeast-2b"
  tags = {
    Name = "fuuuu_pub_subnet_2"
  }
}

resource "aws_subnet" "fuuuu_pri_subnet_2" {
  vpc_id = aws_vpc.fuuuu_vpc.id
  cidr_block = "10.120.4.0/24"
  availability_zone = "ap-northeast-2b"
  tags = {
    Name = "fuuuu_pri_subnet_2"
  }
}
# 인터넷 게이트웨이 생성
resource "aws_internet_gateway" "fuuuu_igw" {
  vpc_id = aws_vpc.fuuuu_vpc.id
  tags = {
    Name = "fuuuu_igw"
  }
}

# 퍼블릭 라우팅 테이블 생성
resource "aws_route_table" "fuuuu_rtb_public" {
  vpc_id = aws_vpc.fuuuu_vpc.id
  tags = {
    Name = "fuuuu_rtb_public"
 }
}

# 퍼블릭 라우팅 테이블에 인터넷 게이트웨이 연결
resource "aws_route" "fuuuu_public_route" {
  route_table_id = aws_route_table.fuuuu_rtb_public.id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id = aws_internet_gateway.fuuuu_igw.id
}

# 퍼블릭 라우팅 테이블에 서브넷 연결
resource "aws_route_table_association" "fuuuu_route_table_association_public1" {
  subnet_id = aws_subnet.fuuuu_pub_subnet_1.id
  route_table_id = aws_route_table.fuuuu_rtb_public.id
}

resource "aws_route_table_association" "fuuuu_route_table_association_public2" {
  subnet_id = aws_subnet.fuuuu_pub_subnet_2.id
  route_table_id = aws_route_table.fuuuu_rtb_public.id
}
# 프라이빗 라우팅 테이블 생성
resource "aws_route_table" "fuuuu_rtb_private" {
  vpc_id = aws_vpc.fuuuu_vpc.id
  tags = {
    Name = "fuuuu_rtb_private"
  }
}

# 프라이빗 라우터에 서브넷 연결
resource "aws_route_table_association" "route_table_association_private1" {
  subnet_id = aws_subnet.fuuuu_priv_subnet_1.id
  route_table_id = aws_route_table.fuuuu_rtb_private.id
}

resource "aws_route_table_association" "route_table_association_private2" {
  subnet_id = aws_subnet.fuuuu_priv_subnet_2.id
  route_table_id = aws_route_table.fuuuu_rtb_private.id
}

# EIP 및 NAT 게이트웨이 생성 돈나가서 제외함
//resource "aws_eip" "fuuuu_nat_eip" {
//  depends_on = [aws_internet_gateway.fuuuu_igw]
//}

//resource "aws_nat_gateway" "fuuuu_nat_gw" {
//  allocation_id = aws_eip.fuuuu_nat_eip.id
//  subnet_id = aws_subnet.fuuuu_pub_subnet_1.id
//  tags = {
//    Name = "fuuuu_nat_gw"
//  }
//}

# 프라이빗 라우터에 NAT 게이트웨이 연결
//resource "aws_route" "fuuuu_private_route" {
//  route_table_id = aws_route_table.fuuuu_rtb_private.id
//  destination_cidr_block = "0.0.0.0/0"
//  nat_gateway_id = aws_nat_gateway.fuuuu_nat_gw.id
//}
# 보안그룹 생성(ssh)
resource "aws_security_group" "fuuuu_bastion" {
  vpc_id = aws_vpc.fuuuu_vpc.id
  tags = {
    Name = "fuuuu_bastion"
  }
  ingress {
    from_port = 22
    to_port = 22
    protocol = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  egress {
    from_port = 0
    to_port = 0
    protocol = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# bastion instance 생성
resource "aws_instance" "fuuuu_bastion_ec2_1" {
        ami = "ami-0c101f26f147fa7fd"
        instance_type = "t2.micro"
        subnet_id = aws_subnet.fuuuu_pub_subnet_1.id
        vpc_security_group_ids = [aws_security_group.fuuuu_bastion.id]
        associate_public_ip_address = true
        tags = {
                Name = "fuuuu_bastion_ec2_1"
        }
}

 

+ Recent posts