[컴] Terraform 사용법

helloworld / hell world / 테라폼 예시 / 예제 / 간단 예시

Terraform 사용법

prerequisite

  1. AWS CLI 가 설치돼야 한다.
  2. AWS secret key 도 설정돼야 한다.
  3. terraform

기본적으로 .tffile 을 수정하고, terraform apply 를 하면 infra 에 반영된다. terraform apply을 하면, 일단 변경사항이 어떤 것들인지 보여주고, 적용할 것인지를 묻는다. 그 때 yes 를 하면 실제 적용이 이뤄진다.

  1. 다음처럼 main.tf를 하나 만들고,
  2. terraform init 을 한다.
    • 다음 내용들이 만들어진다.
      • .terraform.lock.hcl : lock file, provider version 들이 적혀있다.
      • .terraform/ : download 해서 install 한 provider 가 들어가 있다.(.exe file)
  3. terraform fmt : formatting 을 해준다. format 에 변경이 있는 file 이름만 stdout 으로 보인다.
  4. terraform validate : 문법확인
  5. terraform apply : code를 보고, 실제 infra 를 생성해준다.
    • terraform.tfstatae file
      • 그러면 이 때 생기는 instance 정보를 다시 가져와서 terraform.tfstataefile에 넣어준다.
      • terraform 은 현재 instance 의 정보를 이 state file(terraform.tfstatae) 로 판단하기 때문에 aws web console 로 변경하면 이 state file 이 out-of-sync 가 된다.
      • 그리고 보안도 중요하다. 그래서 이 파일(terraform.tfstatae)에 대한 접근은 제한적인 사람만 access 할 수 있게 해야 한다.
      • terraform show 를 하면 이 파일의 내용을 보여준다.
  6. terraform state list : 이것을 하면 terraform state file 에 있는 resource 의 list 를 보여준다.
  7. terraform destroy : 만들어 놓은 infra 를 삭제한다. dependency 가 있으면, 그에 맞는 순서대로 terraform 이 삭제해 준다.
  8. terraform output : .tf에서 output 으로 정의한 것들을 보여주게 된다.
# main.tf
# ref: https://developer.hashicorp.com/terraform/tutorials/aws-get-started/aws-build
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }

  required_version = ">= 1.2.0"
}

provider "aws" {
  region  = "ap-northeast-2"
}

resource "aws_instance" "myserverid1" {
  ami           = "ami-055437ee4dcf92427"
  instance_type = "t2.micro"

  tags = {
    Name = "my-instance-name-namh"
  }
}

# 이 부분을 따로 variable.tf 등으로 만들어도 상관없다. 동작은 같다.
variable "instance_name" {
  description = "Value of the Name tag for the EC2 instance"
  type        = string
  default     = "my-instance-name-namh"
}

# 이 부분을 따로 output.tf 등으로 만들어도 상관없다. 동작은 같다.
output "instance_id" {
  description = "ID of the EC2 instance"
  value       = aws_instance.namh-test.id
}
d:\a\terrform\test>terraform fmt
hello.tf

d:\a\terrform\test>terraform validate
Success! The configuration is valid.

terraform 에서 infra 변경

만약 위 main.tf 에서 ami 부분을 변경하면, terraform 은 현재 떠 있는 instance 에서 ami 를 변경할 수 없기 때문에 기존의 것을 삭제 하고 새로운 ami 로 instance 를 만들게 된다.

resource "aws_instance" "myserverid1" {
  ami           = "ami-0676d41f079015f32"
  instance_type = "t2.micro"
d:\a\terrform\test>terraform apply
aws_instance.namh-test: Refreshing state... [id=i-05b38172e99a351bd]

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:

  # aws_instance.namh-test must be replaced
-/+ resource "aws_instance" "namh-test" {
      ~ ami                                  = "ami-055437ee4dcf92427" -> "ami-0676d41f079015f32" # forces replacement
      ~ arn                                  = "arn:aws:ec2:ap-northeast-2:786531771744:instance/i-05b38172e99a351bd" -> (known after apply)
      ~ associate_public_ip_address          = true -> (known after apply)
      ~ availability_zone                    = "ap-northeast-2a" -> (known after apply)
      ~ cpu_core_count                       = 1 -> (known after apply)
      ~ cpu_threads_per_core                 = 1 -> (known after apply)
      ~ disable_api_stop                     = false -> (known after apply)
      ~ disable_api_termination              = false -> (known after apply)
      ~ ebs_optimized                        = false -> (known after apply)
      - hibernation                          = false -> null
      + host_id                              = (known after apply)
      + host_resource_group_arn              = (known after apply)
      + iam_instance_profile                 = (known after apply)
      ~ id                                   = "i-05b38172e99a351bd" -> (known after apply)
      ~ instance_initiated_shutdown_behavior = "stop" -> (known after apply)
      ~ instance_state                       = "running" -> (known after apply)
      ~ ipv6_address_count                   = 0 -> (known after apply)
      ~ ipv6_addresses                       = [] -> (known after apply)
      + key_name                             = (known after apply)
      ~ monitoring                           = false -> (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      ~ placement_partition_number           = 0 -> (known after apply)
      ~ primary_network_interface_id         = "eni-0024d58ca1afb689e" -> (known after apply)
      ~ private_dns                          = "ip-172-31-6-77.ap-northeast-2.compute.internal" -> (known after apply)
      ~ private_ip                           = "172.31.6.77" -> (known after apply)
      ~ public_dns                           = "ec2-52-78-198-216.ap-northeast-2.compute.amazonaws.com" -> (known after apply)
      ~ public_ip                            = "52.78.198.216" -> (known after apply)
      ~ secondary_private_ips                = [] -> (known after apply)
      ~ security_groups                      = [
          - "default",
        ] -> (known after apply)
      ~ subnet_id                            = "subnet-d1f1b0b8" -> (known after apply)
      ~ tags                                 = {
          ~ "Name" = "ExampleAppServerInstance" -> "namh-test"
        }
      ~ tags_all                             = {
          ~ "Name" = "ExampleAppServerInstance" -> "namh-test"
        }
      ~ tenancy                              = "default" -> (known after apply)
      + user_data                            = (known after apply)
      + user_data_base64                     = (known after apply)
      ~ vpc_security_group_ids               = [
          - "sg-14baa97d",
        ] -> (known after apply)
        # (4 unchanged attributes hidden)

      ~ capacity_reservation_specification {
          ~ capacity_reservation_preference = "open" -> (known after apply)

          + capacity_reservation_target {
              + capacity_reservation_id                 = (known after apply)
              + capacity_reservation_resource_group_arn = (known after apply)
            }
        }

      - credit_specification {
          - cpu_credits = "unlimited" -> null
        }

      + ebs_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + snapshot_id           = (known after apply)
          + tags                  = (known after apply)
          + throughput            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }

      ~ enclave_options {
          ~ enabled = false -> (known after apply)
        }

      + ephemeral_block_device {
          + device_name  = (known after apply)
          + no_device    = (known after apply)
          + virtual_name = (known after apply)
        }

      ~ maintenance_options {
          ~ auto_recovery = "default" -> (known after apply)
        }

      ~ metadata_options {
          ~ http_endpoint               = "enabled" -> (known after apply)
          ~ http_put_response_hop_limit = 1 -> (known after apply)
          ~ http_tokens                 = "optional" -> (known after apply)
          ~ instance_metadata_tags      = "disabled" -> (known after apply)
        }

      + network_interface {
          + delete_on_termination = (known after apply)
          + device_index          = (known after apply)
          + network_card_index    = (known after apply)
          + network_interface_id  = (known after apply)
        }

      ~ private_dns_name_options {
          ~ enable_resource_name_dns_a_record    = false -> (known after apply)
          ~ enable_resource_name_dns_aaaa_record = false -> (known after apply)
          ~ hostname_type                        = "ip-name" -> (known after apply)
        }

      ~ root_block_device {
          ~ delete_on_termination = true -> (known after apply)
          ~ device_name           = "/dev/xvda" -> (known after apply)
          ~ encrypted             = false -> (known after apply)
          ~ iops                  = 100 -> (known after apply)
          + kms_key_id            = (known after apply)
          ~ tags                  = {} -> (known after apply)
          ~ throughput            = 0 -> (known after apply)
          ~ volume_id             = "vol-0e01fc659049ed2d4" -> (known after apply)
          ~ volume_size           = 8 -> (known after apply)
          ~ volume_type           = "gp2" -> (known after apply)
        }
    }

Plan: 1 to add, 0 to change, 1 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.namh-test: Destroying... [id=i-05b38172e99a351bd]
aws_instance.namh-test: Still destroying... [id=i-05b38172e99a351bd, 10s elapsed]
aws_instance.namh-test: Still destroying... [id=i-05b38172e99a351bd, 20s elapsed]
aws_instance.namh-test: Destruction complete after 30s
aws_instance.namh-test: Creating...
aws_instance.namh-test: Still creating... [10s elapsed]
aws_instance.namh-test: Still creating... [20s elapsed]
aws_instance.namh-test: Still creating... [30s elapsed]
aws_instance.namh-test: Creation complete after 31s [id=i-0ff6c6e6655dec3bc]

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

terraform destroy

d:\a\terrform\test>terraform destroy
aws_instance.namh-test: Refreshing state... [id=i-0ff6c6e6655dec3bc]

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.namh-test will be destroyed
  - resource "aws_instance" "namh-test" {
      - ami                                  = "ami-0676d41f079015f32" -> null
      - arn                                  = "arn:aws:ec2:ap-northeast-2:786531771744:instance/i-0ff6c6e6655dec3bc" -> null
      - associate_public_ip_address          = true -> null
      - availability_zone                    = "ap-northeast-2a" -> 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-0ff6c6e6655dec3bc" -> 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-0ebdd0e3153dba51d" -> null
      - private_dns                          = "ip-172-31-5-167.ap-northeast-2.compute.internal" -> null
      - private_ip                           = "172.31.5.167" -> null
      - public_dns                           = "ec2-3-35-50-89.ap-northeast-2.compute.amazonaws.com" -> null
      - public_ip                            = "3.35.50.89" -> null
      - secondary_private_ips                = [] -> null
      - security_groups                      = [
          - "default",
        ] -> null
      - source_dest_check                    = true -> null
      - subnet_id                            = "subnet-d1f1b0b8" -> null
      - tags                                 = {
          - "Name" = "namh-test"
        } -> null
      - tags_all                             = {
          - "Name" = "namh-test"
        } -> null
      - tenancy                              = "default" -> null
      - user_data_replace_on_change          = false -> null
      - vpc_security_group_ids               = [
          - "sg-14baa97d",
        ] -> null

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

      - credit_specification {
          - cpu_credits = "unlimited" -> 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-0198723c2720811de" -> null
          - volume_size           = 8 -> 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.namh-test: Destroying... [id=i-0ff6c6e6655dec3bc]
aws_instance.namh-test: Still destroying... [id=i-0ff6c6e6655dec3bc, 10s elapsed]
aws_instance.namh-test: Still destroying... [id=i-0ff6c6e6655dec3bc, 20s elapsed]
aws_instance.namh-test: Destruction complete after 30s

Destroy complete! Resources: 1 destroyed.

terraform output

d:\a\prog\terrform\test>terraform output
╷
│ Warning: No outputs found
│
│ The state file either has no outputs defined, or all the defined outputs are empty. Please define an output in your
│ configuration with the `output` keyword and run `terraform refresh` for it to become available. If you are using
│ interpolation, please verify the interpolated value is not empty. You can use the `terraform console` command to
│ assist.
╵

d:\a\prog\terrform\test>terraform apply
aws_instance.namh-test: Refreshing state... [id=i-0f9248e0f31df356f]

Changes to Outputs:
  + instance_id = "i-0f9248e0f31df356f"

You can apply this plan to save these new output values to the Terraform state, without changing any real
infrastructure.

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


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

Outputs:

instance_id = "i-0f9248e0f31df356f"

d:\a\prog\terrform\test>terraform output
instance_id = "i-0f9248e0f31df356f"

기본 rds 생성하는 예제

rds 생성하는 terraform이다. 이것은 기존에 존재하는 security group 등을 사용했다.

# ref: https://developer.hashicorp.com/terraform/tutorials/aws-get-started/aws-build
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }

  required_version = ">= 1.2.0"
}

provider "aws" {
  region = var.region
}


data "aws_availability_zones" "available" {}

resource "aws_db_parameter_group" "myproj" {
  name   = "myproj"
  family = "mariadb10.6"

  # parameter {
  #   name  = "log_connections"
  #   value = "1"
  # }
}

resource "aws_db_instance" "myproj-1" {
  identifier        = "myproj-1"
  instance_class    = "db.t3.micro"
  allocated_storage = 5
  engine            = "MariaDB"
  engine_version    = "10.6.12"
  username          = "myusername"
  password          = var.db_password
  # my-group-prod
  # https://ap-northeast-2.console.aws.amazon.com/rds/home?region=ap-northeast-2#db-subnet-group:id=my-group-prod
  db_subnet_group_name = "my-group-prod"
  vpc_security_group_ids = [
    "sg-0a0a0a0a0a0a0a0aa", # my security group 1
    "sg-0b0b0b0b0b0b0b0b"   # my security group 2
  ]
  # https://ap-northeast-2.console.aws.amazon.com/rds/home?region=ap-northeast-2#parameter-groups-detail:ids=mariadb-10-6-v220825;type=DbParameterGroup;editing=false
  parameter_group_name = "mariadb-10-6-v220825"
  publicly_accessible  = true
  skip_final_snapshot  = true
}


# ----------------------------------------
# variable
# ----------------------------------------

variable "region" {
  default     = "ap-northeast-2"
  description = "AWS region"
}

variable "db_password" {
  description = "RDS root user password"
  sensitive   = true
}

# ----------------------------------------
# output
# ----------------------------------------

output "rds_hostname" {
  description = "RDS instance hostname"
  value       = aws_db_instance.myproj-1.address
  sensitive   = true
}

output "rds_port" {
  description = "RDS instance port"
  value       = aws_db_instance.myproj-1.port
  sensitive   = true
}

output "rds_username" {
  description = "RDS instance root username"
  value       = aws_db_instance.myproj-1.username
  sensitive   = true
}

state 가 다른 방법으로 변경되었을 때 다시 state 를 맞추는 법

terraform plan -refresh-only

terraform 으로 만든후, web console로 변경하면, state 의 sync가 달라지게 된다. 그 때 다시 state 를 sync 하는 방법이다.

terraform plan -refresh-only 를 해서 변경되는 state 가 마음에 들면, terraform apply -refresh-only 를 하면 state file 내용이 변경된다. 이 때 infrastructure 를 적은 .tf 파일 내용은 변경되지 않는다.

terraform plan -refresh-only
terraform apply -refresh-only

terraform state mv

예시:

resource stage_b 를 stage_c 로 이름 변경하는 경우

resource "aws_instance" "stage_b" {
  ...
}
resource "aws_instance" "stage_c" {
  ...
}
terraform state mv aws_instance.stage_b aws_instance.stage_c

moved

stage_bstage_c로 변경한 경우 아래 처럼 써주고, terraform apply 를 해주면 된다. apply 를 하고나선 moved는 삭제되어도 된다.

resource "aws_instance" "stage_c" {
  ...
}

moved {
  from = aws_instance.stage_b
  to   = aws_instance.stage_c
}

만약 .tfstate 파일이 없을때.

만약 infra 가 존재하고 있고, .tfstate 가 없다면, 어떻게 해야 하는가?

이 때는 terraformer 를 사용하도록 하자. 개인적인 경험으로는, terraform import 를 이용하는 것이 낫다.

terraform import

먼저 terraform 에 import 할 내용을 일부 적는다. 그리고 import 를 하면 된다.

아래는 ec2 예시다. ec2 에서는 id 를 사용하고, 다른 곳에서는 id 외의 것을 사용할 수도 있다.

팁: 일단 그냥 시도해봐도 된다. 존재하지 않으면, 존재하지 않는다는 error 가 뜬다. AWS console 의 description 부분을 보면 된다. 그리고 terraformer 을 이용해도 id를 얻을 수 있다.

팁2: terraform console 을 이용하면, import 한 값들을 확인할 수 있다.

resource "aws_instance" "example" {
  # ...instance configuration...
}
terraform import aws_instance.example i-abcd1234

terraform 과 infra 의 연결을 끊으려면(detach)

cloud 에 있는 service 는 그대로 두고, terraform 의 resource 를 지우는 방법.

terraform state 에서 지우면 된다.

terraform state list
terraform state rm <name>

terraform.tfstate 를 s3 에 저장하는 법

# ref: https://developer.hashicorp.com/terraform/tutorials/aws-get-started/aws-build
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }

  required_version = ">= 1.2.0"
  backend "s3" {
    bucket = "mys3bucket-name"
    key    = "global/ex/myapp/terraform.tfstate"
    region = "ap-northeast-2"
  }
}

instance 를 생성후 bash script 실행하는 법

resource "aws_instance" "linux_instance" {
  ...
  provisioner "file" {
    source      = "./mysetup.sh"      # local 에서의 bash script 위치
    destination = "/tmp/mysetup.sh"   # aws 에서의 위치
  }

  provisioner "remote-exec" {
    inline = [
      "chmod +x /tmp/mysetup.sh",  # 권한 변경
      "sudo /tmp/mysetup.sh",      # 실행
    ]
  }
  # Login to the ec2-user with the aws key.
  connection {
    type        = "ssh"
    user        = "ec2-user"
    password    = ""
    private_key = file(var.keyPath)
    host        = self.public_ip
  }
}

global variable 을 사용하는 법

AWS관련 다른 예시들

See Also

  1. 쿠…sal: [컴] terraform
  2. 쿠...sal: [컴] terraform 에서 ebs block 을 추가하고, user_data 사용하는 예시

댓글 없음:

댓글 쓰기