들어가며

테라폼 기초 입문 실습 스터디 2주차 입니다. 1주차에 이어 테라폼의 기본 사용법에 대해 실습해보려 합니다. 이번 주에도 “테라폼으로 시작하는 IaC” 책을 기준으로 정리하였습니다.

테라폼으로 시작하는 IaC

테라폼으로 시작하는 IaC

HCL (계속)

지난주에 이어서 HCL(HashiCorp Configuration Language) 의 문법에 대해 알아보겠습니다.

테라폼 블록

data 블록

  • 데이터 소스는 테라폼으로 정의되지 않은 외부 리소스와 저장된 정보를 참조할때 사용합니다.
  • data 블록 선언 방법

      data "<프로바이더이름_리소스유형>" "<이름>" {
          <인수> = <값>
      }
    

    이때 인수는 데이터 소스를 참조할때 사용할 인수이며 아래의 “데이터 소스 참조 방법”에서의 속성과는 다른 값입니다. 이 인수로 참조된 데이터의 속성 값을 아래의 방법으로 가져올 수 있습니다.

  • 데이터 소스 참조 방법

      # 데이터 소스 참조
      data.<프로바이더이름_리소스유형>.<이름>.<속성>  
    
  • data 블록 사용 예시
    1. 현재 region에서 사용가능한 availability zone 정보를 가져오기
      • main.tf
       # main.tf
       data "aws_availability_zones" "available_zones" {
           state = "available"   # 현재 region에서 사용가능한 availability zone 정보를 가져오기
       }
      
      • 실행 결과 확인
       # main.tf 적용
       $ terraform init && terraform plan && terraform apply -auto-approve
            
       # state 확인
       $ terraform state list      
       # => data.aws_availability_zones.available_zones
            
       # 테라폼 콘솔에서 확인
       $ terraform console
       > # <확인하고 싶은 속성> 
       > data.aws_availability_zones.available_zones       # => ...
       > data.aws_availability_zones.available_zones.id    # => "ap-northeast-2"
       > data.aws_availability_zones.available_zones.names # => AZ 이름 목록...
       > data.aws_availability_zones.available_zones.zone_ids # => AZ ID 목록...
            
       # tip. echo <확인하고 싶은 속성> || terraform console 로 값 확인
       $ echo "data.aws_availability_zones.available_zones" | terraform console     # => ...
       $ echo "data.aws_availability_zones.available_zones.id" | terraform console   # => "ap-northeast-2"
       $ echo "data.aws_availability_zones.available_zones.names" | terraform console # => AZ 이름 목록...
       $ echo "data.aws_availability_zones.available_zones.zone_ids" | terraform console # => AZ ID 목록...
       $ echo "data.aws_availability_zones.available_zones.zone_ids[0]" | terraform console # => "apne2-az1"
      
    2. 로컬 파일의 내용 읽기
      • main.tf
       # main.tf
       resource "local_file" "abc" {
           content  = "Hello world!!"
           filename = "${path.module}/abc.txt"
       }
              
       data "local_file" "abc" {
           filename = local_file.abc.filename
       }
              
       resource "local_file" "def" {
           content  = data.local_file.abc.content
           filename = "${path.module}/def.txt"
       }
      
      • 실행 결과 확인

        data.local_file.abc local_file.abc local_file.def
       # main.tf 적용
       $ terraform init && terraform plan && terraform apply -auto-approve
              
       # state 확인
       $ terraform state list      
       # => data.local_file.abc
       #    local_file.abc
       #    local_file.def
              
       # graph 확인
       $ terraform graph > graph.dot
             
       # 데이터 소스 참조 확인
       $ echo "data.local_file.abc.content" | terraform console
      

variable 블록

  • 변수(variable)블록은 인프라를 구성하는데 필요한 속성 값을 정의해 코드의 변경 없이 여러 인프라를 생성하는데 사용합니다.
  • 테라폼에서의 variable 블록을 입력 변수, 즉 input variable라 합니다.
  • variable 블록 선언 방법

      variable "<이름>" {
          <인수> = <값>
          <메타변수> = <메타변수 값>
      }
    
    • source, version, providers 등의 예약어는 변수이름으로 사용할 수 없습니다.
  • 변수 참조 방법

      # 변수 참조
      var.<이름>
      
      # 배열인 경우 [<인덱스>] 로 참조
      var.<이름>[<인덱스>]
      
      # 배열의 10번째 항목 참조 예시
      var.<이름>[9]  
    
variable 블록에서 사용 가능한 메타변수
  • variable 블록에서는 다음의 메타변수를 사용 가능합니다.
메타변수 설명
default 기본값을 지정합니다. 기본값도 없고, 다른 방법들로 지정되지 않으면 대화식으로 사용자에게 묻습니다.
type 변수 유형을 지정합니다.
description 입력변수의 설명을 기입합니다.
validation 변수의 제약 조건을 부여하고 유효성 검사 규칙을 정의합니다. 참고
sensitive 비밀번호 등의 민감한 변수임을 표시하고 출력문에서 값 노출을 제한합니다. 참고
nullable 변수에 값이 없어도 됨을 지정합니다.
입력 변수 유형
유형 설명
string 문자열을 나타냅니다.
number 숫자를 나타냅니다.
bool true, false의 불리언 값을 나타냅니다.
list(<유형>)유형> 리스트를 나타냅니다.
map(<유형>)유형> 맵을 나타냅니다.
set(<유형>)유형> 집합을 나타냅니다.
object({<인수 이름="">=<유형>, ...})유형>인수> 객체를 나타냅니다.
tuple([<유형>, ...])유형> 튜플을 나타냅니다.
  • 입력 변수 사용 예시
    1. number 변수 예시
        variable "number" {
         type    = number
         default = 10
        }
      
    2. list 변수 예시
        variable "list" {
         type    = list
         default = [1, "two", 3]
        }
      
    3. list의 값으로 number로 받는 조건 결합 예시
        variable "list" {
         type        = list(number)
         default     = [1, 2, 3]
         # default = [1, "two", 3] 과 같이 지정된 number 타입이 아닌 경우 오류 발생       
        }
      
    4. 모든 값이 string 인 map 예시
        variable "map" {
         type    = map(string)
         default = {
             key1 = "value1"
             key2 = "value2"
         }
        }
      
    5. object 변수를 사용하여 복잡한 구조적 유형 (structural type) 작성 예시
        variable "object_example" {
         type = object({
             name    = string
             age     = number
             tags    = list(string)
             enabled = bool
         })
              
         default = {
             name    = "value1"
             age     = 42
             tags    = ["a", "b", "c"]
             enabled = true
         }
        } 
      
유효성 검사
  • variable 선언시 변수 타입 이외에도 사용자 지정 유효성 검사를 추가할 수 있습니다.
  • 유효성 검사 작성 방법

      variable "image_id" {
        type        = string
        description = "The id of the machine image (AMI) to use for the server."
        
        validation {
          condition     = length(var.image_id) > 4    # true 또는 false를 반환하는 규칙 
          error_message = "The image_id value must exceed 4." # false일 때 출력할 오류 메시지
        }
        
        validation {
          condition     = can(regex("^ami-", var.image_id))  # 정규 표현식을 사용한 예시
          error_message = "The image_id value must starting with \"ami-\"."
        }
      }
    
    • variable 블록 내부에 validation 블록을 넣고, 그 안에 conditionerror_message를 지정합니다.
    • condition : 유효성 검사 조건을 지정합니다.
    • error_message : 조건이 만족하지 않을 때 출력할 오류 메시지를 지정합니다.
    • 확인
      • 아래와 같이 조건에 맞지 않는 ami 입력시 유효성 검사를 만족하지 못하여 오류 메시지가 출력되는것을 확인할 수 있습니다.
          $ terraform plan
          # => var.image_id
          #      The id of the machine image (AMI) to use for the server.
          #    
          #      Enter a value: ami
          #    ...
          #    
          #    The image_id value must exceed 4.
          #    
          #    This was checked by the validation rule at main.tf:5,7-17.
          #
          #    ...    
          #    
          #    The image_id value must starting with "ami-".
          #    
          #    This was checked by the validation rule at main.tf:10,7-17.
        
      • 유효성 검사 조건을 만족하는 ami-1234 입력시 정상적으로 실행됩니다.
          $ terraform plan
          # => var.image_id
          #    The id of the machine image (AMI) to use for the server.
          #    
          #    Enter a value: ami-1234
          #    
          #    local_file.abc: Refreshing state... [id=3ee66cf7a43f216ff7060c2bd85758424f7f18b3]
          #    
          #    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.        
        
sensitive 메타변수를 사용한 민감한 변수값 처리
  • sensitive = truevariable 블록에 넣어서 해당 변수가 민감한 값을 갖고 있음을 지정할 수 있습니다.
  • 민감한 변수값은 출력문에서 노출되지 않고 (sensitive value) 라는 내용으로 감춰집니다.

      # main.tf
      variable "my_password" {
          default = "supersecret"
          sensitive = true
      } 
        
      resource "local_file" "abc" {
        content  = var.my_password
        filename = "${path.module}/abc.txt"
      }
    
    • 확인
        # 1. terraform apply 실행시 my_password 변수값이 민감한 값으로 처리되어 출력됩니다.
        $ terraform apply --auto-approve
        # => ...
        #          + content              = (sensitive value)
        #    ...
            
        # 2. terraform console로 확인시에도 민감한 값으로 필터링 되어 출력됩니다.
        $ echo "local_file.abc.content" | terraform console
        # => (sensitive value)
            
        # 3. 파일 내용은 실제 값으로 들어갑니다.
        $ cat abc.txt 
        # => supersecret
            
        # 4. terraform.tfstate 파일은 실제값이 들어가 있습니다. .tfstate 파일을 안전하게 관리해야 할 필요가 있습니다.
        $ cat terraform.tfstate| grep "content"
        # => "content": "supersecret",
      
      • terraform.tfstate 에서는 sensitive 값이 노출되므로 주의해야 합니다. terraform.tfstate 파일을 안전하게 관리하는 방법은 다음의 방법 등이 있습니다.
        1. AWS S3 등의 외부 스토리지에 저장하고, 권한을 제한
        2. 민감한 정보를 variable를 사용하지 않고 다른 방법으로 (예) 파일 등) 넘기거나, 일시적으로 사용하고 즉시 변경할 수 있는 경우 사용
        3. 민감한 정보는 Terraform에 넘길때 암호화 하여 사용하고, provisioning시 복호화하여 사용
변수 입력 방식과 우선 순위
  • 변수 입력 방식은 다음과 같습니다. 아래로 갈 수록 우선순위가 높아집니다.
    1. 실행 후 대화식 입력
       $ terraform apply -auto-approve
       # => var.my_var
       #    Enter a value: abcd
            
       $ echo "local_file.abc.content" | terraform console
       # => "abcd"
      
    2. variable 블록에 default로 지정된 값
      • main.tf 작성
          # main.tf
          variable "my_var" {
              default = "default value"
          }
        
      • 실행
          $ terraform apply -auto-approve
          # => ...
          # => Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
                 
          $ echo "local_file.abc.content" | terraform console
          # => "default value"
        
    3. 환경 변수로 입력
       $ export TF_VAR_my_var="환경변수"
       $ terraform apply -auto-approve
       $ echo "local_file.abc.content" | terraform console
       # => "환경변수"
      
    4. terraform.tfvars 파일을 통해 변수 파일로 입력
       $ echo 'my_var = "terraform.tfvars 값"' > terraform.tfvars
       $ terraform apply -auto-approve
       $ echo "local_file.abc.content" | terraform console
       # => "terraform.tfvars 값"
      
    5. terraform.tfvars.json 파일을 통해 변수 파일로 입력
       $ echo '{ "my_var": "terraform.tfvars.json 값" }' > terraform.tfvars.json
       $ terraform apply -auto-approve
       $ echo "local_file.abc.content" | terraform console
       # => "terraform.tfvars.json 값"
      
    6. *.auto.tfvars 또는 *.auto.tfvars.json 을 통해 변수 파일로 입력
      • 단, 파일명의 정렬 순서에 따라 *.auto.tfvars 파일간의 우선순위기 적용됩니다.
       $ echo 'my_var = "eeee.auto.tfvars 값"' > eeee.auto.tfvars
       $ terraform apply -auto-approve
       $ echo "local_file.abc.content" | terraform console
       # => "eeee.auto.tfvars 값"
      
       $ echo 'my_var = "zzzz.auto.tfvars 값"' > zzzz.auto.tfvars
       $ terraform apply -auto-approve
       $ echo "local_file.abc.content" | terraform console
       # => "zzzz.auto.tfvars 값"
      
       $ echo 'my_var = "aaaa.auto.tfvars 값"' > aaaa.auto.tfvars
       $ terraform apply -auto-approve
       $ echo "local_file.abc.content" | terraform console
       # => "zzzz.auto.tfvars 값"
      
      • 위의 예제에서 보는것과 같이 정렬 순서에서 뒤로 갈 수록 우선순위가 높아집니다. z가 a나 e 보다 뒤이기 때문에 zzzz.auto.tfvars 값이 우선하게 적용됩니다.
    7. terraform apply-var 또는 -var-file로 파일 지정 명령행 인수 사용
       $ terraform apply -auto-approve -var='my_var=명령행 인수 값' -var="addtional_var=추가 변수 값"
       $ echo "local_file.abc.content" | terraform console
       # => "명령행 인수 값"
           
       $ echo 'my_var="-var-file 값"' > varfiletest.txt
       $ terraform apply -auto-approve -var-file=varfiletest.txt
       $ echo "local_file.abc.content" | terraform console
       # => "-var-file 값"
      

locals 지역 값 블록

  • locals 블록은 테라폼 코드 내에서 재사용 가능한 지역 변수를 정의할 때 사용합니다.
  • variable 과는 달리 선언된 모듈 내에서만 사용 가능하며, 실행시에 입력받을 수 없습니다.
  • 단점으로는 실제 값에 대한 추적이 어려워 질 수 있다는 점입니다.
  • locals 블록 선언 방법

      locals {
          <인수> = <값>
      }
    
    • source, version, providers 등의 예약어는 변수이름으로 사용할 수 없습니다.
  • 참조 방법

      # 변수 참조
      local.<이름>
      
      # 배열인 경우 [<인덱스>] 로 참조
      local.<이름>[<인덱스>]
      
      # 배열의 10번째 항목 참조 예시
      local.<이름>[9]  
    
  • locals 블록 사용 예시
    • main.tf 작성
        # main.tf
        variable "prefix" {
            type    = string
            default = "hello"
        }
          
        locals {
            name = "terraform"
            content = "${var.prefix} ${local.name}"
            my_info = {
                age = 20
                region = "KR"
            }
            my_nums = [1,2,3,4,5]
        }
          
        locals {
            my_string = "string ${local.content}"
            # content = "content2" # 위의 locals에 등록되어서 로컬변수명이 중복되어 오류가 발생합니다.    
        }
            
        resource "local_file" "abc" {
            content  = local.content
            filename = "${path.module}/abc.txt"
        }
      
    • 실행 결과 확인
        $ terraform apply -auto-approve
              
        $ echo "local_file.abc.content" | terraform console
        # => "hello terraform"
          
        $ echo "local.my_string" | terraform console
        # => "string hello terraform"
      

output 출력 값 블록

  • output 블록은 테라폼 코드 프로비저닝 수행 후 결과 속성 값을 확인하는 용도로 사용합니다.
  • 출력 값은 다음의 용도로 사용할 수 있습니다.
    • 루트 모듈에서 사용자가 확인하고자 하는 특정 속성 출력
    • 자식 모듈의 특정값을 정의하고 루트 모듈에서 결과를 참조
    • 루트 모듈 간의 결과를 원격으로 읽기 위한 접근 요소
  • output 블록 선언 방법
      output "<이름>" {
          value = <값>
          <메타변수> = <메타변수 값>
      }
    
output 블록에서 사용 가능한 메타변수
  • output 블록에서는 다음의 메타변수를 사용 가능합니다.

    메타변수 설명
    description 출력값의 설명을 기입합니다.
    sensitive 비밀번호 등의 민감한 값임을 표시하고 출력문에서 값 노출을 제한합니다.
    depends_on value에 담길 값이 종속성이 있는 경우 지정하면 생성 순서가 조정됩니다.
    precondition 출력전에 지정된 조건을 검증합니다.
  • 출력 값 사용 예시

      # main.tf
      locals {
        content = "local content"
      }
        
      resource "local_file" "abc" {
        content  = "${local.content}"
        filename = "${path.module}/abc.txt"
      }
        
      output "file_id" {
        value = local_file.abc.id
        depends_on = [ local_file.abc ]
      }
        
      output "file_abspath" {
        value = abspath(local_file.abc.filename)
        description = "파일의 절대 경로"
      }
        
      output "local_content" {
        value = local.content
        sensitive = true
      }
    
    • 실행 결과
      $ terraform apply -auto-approve
      # => ...
      #    Outputs:
      #    
      #    file_abspath = ".../week-2/mytest7-output/abc.txt"
      #    file_id = "094f587b52451d1d5f3ef42a921fc877d8b18c1b"
      #    local_content = <sensitive>
    

반복문 (1/2)

  • list 형태의 값 목록이나 Key-Value 형태의 맵을 항목 만큼 반복하여 인프라 구성 정의를 항목 개수만큼 반복적으로 작성하지 않고, 반복문을 사용하여 간단하게 작성할 수 있습니다.
  • 테라폼의 HCL에는 countfor_each 두 가지 반복문이 있습니다.
  • 이번 주차에는 count 반복문에 대해 알아보았습니다.

count 반복문

  • count는 프로그래밍 언어의 for문 처럼 정수값만큼 반복합니다.
  • 기본 적인 사용법은 아래와 같습니다.
resource "<프로바이더이름_리소스유형>" "<이름>" {
    count = <반복횟수>
    content = "Index = ${count.index}" # count.index 로 현재 반복 인덱스를 참조
}
  • 위의 코드에서 count 횟수 만큼 반복해서 resource나 모듈을 생성합니다.
  • count의 반복횟수 참고값은 count.index를 통해 확인할 수 있으며, 0부터 시작해서 1씩 증가합니다.
  • 사용 예제
    # main.tf
    variable "fruits" {
      type = list
      default = ["apple", "banana", "kiwi"]
    }
        
    resource "local_file" "fruit_files" {
      count = length(var.fruits)
      filename = "${path.module}/i_love_${var.fruits[count.index]}.txt"
      content = "I love ${var.fruits[count.index]}!"
    }
    
    • 실행 결과
      $ terraform apply -auto-approve
           
      $ ls -1
      # => i_love_apple.txt
      #    i_love_banana.txt
      #    i_love_kiwi.txt
          
      $ cat i_love_apple.txt
      # => I love apple!
      

도전과제

도전과제 1. 리전 내에서 사용 가능한 가용영역 목록 가져오기를 사용한 VPC 리소스 생성 실습 진행

  • 가용영역 목록을 가져와서 VPC를 만들고, 사용 가능한 첫번째 가용영역에 subnet을 만드는 실습을 진행하였습니다.
  • 소스
    # main.tf
    data "aws_availability_zones" "available" {
      state = "available"
    }
      
    resource "aws_vpc" "peter_vpc" {
      cidr_block = "10.0.0.0/16"
      tags = {
        Name = "challenge1 vpc"
      }
    }
      
    resource "aws_subnet" "peter_subnet" {
      vpc_id = aws_vpc.peter_vpc.id
      availability_zone = data.aws_availability_zones.available.names[0]
      cidr_block = "10.0.1.0/24"
      tags = {
        Name = "challenge1 subnet"
      }
    }
      
    
  • 실행 결과
    $ terraform apply -auto-approve
    # => ...
    #    aws_vpc.peter_vpc: Creating...
    #    aws_vpc.peter_vpc: Creation complete after 1s [id=vpc-0e6762235c588581b]
    #    aws_subnet.peter_subnet: Creating...
    #    aws_subnet.peter_subnet: Creation complete after 0s [id=subnet-06888df37bc01ab4c]
      
    # 결과 확인
    $ echo "aws_vpc.peter_vpc.id" | terraform console
    # => "vpc-0e6762235c588581b"
    $ echo "aws_subnet.peter_subnet.id" | terraform console
    # => "subnet-06888df37bc01ab4c"
    
    • AWS 콘솔에서 VPC 생성 확인 20240622_terraform_w2_vpc.png
    • AWS 콘솔에서 Subnet 생성 확인 20240622_terraform_w2_subnet.png
  • 그래프

    data.aws_availability_zones.available aws_subnet.peter_subnet aws_vpc.peter_vpc

도전과제 2. 위 3개 코드 파일 내용에 리소스의 이름(myvpc, mysubnet1 등)을 반드시! 꼭! 자신의 닉네임으로 변경해서 배포 실습해보세요!

  • vpc.tf 생성
    # vpc.tf
    provider "aws" {
        region = "ap-northeast-2"
    }
      
    resource "aws_vpc" "peter_vpc" {
      cidr_block = "10.10.0.0/16"
      tags = {
        name = "t101-study"
      }
    }
    
    • 실행
      $ terraform init && terraform plan && terraform apply -auto-approve
      # => ...
      #    Plan: 1 to add, 0 to change, 0 to destroy.
      #    aws_vpc.peter_vpc: Creating...
      #    aws_vpc.peter_vpc: Creation complete after 2s [id=vpc-0e1ad43009a8fe8fa]
      #    
      #    Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
          
      $ echo "aws_vpc.peter_vpc.id" | terraform console
      # => "vpc-0e1ad43009a8fe8fa"
         
      $ aws ec2 describe-vpcs --filter 'Name=isDefault,Values=false' | jq
      # => {
      #      "Vpcs": [
      #        {
      #          "CidrBlock": "10.10.0.0/16",
      #          "DhcpOptionsId": "dopt-0c264ef627294fd1f",
      #          "State": "available",
      #          "VpcId": "vpc-0e1ad43009a8fe8fa",
      #          "OwnerId": "123456789012",
      #          "InstanceTenancy": "default",
      #          "CidrBlockAssociationSet": [
      #            {
      #              "AssociationId": "vpc-cidr-assoc-0e9adafb317c75f4f",
      #              "CidrBlock": "10.10.0.0/16",
      #              "CidrBlockState": {
      #                "State": "associated"
      #              }
      #            }
      #          ],
      #          "IsDefault": false,
      #          "Tags": [
      #            {
      #              "Key": "name",
      #              "Value": "t101-study"
      #            }
      #          ]
      #        }
      #      ]
      #    }
      
  • AWS 콘솔에서 VPC 생성정보 확인 : DNS 옵션값 확인
    • 아래의 캡쳐와 같이 DNS hostnames는 Disabled, DNS resolution은 Enabled 된 것을 확인하였습니다. 20240622_terraform_w2_c2_p1_1.png
  • vpc.tf의 DNS 옵션을 수정하여 두 DNS 옵션값을 enabled 로 변경
    # vpc.tf
    provider "aws" {
      region = "ap-northeast-2"
    }
      
    resource "aws_vpc" "peter_vpc" {
      cidr_block = "10.10.0.0/16"
      enable_dns_support = true   # 추가
      enable_dns_hostnames = true # 추가
      tags = {
        Name = "t101-study"
      }
    }
    
  • 수정된 vpc.tf 적용
    $ terraform apply -auto-approve
    
  • AWS 콘솔에서 VPC 생성정보 확인 : DNS 옵션값 확인
    • 아래와 같이 DNS hostnames 도 Enabled 되었습니다. 20240622_terraform_w2_c2_p1_2.png
  • vpc.tf 코드 내용 수정 : 서브넷 2개 생성 추가
    provider "aws" {
        region = "ap-northeast-2"
    }
      
    resource "aws_vpc" "peter_vpc" {
      cidr_block = "10.10.0.0/16"
      enable_dns_support = true
      enable_dns_hostnames = true
      tags = {
        Name = "t101-study"
      }
    }
      
    resource "aws_subnet" "peter_subnet1" {
      vpc_id     = aws_vpc.peter_vpc.id
      cidr_block = "10.10.1.0/24"
      
      availability_zone = "ap-northeast-2a"
      
      tags = {
        Name = "t101-subnet1"
      }
    }
      
    resource "aws_subnet" "peter_subnet2" {
      vpc_id     = aws_vpc.peter_vpc.id
      cidr_block = "10.10.2.0/24"
      
      availability_zone = "ap-northeast-2c"
      
      tags = {
        Name = "t101-subnet2"
      }
    }
      
    output "aws_vpc_id" {
      value = aws_vpc.peter_vpc.id
    }
    
    • 실행 결과 확인
      $ terraform apply -auto-approve
          
      $ terraform state list
      # => aws_subnet.peter_subnet1
      #    aws_subnet.peter_subnet2
      #    aws_vpc.peter_vpc
          
      $ terraform output
      # => aws_vpc_id = "vpc-0e1ad43009a8fe8fa"
          
      $ terraform output aws_vpc_id
      # => "vpc-0e1ad43009a8fe8fa"
          
      $ terraform output -raw aws_vpc_id
      # => vpc-0e1ad43009a8fe8fa
          
      $ VPCID=$(terraform output -raw aws_vpc_id)
          
      $ aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPCID" --output table
      

      20240622_terraform_w2_c2_p1_3.png

      terraform output-raw 옵션을 사용하면 “(따옴표) 없이 출력값만을 확인할 수 있습니다. 스크립트 작성시 유용할듯합니다.

    • 그래프

      aws_subnet.peter_subnet1 aws_vpc.peter_vpc aws_subnet.peter_subnet2
  • 이후 다음의 작업을 추가 적용 하였습니다.
    • IGW 인터넷 게이트웨이 추가
    • IGW 인터넷 게이트웨이로 전달하는 디폴트 라우팅 정보 추가
    • 보안그룹/EC2 배포
  • 최종 결과는 아래와 같습니다.
    • vpc.tf
      # vpc.tf
      provider "aws" {
          region = "ap-northeast-2"
      }
          
      resource "aws_vpc" "peter_vpc" {
        cidr_block = "10.10.0.0/16"
        enable_dns_support = true
        enable_dns_hostnames = true
        tags = {
          Name = "t101-study"
        }
      }
          
      resource "aws_subnet" "peter_subnet1" {
        vpc_id     = aws_vpc.peter_vpc.id
        cidr_block = "10.10.1.0/24"
          
        availability_zone = "ap-northeast-2a"
          
        tags = {
          Name = "t101-subnet1"
        }
      }
          
      resource "aws_subnet" "peter_subnet2" {
        vpc_id     = aws_vpc.peter_vpc.id
        cidr_block = "10.10.2.0/24"
          
        availability_zone = "ap-northeast-2c"
          
        tags = {
          Name = "t101-subnet2"
        }
      }
          
      resource "aws_internet_gateway" "peter_igw" {
        vpc_id = aws_vpc.peter_vpc.id
          
        tags = {
          Name = "t101-igw"
        }
      }
          
          
      resource "aws_route_table" "peter_rt" {
        vpc_id = aws_vpc.peter_vpc.id
          
        tags = {
          Name = "t101-rt"
        }
      }
          
      resource "aws_route_table_association" "peter_rtassociation1" {
        subnet_id      = aws_subnet.peter_subnet1.id
        route_table_id = aws_route_table.peter_rt.id
      }
          
      resource "aws_route_table_association" "peter_rtassociation2" {
        subnet_id      = aws_subnet.peter_subnet2.id
        route_table_id = aws_route_table.peter_rt.id
      }
          
      resource "aws_route" "peter_defaultroute" {
        route_table_id         = aws_route_table.peter_rt.id
        destination_cidr_block = "0.0.0.0/0"
        gateway_id             = aws_internet_gateway.peter_igw.id
      }
          
      output "aws_vpc_id" {
        value = aws_vpc.peter_vpc.id
      }    
      
    • sg.tf
      # sg.tf
      resource "aws_security_group" "peter_sg" {
        vpc_id      = aws_vpc.peter_vpc.id
        name        = "T101 SG"
        description = "T101 Study SG"
      }
          
      resource "aws_security_group_rule" "peter_sginbound" {
        type              = "ingress"
        from_port         = 80
        to_port           = 80
        protocol          = "tcp"
        cidr_blocks       = ["0.0.0.0/0"]
        security_group_id = aws_security_group.peter_sg.id
      }
          
      resource "aws_security_group_rule" "peter_sgoutbound" {
        type              = "egress"
        from_port         = 0
        to_port           = 0
        protocol          = "-1"
        cidr_blocks       = ["0.0.0.0/0"]
        security_group_id = aws_security_group.peter_sg.id
      }
          
      output "aws_security_group_id" {
        value       = aws_security_group.peter_sg.id
      }    
      
    • ec2.tf
      # ec2.tf
      data "aws_ami" "peter_amazonlinux2" {
        most_recent = true
        filter {
          name   = "owner-alias"
          values = ["amazon"]
        }
          
        filter {
          name   = "name"
          values = ["amzn2-ami-hvm-*-x86_64-ebs"]
        }
          
        owners = ["amazon"]
      }
          
      resource "aws_instance" "peter_ec2" {
          
        depends_on = [
          aws_internet_gateway.peter_igw
        ]
          
        ami                         = data.aws_ami.peter_amazonlinux2.id
        associate_public_ip_address = true
        instance_type               = "t2.micro"
        vpc_security_group_ids      = ["${aws_security_group.peter_sg.id}"]
        subnet_id                   = aws_subnet.peter_subnet1.id
          
        user_data = <<-EOF
                    #!/bin/bash
                    wget https://busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/busybox-x86_64
                    mv busybox-x86_64 busybox
                    chmod +x busybox
                    RZAZ=$(curl http://169.254.169.254/latest/meta-data/placement/availability-zone-id)
                    IID=$(curl 169.254.169.254/latest/meta-data/instance-id)
                    LIP=$(curl 169.254.169.254/latest/meta-data/local-ipv4)
                    echo "< h1>RegionAz($RZAZ) : Instance ID($IID) : Private IP($LIP) : Web Server</h1><br/>Greetings commander" > index.html
                    nohup ./busybox httpd -f -p 80 &
                    EOF
          
        user_data_replace_on_change = true
          
        tags = {
          Name = "t101-peter_ec2"
        }
      }
          
      output "peter_ec2_public_ip" {
        value       = aws_instance.peter_ec2.public_ip
        description = "The public IP of the Instance"
      }    
      
  • 배포 후 EC2 확인
    $ terraform apply -auto-approve
    # => ...
    #    Outputs:
    
    #    aws_security_group_id = "sg-07761e7b1e39b5af6"
    #    aws_vpc_id = "vpc-0e1ad43009a8fe8fa"
    #    peter_ec2_public_ip = "54.180.146.116"
      
    # ami 데이터 확인
    $ terraform state show data.aws_ami.my_amazonlinux2
      
    # EC2 인스턴스 정보 확인
    $ terraform state show aws_instance.myec2
      
    # EC2 인스턴스 IP 확인
    $ echo "aws_instance.peter_ec2.public_ip" | terraform console
    # => "54.180.146.116"
    $ terraform output -raw peter_ec2_public_ip
    # => 54.180.146.116
      
    # curl 로 접속 확인
    $ PETER_EC2_IP=$(terraform output -raw peter_ec2_public_ip)
    $ curl $PETER_EC2_IP                                      
    # => < h1>RegionAz(apne2-az1) : Instance ID(i-0d4b665d1ce7a4b09) : Private IP(10.10.1.41) : Web Server</h1><br/>Greetings commander
    
  • 브라우저로 접속 확인 20240622_terraform_w2_c2_p1_4.png
  • 그래프 확인 data.aws_ami.peter_amazonlinux2 aws_instance.peter_ec2 aws_internet_gateway.peter_igw aws_security_group.peter_sg aws_subnet.peter_subnet1 aws_vpc.peter_vpc aws_route.peter_defaultroute aws_route_table.peter_rt aws_route_table_association.peter_rtassociation1 aws_route_table_association.peter_rtassociation2 aws_subnet.peter_subnet2 aws_security_group_rule.peter_sginbound aws_security_group_rule.peter_sgoutbound

마치며

  • 실습을 통해 리소스 유형과 리소스 이름의 사용법, 리소스 속성 등에 대해 이해를 높일 수 있었습니다. 새삼스럽지만 이렇게 쉽게 각종 리소스들이 생성되고, 서비스가 운영된다는게 신기합니다.
  • 악분님 실습도 해보고 싶었지만 생각보다 여기까지 시간이 많이 소요되어 다음번으로 미루어야겠습니다. :cry:
  • 따라가기 버거운 만큼 남는것이 많은 스터디가 될것 같습니다. 끝까지 생존해서 완주 할 수 있길 바라며 오늘의 포스팅을 마칩니다.