카이도스의 Tech Blog

2주차 - 테라폼 기본 사용 2/3 본문

Terraform

2주차 - 테라폼 기본 사용 2/3

카이도스 2023. 7. 12. 11:00
728x90
반응형
CloudNet@-가시다(Gasida)님의 Terraform 스터디와
테라폼으로 시작하는 IaC’ 책을 기준하여 정리하였습니다.

2023.07.05 - [Terraform] - 1주차 - 테라폼 기본 사용 1/2

 

1주차 - 테라폼 기본 사용 1/2

CloudNet@-가시다(Gasida)님의 Terraform 스터디와 테라폼으로 시작하는 IaC’ 책을 기준하여 정리하였습니다. 1. Iac와 테라폼 1-1. 테라폼 : Terraform의 구성 언어는 선언적입니다. 즉, 인프라에 대해 원하

djdakf1234.tistory.com


3.5 데이터 소스

데이터 소스는 테라폼으로 정의되지 않은 외부 리소스 또는 저장된 정보를 테라폼 내에서 참조할 때 사용한다.


A. 데이터 소스 구성

  • 실습을 위해서 3.5 디렉터리를 신규 생성 후 열기 → main.tf 파일 생성
mkdir 3.5 && cd 3.5
  • 데이터 소스 블록은 data로 시작, 이후 ‘데이터 소스 유형’을 정의 ← Resource 블록 정의와 유사
    • 데이터 소스 유형은 첫 번째 _를 기준으로 앞은 프로바이더 이름, 뒤는 프로바이더에서 제공하는 리소스 유형을 의미한다.
    • 데이터 소스 유형을 선언한 뒤에는 고유한 이름을 붙인다. 리소스의 이름과 마찬가지로 이름은 동일한 유형에 대한 식별자 역할을 하므로 중복될 수 없다.
    • 이름 뒤에는 데이터 소스 유형에 대한 구성 인수들은 { } 안에 선언한다. 인수가 필요하지 않은 유형도 있지만, 그때에도 { } 는 입력한다
**data** "**local**_file" "abc" {
  filename = "${path.module}/abc.txt"
}
  • 데이터 소스를 정의할 때 사용 가능한 메타인수는 다음과 같다.
    • depends_on : 종속성을 선언하며, 선언된 구성요소와의 생성 시점에 대해 정의
    • count : 선언된 개수에 따라 여러 리소스를 생성
    • for_each : map 또는 set 타입의 데이터 배열의 값을 기준으로 여러 리소스를 생성
    • lifecycle : 리소스의 수명주기 관리
  • 실습 확인
# 실습 확인을 위해서 abc.txt 파일 생성
echo "t101 study - 2week" > abc.txt

# 
**terraform init && terraform plan && terraform apply -auto-approve**
terraform state list
****
# 테라폼 콘솔 : 데이터 소스 참조 확인
**terraform console**
> 
**data.local_file.abc
...
data.local_file.abc.filename
data.local_file.abc.content
data.local_file.abc.id**
exit

B. 데이터 소스 속성 참조

  • 데이터 소스로 읽은 대상을 참조하는 방식은 리소스와 구별되게 data가 앞에 붙는다. 속성 값은 다음과 같이 접근할 수 있다.
# Terraform Code
data "<리소스 유형>" "<이름>" {
  <인수> = <값>
}

# 데이터 소스 참조
data.<리소스 유형>.<이름>.<속성>
  • 코드 예시 : 데이터 소스를 활용해 AWS 가용영역 인수를 정의 → 리전 내에서 사용 가능한 가용영역 목록 가져오기
# Declare the data source
data "aws_availability_zones" "available" {
  state = "available"
}
resource "aws_subnet" "primary" {
  availability_zone = data.aws_availability_zones.available.names[0]
  # e.g. ap-northeast-2a
}
resource "aws_subnet" "secondary" {
  availability_zone = data.aws_availability_zones.available.names[1]
  # e.g. ap-northeast-2b
}
  • 아래 문서에서 데이터 소스로 가져오기 위한 조건인 인수는 Argument로 표현되어 있고, 가져온 데이터 소스의 내용은 Attributes에 안내되어 있다

Terraform Registry

resource "local_file" "abc" {
  content  = "123!"
  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"
}
  • 확인
#
**terraform apply -auto-approve**
terraform state list

****# 파일 확인
ls *.txt
**diff abc.txt def.txt**

# graph 확인
terraform graph > **graph.dot**

# 테라폼 콘솔 : 데이터 소스 참조 확인
**terraform console**
> 
**data.local_file.abc.content
...**
exit

# 생성된 파일 권한이 다름??? 왜지???
ls -l

(추가 실습) az.tf 파일 생성 - az datasource - 링크

data "aws_availability_zones" "available" {
  state = "available"
}
  • 확인
#
terraform init -**upgrade** && terraform apply -auto-approve
terraform state list

#
**terraform state show data.aws_availability_zones.available**
# data.aws_availability_zones.available:
data "aws_availability_zones" "available" {
    group_names = [
        "ap-northeast-2",
    ]
    id          = "ap-northeast-2"
    names       = [
        "ap-northeast-2a",
        "ap-northeast-2b",
        "ap-northeast-2c",
        "ap-northeast-2d",
    ]
    state       = "available"
    zone_ids    = [
        "apne2-az1",
        "apne2-az2",
        "apne2-az3",
        "apne2-az4",
    ]
}

# 
**terraform console**
>
-----------------
data.aws_availability_zones.available
data.aws_availability_zones.available.names
data.aws_availability_zones.available.names[0]
data.aws_availability_zones.available.names[1]
data.aws_availability_zones.available.zone_ids[0]
data.aws_availability_zones.available.zone_ids[1]
exit
-----------------

3.6 입력 변수 Variable

  • 입력 변수는 인프라를 구성하는 데 필요한 속성 값을 정의해 코드의 변경 없이 여러 인프라를 생성하는 데 목적이 있다.
  • 테라폼에서는 이것을 입력 변수 Input Variables 로 정의한다.

A. 변수 선언 방식

  • 실습을 위해서 3.6 디렉터리를 신규 생성 후 열기 → main.tf 파일 생성
cd .. && rm -rf 3.5
mkdir 3.6 && cd 3.6
  • 변수는 variable로 시작되는 블록으로 구성된다. 변수 블록 뒤의 이름 값은 동일 모듈 내 모든 변수 선언에서 고유해야 하며, 이 이름으로 다른 코드 내에서 참조된다.
# variable 블록 선언의 예
variable "<이름>" {
 <인수> = <값>
}

variable "image_id" {
 type = string
}
  • 테라폼 예약 변수 이름으로 사용 불가능 : source, version, providers, count, for_each, lifecycle, depends_on, locals
  • 변수 정의 시 사용 가능한 메타인수
    • default : 변수 값을 전달하는 여러 가지 방법을 지정하지 않으면 기본값이 전달됨, 기본값이 없으면 대화식으로 사용자에게 변수에 대한 정보를 물어봄
    • type : 변수에 허용되는 값 유형 정의, string number bool list map set object tuple 와 유형을 지정하지 않으면 any 유형으로 간주
    • description : 입력 변수의 설명
    • validation : 변수 선언의 제약조건을 추가해 유효성 검사 규칙을 정의 - 링크
    • sensitive : 민감한 변수 값임을 알리고 테라폼의 출력문에서 값 노출을 제한 (암호 등 민감 데이터의 경우) - 링크
    • nullable : 변수에 값이 없어도 됨을 지정

B. 변수 유형 : 지원되는 변수의 범주와 형태

  • 기본 유형
    • string : 글자 유형
    • number : 숫자 유형
    • bool : true 또는 false
    • any : 명시적으로 모든 유형이 허용됨을 표시
  • 집합 유형
    • list (<유형>): 인덱스 기반 집합
    • map (<유형>): 값 = 속성 기반 집합이며 키값 기준 정렬
    • set (<유형>): 값 기반 집합이며 정렬 키값 기준 정렬
    • object ({<인수 이름>=<유형>, …})
    • tuple ([<유형>, …])
  • list와 set은 선언하는 형태가 비슷하지만 참조 방식이 인덱스와 키로 각각 차이가 있고, map와 set의 경우 선언된 값이 정렬되는 특징을 가진다.
  • 변수 유형별 선언 방식의 예시 - main.tf 파일
variable "string" {
  **type        = string**
  description = "var String"
  default     = "myString"
}

variable "number" {
  **type    = number**
  default = 123
}

variable "boolean" {
  default = true
}

variable "list" {
  default = [
    "google",
    "vmware",
    "amazon",
    "microsoft"
  ]
}

**output** "list_index_0" {
  value = var.list.0
}

**output** "list_all" {
  value = [
    for name in var.list : upper(name)
  ]
}

variable "map" { # Sorting
  default = {
    aws   = "amazon",
    azure = "microsoft",
    gcp   = "google"
  }
}

variable "set" { # Sorting
  type = set(string)
  default = [
    "google",
    "vmware",
    "amazon",
    "microsoft"
  ]
}

variable "object" {
  type = object({ name = string, age = number })
  default = {
    name = "abc"
    age  = 12
  }
}

variable "tuple" {
  type    = tuple([string, number, bool])
  default = ["abc", 123, true]
}

variable "ingress_rules" { # optional ( >= terraform 1.3.0)
  type = list(object({
    port        = number,
    description = optional(string),
    protocol    = optional(string, "tcp"),
  }))
  default = [
    { port = 80, description = "web" },
  { port = 53, protocol = "udp" }]
}
  • 확인
#
terraform init && terraform plan && terraform apply -auto-approve
terraform state list

#
**terraform output**
list_all = [
  "GOOGLE",
  "VMWARE",
  "AMAZON",
  "MICROSOFT",
]
list_index_0 = "google"

C. 유효성 검사 : 입력되는 변수 타입 지징 이외, 사용자 지정 유효성 검사가 가능

  • 변수 블록 내에 validation 블록에서 조건인 condition에 지정되는 규칙이 true 또는 false를 반환해야 하며, error_message는 condition 값의 결과가 false 인 경우 출력되는 메시지를 정의한다.
  • regex 함수는 대상의 문자열에 정규식을 적용하고 일치하는 문자열을 반환하는데, 여기에 can 함수를 함께 사용하면 정규식에 일치하지 않는 경우의 오류를 검출한다.
  • validation 블록은 중복으로 선언할 수 있다.
  • variable 유효성 검사의 예 - main.tf 코드 파일 내용 수정
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
    error_message = "The image_id value must exceed 4."
  }

  **validation** {
    # regex(...) fails if it cannot find a match
    condition     = can(regex("^ami-", var.image_id))
    error_message = "The image_id value must starting with \\"ami-\\"."
  }
}
  • 확인
#
**terraform apply -auto-approve**
var.image_id
  The id of the machine image (AMI) to use for the server.

  Enter a value: **ami**
...

#
**terraform apply -auto-approve**
var.image_id
  The id of the machine image (AMI) to use for the server.

  Enter a value: **ami-**
...

#
**terraform apply -auto-approve**
var.image_id
  The id of the machine image (AMI) to use for the server.

  Enter a value: **ami-12345678**
...

D. 변수 참조 : variable은 코드 내에서 var.<이름>으로 참조된다.

  • main.tf 코드 파일 내용 수정
variable "my_password" {}

resource "local_file" "abc" {
  content  = var.my_password
  filename = "${path.module}/abc.txt"
}
  • 실행
#
terraform init -upgrade
**terraform apply -auto-approve**
var.my_password
  Enter a value: **qwe123**
...

# 확인
terraform state list
**terraform state show local_file.abc**
cat abc.txt ; echo

# 해당 파일에 다른 내용으로 변경해보기
**terraform apply -auto-approve**
var.my_password
  Enter a value: **t101mypss**
...

# 확인
cat abc.txt ; echo

E. 민감한 변수 취급 : 입력 변수의 민감 여부 선언 가능

  • main.tf 코드 파일 내용 수정
    • 기본값 추가로 입력 항목은 발생하지 않지만, 출력에서 참조되는 변수 값이(sensitive)로 감춰지는 것을 확인 할 수 있다
variable "my_password" {
  default   = "password"
  **sensitive = true**
}

resource "local_file" "abc" {
  content  = var.my_password
  filename = "${path.module}/abc.txt"
}
  • 확인 : 민감한 변수로 지정해도 terraform.tfstate 파일에는 결과물이 평문으로 기록되므로 State 파일의 보안에 유의해야 한다

F. 변수 입력 방식과 우선순위

  • 변수 입력 방식과 우선순위
    • variable의 목적은 코드 내용을 수정하지 않고 테라폼의 모듈적 특성을 통해 입력되는 변수로 재사용성을 높이는 데 있다.
    • 특히 입력 변수라는 명칭에 맞게 사용자는 프로비저닝 실행 시에 원하는 값으로 변수에 정의할 수 있다.
    • 선언되는 방식에 따라 변수의 우선순위가 있으므로, 이를 적절히 사용해 로컬 환경과 빌드 서버 환경에서의 정의를 다르게 하거나, 프로비저닝 파이프라인을 구성하는 경우 외부 값을 변수에 지정할 수 있다.
    • main.tf 코드 파일 내용 수정
    variable "my_var" {}
    
    resource "local_file" "abc" {
      content  = var.my_var
      filename = "${path.module}/abc.txt"
    }
    
    • [우선순위 수준]의 숫자가 작을수록 우선순위도 낮다.
    [우선순위 수준 1] 실행 후 입력
    # 실행
    **terraform apply -auto-approve**
    var.my_var
      Enter a value: **var1**
    ...
    
    # 확인
    terraform state show local_file.abc
    cat abc.txt ; echo
    
    [우선순위 수준 2] variable 블록의 default 값
    • main.tf 코드 파일 내용 수정
    variable "my_var" {
      **default = "var2"**
    }
    
    resource "local_file" "abc" {
      content  = var.my_var
      filename = "${path.module}/abc.txt"
    }
    
    • 확인
    # 실행
    **terraform apply -auto-approve**
    
    # 확인
    terraform state show local_file.abc
    cat abc.txt ; echo
    
    [우선순위 수준 3] 환경 변수 (TF_VAR 변수 이름)
    • 시스템 환경 변수의 접두사에 TF_VAR_ 가 포함되면 그 뒤의 문자열을 변수 이름으로 인식한다.
    • 앞서 default로 추가한 내용과 어떤 방식이 우선순위가 높은지 확인해보자
    # Linux/macOS
    export TF_VAR_**my_var=var3**
    **terraform apply -auto-approve**
    
    # 확인
    cat abc.txt ; echo
    
    [우선순위 수준 4] terraform.tfvars에 정의된 변수 선언
    • 루트 모듈의 main.tf 파일과 같은 위치에 terraform.tfvars 파일을 생성해 변수에 대한 값을 추가하고 앞서 선언한 변수 선언과 비교해 우선순위를 확인
    #
    echo 'my_var="**var4**"' > terraform.tfvars
    cat terraform.tfvars
    
    #
    **terraform apply -auto-approve**
    
    # 확인
    cat abc.txt ; echo
    
    [우선순위 수준 5] *.auto.tfvars에 정의된 변수 선언
    • 파일명의 정렬에 따라 우선순위가 적용된다
    # a.auto.tfvars 파일 생성
    echo 'my_var="**var5_a**"' > **a**.auto.tfvars
    ls *.tfvars
    
    #
    **terraform apply -auto-approve**
    
    # 확인
    cat abc.txt ; echo
    
    # b.auto.tfvars 파일 생성
    echo 'my_var="**var5_b**"' > **b**.auto.tfvars
    ls *.tfvars
    
    #
    **terraform apply -auto-approve**
    
    # 확인
    cat abc.txt ; echo
    
    
    [우선순위 수준 6] *.auto.tfvars.json에 정의된 변수 선언
    • *.auto.tfvars와 같이 파일명의 정렬에 따라 우선순위가 적용된다
    # a.auto.tfvars.json 파일 생성
    cat <<EOF > a.auto.tfvars.json
    {
      "my_var" : "**var6_a**"
    }
    EOF
    ls *.tfvars ; ls *.json
    
    #
    **terraform apply -auto-approve**
    
    # 확인
    cat abc.txt ; echo
    
    # c.auto.tfvars.json 파일 생성
    cat <<EOF > c.auto.tfvars.json
    {
      "my_var" : "**var6_c**"
    }
    EOF
    ls *.tfvars ; ls *.json
    
    #
    **terraform apply -auto-approve**
    
    # 확인
    cat abc.txt ; echo
    
    [우선순위 수준 7] CLI 실행 시 -var 인수에 지정 또는 -var-file로 파일 지정
    • 여러 인수가 선언되는 경우 나중에 선언된 변수의 우선순위가 높다
    #
    **terraform apply -auto-approve -var=my_var=var7**
    cat abc.txt ; echo
    
    #
    **terraform apply -auto-approve -var=my_var=var7 -var=my_var=var8**
    cat abc.txt ; echo
    
    • *.tfvars와 같은 형식의 내용의 파일이라면 -var-file로 지정할 수 있다.
    # var9.txt 파일 생성
    echo 'my_var="var9"' > var9.txt
    
    #
    **terraform apply -auto-approve -var=my_var=var7 -var-file="var9.txt"**
    cat abc.txt ; echo
    
    
    • .tfvars 확장자로 생성된 파일에 변수를 미리 기입하면 실행 시 입력해야 하는 변수 값을 하나의 파일에서 관리할 수 있다는 장점이 있다.

3.7 local 지역 값

  • 코드 내에서 사용자가 지정한 값 또는 속성 값을 가공해 참조 가능한 local (지역 값)은 외부에서 입력되지 않고, 코드 내에서만 가공되어 동작하는 값을 선언한다.
  • 'local’은 입력 변수와 달리 선언된 모듈 내에서만 접근 가능하고, 변수처럼 실행 시에 입력받을 수 없다.
  • 로컬은 사용자가 테라폼 코드를 구현할 때 값이나 표현식을 반복적으로 사용할 수 있는 편의를 제공한다. 하지만 빈번하게 여러 곳에서 사용되는 경우 실제 값에 대한 추적이 어려워져 유지 관리 측면에서 부담이 발생할 수 있으므로 주의해야 한다.

A. local 선언

  • 실습을 위해서 3.7 디렉터리를 신규 생성 후 열기 → main.tf 파일 생성
cd .. && rm -rf 3.6
mkdir 3.7 && cd 3.7
  • 로컬이 선언되는 블록은 locals로 시작한다. 선언되는 인수에 표현되는 값은 상수만이 아닌 리소스의 속성, 변수의 값들도 조합해 정의할 수 있다.
  • 동일한 tf 파일 내에서 여러 번 선언하는 것도 가능하고 여러 파일에 걸쳐 만드는 것도 가능하다.
  • 다만 lcoals에 선언한 로컬 변수 이름은 전체 루트 모듈 내에서 유일해야 한다.
  • 정의되는 속성 값은 지정된 값의 형태에 따라 다양한 유형으로 정의할 수 있다.
  • main.tf 파일 : local 값 선언 방식의 예
variable "prefix" {
  default = "hello"
}

locals {
  name    = "terraform"
  content = "${var.prefix} ${local.name}"
  my_info = {
    age    = 20
    region = "KR"
  }
  my_nums = [1, 2, 3, 4, 5]
}

locals {
  content = "content2" # 중복 선언되었으므로 오류가 발생한다.
}

 

  • 실행
#
terraform init
variable "prefix" {
  default = "hello"
}

locals {
  name    = "terraform"
  content = "${var.prefix} ${local.name}"
  my_info = {
    age    = 20
    region = "KR"
  }
  my_nums = [1, 2, 3, 4, 5]
}
#
terraform init
terraform apply -auto-approve
terraform state list

B. local 참조

  • 선언된 local 값은 local.<이름>으로 참조할 수 있다.
  • 테라폼 구성 파일을 여러 개 생성해 작업하는 경우 서로 다른 파일에 선언되어 있더라도 다른 파일에서 참조할 수 있다.
  • main.tf 파일 내용 수정
variable "prefix" {
  default = "hello"
}

locals {
  name    = "terraform"
}

resource "local_file" "abc" {
  content  = local.content
  filename = "${path.module}/abc.txt"
}
locals {
  content = "${var.prefix} ${local.name}"
}
  • 실행 : main.tf의 content 내용 값으로 local.content를 참조하며, 해당 값은 다른 테라폼 구성 파일에 있지만 실행 시점에는 마치 하나의 구성 파일 처럼 올바로 표기되는 것을 확인할 수 있다.
#
**ls *.tf**
terraform init -upgrade
terraform apply -auto-approve
terraform state list
terraform state show local_file.abc

# graph 확인 > graph.dot 파일 선택 후 오른쪽 상단 DOT 클릭
terraform graph > **graph.dot**

#
cat abc.txt ; echo
  • 이 예제에서는 서로 다른 테라폼 구성 파일에서도 로컬 값을 참조할 수 있다는 가능성을 확인할 수 있지만, 관리 측면에서는 서로 참조하는 로컬값이 파편화되어 유지 보수가 어려워질 수 있으므로 주의가 필요하다.

(추가) terraform.tfvars에 정의된 변수 선언

#
echo 'prefix="t101-study"' > terraform.tfvars
cat terraform.tfvars

#
terraform apply -auto-approve
cat abc.txt ; echo

3.8 출력 output

출력 값은 주로 테라폼 코드의 프로비저닝 수행 후의 결과 속성 값을 확인하는 용도로 사용된다.

또한 프로그래밍 언어에서 코드 내 요소 간에 제한된 노출을 지원하듯, 테라폼 모듈 간, 워크스페이스 간 데이터 접근 요소로도 활용할 수 있다.

예를 들면 자바의 getter와 비슷한 역할이다. 출력 값의 용도는 다음과 같이 정의할 수 있다.

  • 루트 모듈에서 사용자가 확인하고자 하는 특정 속성 출력
  • 자식 모듈의 특정 값을 정의하고 루트 모듈에서 결과를 참조
  • 서로 다른 루트 모듈의 결과를 원격으로 읽기 위한 접근 요소

A. output 선언

  • 모듈 내에서 생성되는 속성 값들은 outout 블록에 정의된다
output "instance_ip_addr" {
  value = "<http://$>{aws_instance.server.private_ip}"
}
  • 출력되는 값은 value의 값이며 테라폼이 제공하는 조합과 프로그래밍적인 기능들에 의해 원하는 값을 출력할 수 있다.
  • 주의할 점은 output 결과에서 리소스 생성 후 결정되는 속성 값은 프로비저닝이 완료되어야 최종적으로 결과를 확인할 수 있고 terraform plan 단계에서는 적용될 값이 출력하지 않는다는 것이다.
  • 변수 정의 시 사용 가능한 메타인수는 다음과 같다.
    • description : 출력 값 설명
    • sensitive : 민감한 출력 값임을 알리고 테라폼의 출력문에서 값 노출을 제한
    • depends_on : value에 담길 값이 특정 구성에 종속성이 있는 경우 생성되는 순서를 임의로 조정
    • precondition : 출력 전에 지정된 조건을 검증

B. output 활용

  • 실습을 위해서 3.8 디렉터리를 신규 생성 후 열기 → main.tf 파일 생성
mkdir 3.8 && cd 3.8
  • main.tf 파일 내용
    • (참고) abspath : 파일 시스템 경로를 포함하는 문자열을 가져와 절대 경로로 변환하는 함수 - 링크
resource "local_file" "abc" {
  content  = "abc123"
  filename = "${path.module}/abc.txt"
}

output "file_id" {
  value = local_file.abc.id
}

output "file_abspath" {
  value = **abspath**(local_file.abc.filename)
}
  • 실행 : plan 실행 시, 이미 정해진 속성은 출력을 예측하지만 아직 생성되지 않은 file_id 값은 값의 경우는 결과 예측을 할 수 없다
# plan 실행 시, 이미 정해진 속성은 출력을 예측하지만
terraform init && terraform plan
...
Changes to Outputs:
  + file_abspath = "/Users/gasida/Downloads/workspaces/3.8/abc.txt"
  + file_id      = (known after apply)


# 
terraform apply -auto-approve
...
Outputs:
file_abspath = "/Users/gasida/Downloads/workspaces/3.8/abc.txt"
file_id = "6367c48dd193d56ea7b0baad25b19455e529f5ee"

# graph 확인 > graph.dot 파일 선택 후 오른쪽 상단 DOT 클릭
terraform graph > graph.dot

#
terraform state list
terraform output

3.9 반복문

악분님이 테라폼 반복문 관련 내용(count, for_each, for expression 등)을 Youtube 영상들로 잘 정리해주셨습니다.

 

테라폼

 

www.youtube.com

  • list 형태의 값 목록이나 Key-Value 형태의 문자열 집합인 데이터가 있는 경우 동일한 내용에 대해 테라폼 구성 정의를 반복적으로 하지 않고 관리할 수 있다.

A. count : 반복문, 정수 값만큼 리소스나 모듈을 생성

  • 실습을 위해서 3.9 디렉터리를 신규 생성 후 열기 → main.tf 파일 생성
mkdir 3.9 && cd 3.9
  • 리소스 또는 모듈 블록에 count 값이 정수인 인수가 포함된 경우 선언된 정수 값만큼 리소스나 모듈을 생성하게 된다.
  • count에서 생성되는 참조값은 count.index이며, 반복하는 경우 0부터 1씩 증가해 인덱스가 부여된다.
  • main.tf 파일
resource "local_file" "abc" {
  **count    = 5**
  content  = "abc"
  filename = "${path.module}/abc.txt"
}

output "filecontent" {
  value = local_file.abc.*.content
}

output "fileid" {
  value = local_file.abc.*.id
}

output "filename" {
  value = local_file.abc.*.filename
}
  • 실행 후 확인 : 5개의 파일이 생성되어야 하지만 파일명이 동일하여 결과적으로 하나의 파일만 존재 ← count 사용 시 주의
#
terraform init && terraform apply -auto-approve
terraform state list
terraform state show local_file.abc[0]
terraform state show local_file.abc[4] # 왜 정보 출력이 안되나요?
ls *.txt

# 
**terraform console**
>
-----------------
local_file.abc
local_file.abc[0]
local_file.abc[4]
exit
-----------------

# 
terraform output
terraform output filename
terraform output fileid
terraform output filecontent
  • main.tf 파일 수정 : count.index 값 추가
resource "local_file" "abc" {
  count    = 5
  content  = "**abc${count.index}**"
  filename = **"${path.module}/abc${count.index}.txt"**
}

output "fileid" {
  value = local_file.abc.*.id
}

output "filename" {
  value = local_file.abc.*.filename
}

output "filecontent" {
  value = local_file.abc.*.content
}
  • 실행 후 확인
#
terraform apply -auto-approve
terraform state list
ls *.txt

# 
terraform console
>
-----------------
local_file.abc[0]
local_file.abc[4]
exit
-----------------

# 
terraform output
terraform output **filename**
terraform output fileid
terraform output filecontent

# graph 확인 > graph.dot 파일 선택 후 오른쪽 상단 DOT 클릭
terraform graph > **graph.dot**
  • 때때로 여러 리소스나 모듈의 count로 지정되는 수량이 동일해야 하는 상황이 있다. 이 경우 count에 부여되는 정수 값을 외부 변수에 식별되도록 구성할 수 있다.
  • main.tf 파일 수정 : list 형태의 배열을 활용한 반복문 동작 구성
**variable** "names" {
  type    = list(string)
  **default** = ["a", "b", "c"]
}

resource "local_file" "abc" {
  count   = length(var.names)
  content = "abc"
  # 변수 인덱스에 직접 접근
  filename = "${path.module}/abc-${var.names[count.index]}.txt"
}

resource "local_file" "def" {
  count   = length(var.names)
  content = local_file.abc[count.index].content
  # element function 활용
  filename = "${path.module}/def-${element(var.names, count.index)}.txt"
}
  • 실행 후 확인 : local_file.abc와 local_file.def는 var.name에 선언되는 값에 영향을 받아 동일한 갯수만큼 생성하게 된다.
    • local_file.def의 경우 local_file.abc와 개수가 같아야 content에 선언되는 인수 값에 오류가 없을 것이므로 서로 참조되는 리소스와 모듈의 반복정의에 대한 공통의 영향을 주는 변수로 관리할 수 있다.
#
terraform apply -auto-approve
terraform state list
ls *.txt
diff abc-a.txt abc-b.txt
diff abc-a.txt def-c.txt

# 
terraform console
>
-----------------
local_file.abc[0]
local_file.def[4]
exit
-----------------

# 
terraform output
terraform output **filename**
terraform output fileid
terraform output filecontent

# graph 확인 > graph.dot 파일 선택 후 오른쪽 상단 DOT 클릭
terraform graph > **graph.dot**
  • count로 생성되는 리소스의 경우 <리소스 타입>.<이름>[<인덱스 번호>], 모듈의 경우 module.<모듈 이름>[<인덱스 번호>]로 해당 리소스의 값을 참조한다.
  • 단, 모듈 내에 count 적용이 불가능한 선언이 있으므로 주의해야 한다.
    • 예를 들어 provider 블록 선언부가 포함되어 있는 경우에는 count 적용이 불가능하다 → provider 분리
    • 또한 외부 변수가 list 타입인 경우 중간에 값이 삭제되면 인덱스가 줄어들어 의도했던 중간 값에 대한 리소스만 삭제되는 것이 아니라 이후의 정의된 리소스들도 삭제되고 재생성된다.

B. for_each : 반복문, 선언된 key 값 개수만큼 리소스를 생성

  • 리소스 또는 모듈 블록에서 for_each에 입력된 데이터 형태가 map 또는 set이면, 선언된 key 값 개수만큼 리소스를 생성하게 된다.
  • main.tf 파일 수정 : for_each 값이 있는 반복문 동작 확인
resource "local_file" "abc" {
  for_each = {
    a = "content a"
    b = "content b"
  }
  content  = each.value
  filename = "${path.module}/${each.key}.txt"
}
  • 실행 후 확인
#
terraform apply -auto-approve
terraform state list
ls *.txt
cat a.txt ;echo
cat b.txt ;echo

# 
terraform console
>
-----------------
local_file.abc
local_file.abc[a]
local_file.abc["a"]
exit
-----------------
  • for_each가 설정된 블록에서는 each 속성을 사용해 구성을 수정할 수 있다
    • each.key : 이 인스턴스에 해당하는 map 타입의 key 값
    • each.value : 이 인스턴스에 해당하는 map의 value 값
  • 생성되는 리소스의 경우 <리소스 타입>.<이름>[<key>], 모듈의 경우 module.<모듈 이름>[<key>]로 해당 리소스의 값을 참조한다.
  • 이 참조 방식을 통해 리소스 간 종송석을 정의하기도 하고 변수로 다른 리소스에서 사용하거나 출력을 위한 결과 값으로 사용한다.
  • main.tf 파일 수정 : local_file.abc는 변수의 map 형태의 값을 참조, local_file.def의 경우 local_file.abc 도한 결과가 map으로 반환되므로 다시 for_each 구문을 사용할 수 있다
variable "names" {
  default = {
    a = "content a"
    b = "content b"
    c = "content c"
  }
}

resource "local_file" "abc" {
  for_each = var.names
  content  = each.value
  filename = "${path.module}/abc-${each.key}.txt"
}

resource "local_file" "def" {
  for_each = local_file.abc
  content  = each.value.content
  filename = "${path.module}/def-${each.key}.txt"
}
  • 실행 후 확인
#
terraform apply -auto-approve
terraform state list
ls *.txt
  • key 값은 count의 index와는 달리 고유하므로 중간에 값을 삭제한 후 다시 적용해도 삭제한 값에 대해서만 리소스를 삭제한다.
  • main.tf 파일 수정 : count 경우와 유사하게 중간 값을 삭제 후 확인
variable "names" {
  default = {
    a = "content a"
    c = "content c"
  }
}

resource "local_file" "abc" {
  for_each = var.names
  content  = each.value
  filename = "${path.module}/abc-${each.key}.txt"
}

resource "local_file" "def" {
  for_each = local_file.abc
  content  = each.value.content
  filename = "${path.module}/def-${each.key}.txt"
}
  • 실행 후 확인
#
terraform apply -auto-approve
terraform state list
ls *.txt

C. for : 복합 형식 값의 형태를 변환하는 데 사용 ← for_each와 다름

  • 예를 들어 list 값의 포맷을 변경하거나 특정 접두사 prefix를 추가할 수도 있고, output에 원하는 형태로 반복적인 결과를 표현할 수 도 있다.
    • list 타입의 경우 값 또는 인덱스와 값을 반환
    • map 타입의 경우 키 또는 키와 값에 대해 반환
    • set 타입의 경우 키 값에 대해 반환
  • main.tf 파일 수정 : list의 내용을 담는 리소스를 생성, var.name의 내용이 결과 파일에 content로 기록됨
variable "names" {
  default = ["a", "b", "c"]
}

resource "local_file" "abc" {
  content  = jsonencode(var.names) # 결과 : ["a", "b", "c"]
  filename = "${path.module}/abc.txt"
  • 실행 후 확인 : jsonencode Function - 링크
#
terraform apply -auto-approve
terraform state list
ls *.txt
**cat abc.txt ;echo**

# 참고 jsonencode Function
terraform console
>
-----------------
**jsonencode({"hello"="world"})**
{"hello":"world"}
exit
-----------------
  • 목표 : var.name 값을 일괄적으로 대문자로 변환!
  • main.tf 파일 수정 : content의 값 정의에 for 구문을 사용하여 내부 값을 일괄적으로 변경
variable "names" {
  default = ["a", "b", "c"]
}

resource "local_file" "abc" {
  content  = **jsonencode([for s in var.names : upper(s)])** # 결과 : ["A", "B", "C"]
  filename = "${path.module}/abc.txt"
}
  • 실행 후 확인 - title
#
terraform apply -auto-approve
terraform state list
ls *.txt
**cat abc.txt ;echo**

# 참고 jsonencode Function
terraform console
>
-----------------
**jsonencode([for s in var.names : upper(s)])**
**[for s in var.names : upper(s)]
[for txt in var.names : upper(txt)]
title("hello world")**
exit
-----------------
  • for 구문을 사용하는 몇 가지 규칙은 다음과 같다
    • list 유형의 경우 반환 받는 값이 하나로 되어 있으면 을, 두 개의 경우 앞의 인수가 인덱스를 반환하고 뒤의 인수가 을 반환
      • 관용적으로 인덱스는 i, 값은 v로 표현
    • map 유형의 경우 반환 받는 값이 하나로 되어 있으면 를, 두 개의 경우 앞의 인수가 를 반환하고 뒤의 인수가 을 반환
      • 관용적으로 키는 k, 값은 v로 표현
    • 결과 값은 for 문을 묶는 기호가 **[ ]**인 경우 tuple로 반환되고 **{ }**인 경우 object 형태로 반환
    • object 형태의 경우 에 대한 쌍은 기호로 구분
    • { } 형식을 사용해 object 형태로 결과를 반환하는 경우 키 값은 고유해야 하므로 값 뒤에 그룹화 모드 심볼(…)를 붙여서 키의 중복을 방지(SQL의 group by 문 또는 Java의 MultiValueMap과 같은 개념)
    • if 구문을 추가해 조건 부여 가능
  • main.tf 파일 수정 : list 유형에 대한 for 구문 처리의 몇 가지 예를 확인
variable "names" {
  type    = list(string)
  default = ["a", "b"]
}

output "A_upper_value" {
  value = [for v in var.names : upper(v)]
}

output "B_index_and_value" {
  value = [for i, v in var.names : "${i} is ${v}"]
}

output "C_make_object" {
  value = { for v in var.names : v => upper(v) }
}

output "D_with_filter" {
  value = [for v in var.names : upper(v) if v != "a"]
}
  • 실행 후 확인
#
terraform apply -auto-approve
terraform state list

# 
**terraform output**
terraform output A_upper_value
terraform output D_with_filter

# 
terraform console
>
-----------------
**var.names
[for v in var.names : upper(v)]
[for i, v in var.names : "${i} is ${v}"]
{ for v in var.names : v => upper(v) }
[for v in var.names : upper(v) if v != "a"]**
exit
-----------------
  • main.tf 파일 수정 : map 유형에 대한 for 구문 처리의 몇 가지 예를 확인
variable "members" {
  type = map(object({
    role = string
  }))
  default = {
    ab = { role = "member", group = "dev" }
    cd = { role = "admin", group = "dev" }
    ef = { role = "member", group = "ops" }
  }
}

output "A_to_tupple" {
  value = [for k, v in var.members : "${k} is ${v.role}"]
}

output "B_get_only_role" {
  value = {
    for name, user in var.members : name => user.role
    if user.role == "admin"
  }
}

output "C_group" {
  value = {
    for name, user in var.members : user.role => name...
  }
}
  • 실행 후 확인
#
terraform apply -auto-approve
terraform state list

# 
**terraform output**
terraform output A_upper_value

# 
terraform console
>
-----------------
**var.members
[for k, v in var.members : "${k} is ${v.role}"]
{for name, user in var.members : name => user.role}
{for name, user in var.members : name => user.role  if user.role == "admin"}
{for name, user in var.members : user.role => name...}**
exit
-----------------

D. dynamic : 리소스 내부 속성 블록 동적인 블록으로 생성

  • count 나 for_each 구문을 사용한 리소스 전체를 여러 개 생성하는 것 이외도 리소스 내에 선언되는 구성 블록을 다중으로 작성해야 하는 경우가 있다.
  • 예를 들면 AWS Security Group 리소스 구성에 ingress, egress 요소가 리소스 선언 내부에서 블록 형태로 여러 번 정의되는 경우다.
**resource** "aws_security_group" "example" {
  name        = "example-security-group"
  description = "Example security group"
  vpc_id.     = aws_vpc.main.id

  **ingress** {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    ipv6_cidr_blocks = ["::/0"]
  }
}
  • 리소스 내의 블록 속성(Attributes as Blocks)은 리소스 자체의 반복 선언이 아닌 내부 속성 요소 중 블록으로 표현되는 부분에 대해서만 반복 구문을 사용해야 하므로, 이때 dynamic 블록을 사용해 동적인 블록을 생성 할 수 있다.
  • dynamic 블록을 작성하려면, 기존 블록의 속성 이름을 dynamic 블록의 이름으로 선언하고 기존 블록 속성에 정의되는 내용을 content 블록에 작성한다.
  • 반복 선언에 사용되는 반복문 구문은 for_each를 사용한다. 기존 for_each 적용 시 each 속성에 key, value가 적용되었다면 dynamic에서는 dynamic에 지정한 이름에 대해 속성이 부여된다.
  • dynamic 블록 활용 예

일반적인 블록 속성 반복 적용 시 dynamic 블록 적용 시

resource "provider_resource" "name" {  
name = "some_resource"  

some_setting { key = a_value }

some_setting { key = b_value }

some_setting { key = c_value }

some_setting { key = d_value } } | resource "provider_resource" "name" { name = "some_resource"

dynamic "some_setting" { for_each = { a_key = a_value b_key = b_value c_key = c_value d_key = d_value }

content {
  key = some_setting.value
}

} } |

  • main.tf 파일 수정 : archive 프로바이더(링크)의 archive_file에 source 블록 선언을 반복 - 링크
data "**archive**_file" "dotfiles" {
  type        = "zip"
  output_path = "${path.module}/dotfiles.zip"

  source {
    content  = "hello a"
    filename = "${path.module}/a.txt"
  }

  source {
    content  = "hello b"
    filename = "${path.module}/b.txt"
  }

  source {
    content  = "hello c"
    filename = "${path.module}/c.txt"
  }
}
  • 실행 후 확인
#
terraform init -upgrade
terraform apply -auto-approve
terraform state list
terraform state show data.archive_file.dotfiles
ls *.zip
unzip dotfiles.zip
ls *.txt
cat a.txt ; echo
  • main.tf 파일 수정 : 리소스 내에 반복 선언 구성을 dynamic 블록으로 재구성
variable "names" {
  default = {
    a = "hello a"
    b = "hello b"
    c = "hello c"
  }
}

data "archive_file" "dotfiles" {
  type        = "zip"
  output_path = "${path.module}/dotfiles.zip"

  dynamic "source" {
    for_each = var.names
    content {
      content  = source.value
      filename = "${path.module}/${source.key}.txt"
    }
  }
}
  • 실행 후 확인 : 동일한 결과가 기대되어 변경 사항이 없다!
#
terraform apply -auto-approve
terraform state list
terraform state show data.archive_file.dotfiles
ls *.zip

[스터디 전용/실습1] VPC + 보안그룹 + EC2 배포

목표 : default VPC 대신 직접 VPC를 만들고, 해당 VPC내에 EC2 1대를 배포


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

 

Terraform Registry

 

registry.terraform.io


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

  • 리소스의 유형리소스의 이름이 차이를 알고, 리소스의 속성(예. ID)를 참조하는 방법에 대해서 익숙해지자

A. VPC 배포 : vpc.tf

# 신규 디렉터리 생성
mkdir pjh-vpc-ec2
cd pjh-vpc-ec2
  • vpc.tf 파일 생성
provider "aws" {
  region  = "ap-northeast-2"
}

resource "aws_vpc" "pjhvpc" {
  cidr_block       = "10.10.0.0/16"

  tags = {
    Name = "t101-study"
  }
}
# 배포
terraform init && terraform plan && terraform apply -auto-approve
terraform state list
terraform state show aws_vpc.pjhvpc

# VPC 확인
export AWS_PAGER=""
aws ec2 describe-vpcs | jq
aws ec2 describe-vpcs --filter 'Name=isDefault,Values=false' | jq
aws ec2 describe-vpcs --filter 'Name=isDefault,Values=false' --output yaml
  • AWS 관리콘솔에서 VPC 생성 정보 확인 : DNS 옵션값 확인
  • vpc.tf 코드 내용 수정 : VPC DNS 옵션 수정
provider "aws" {
  region  = "ap-northeast-2"
}

resource "aws_vpc" "pjhvpc" {
  cidr_block       = "10.10.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "t101-study"
  }
}
# 배포
terraform plan && terraform apply -auto-approve
  • AWS 관리콘솔에서 VPC 생성 정보 확인 : DNS 옵션값 확인
  • vpc.tf 코드 내용 수정 : 서브넷 2개 생성 추가
  • aws_availability_zones.tf 생성
data "aws_availability_zones" "available" {
  state = "available"
}
# 배포
terraform plan && terraform apply -auto-approve

# 확인
terraform state list
terraform state show data.aws_availability_zones.available
provider "aws" {
  region  = "ap-northeast-2"
}

resource "aws_vpc" "pjhvpc" {
  cidr_block       = "10.10.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "t101-study"
  }
}

resource "aws_subnet" "pjhsubnet1" {
  vpc_id     = aws_vpc.pjhvpc.id
  cidr_block = "10.10.1.0/24"

  availability_zone = data.aws_availability_zones.available.names[0]

  tags = {
    Name = "t101-subnet1"
  }
}

resource "aws_subnet" "pjhsubnet2" {
  vpc_id     = aws_vpc.pjhvpc.id
  cidr_block = "10.10.2.0/24"

  availability_zone = data.aws_availability_zones.available.names[1]

  tags = {
    Name = "t101-subnet2"
  }
}

output "aws_vpc_id" {
  value = aws_vpc.pjhvpc.id
}
# 배포
terraform plan && terraform apply -auto-approve
data.aws_availability_zones.available
aws_subnet.pjhsubnet1
aws_subnet.pjhsubnet2
aws_vpc.pjhvpc

terraform state show aws_subnet.pjhsubnet1

terraform output
terraform output aws_vpc_id
terraform output -raw aws_vpc_id
vpc-0d928cd9fee09e007

# graph 확인 > graph.dot 파일 선택 후 오른쪽 상단 DOT 클릭
terraform graph > graph.dot

# 서브넷 확인
aws ec2 describe-subnets --output text

# 참고 : aws ec2 describe-subnets --filters "Name=vpc-id,Values=vpc-<자신의 VPC ID>"
VPCID=$(terraform output -raw aws_vpc_id)
aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPCID" | jq
aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPCID" --output table

vpc.tf 코드 내용 수정 : IGW 인터넷 게이트웨이 추가, IGW 인터넷 게이트웨이로 전달하는 디폴트 라우팅 정보 추가

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

resource "aws_vpc" "pjhvpc" {
  cidr_block       = "10.10.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "t101-study"
  }
}

resource "aws_subnet" "pjhsubnet1" {
  vpc_id     = aws_vpc.pjhvpc.id
  cidr_block = "10.10.1.0/24"

  availability_zone = data.aws_availability_zones.available.names[0]

  tags = {
    Name = "t101-subnet1"
  }
}

resource "aws_subnet" "pjhsubnet2" {
  vpc_id     = aws_vpc.pjhvpc.id
  cidr_block = "10.10.2.0/24"

  availability_zone = data.aws_availability_zones.available.names[1]

  tags = {
    Name = "t101-subnet2"
  }
}


resource "aws_internet_gateway" "pjhigw" {
  vpc_id = aws_vpc.pjhvpc.id

  tags = {
    Name = "t101-igw"
  }
}

resource "aws_route_table" "pjhrt" {
  vpc_id = aws_vpc.pjhvpc.id

  tags = {
    Name = "t101-rt"
  }
}

resource "aws_route_table_association" "pjhrtassociation1" {
  subnet_id      = aws_subnet.pjhsubnet1.id
  route_table_id = aws_route_table.pjhrt.id
}

resource "aws_route_table_association" "pjhrtassociation2" {
  subnet_id      = aws_subnet.pjhsubnet2.id
  route_table_id = aws_route_table.pjhrt.id
}

resource "aws_route" "pjhdefaultroute" {
  route_table_id         = aws_route_table.pjhrt.id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = aws_internet_gateway.pjhigw.id
}

output "aws_vpc_id" {
  value = aws_vpc.pjhvpc.id
}
# 배포
terraform plan && terraform apply -auto-approve
terraform state list
data.aws_availability_zones.available
aws_internet_gateway.pjhigw
aws_route.pjhdefaultroute
aws_route_table.pjhrt
aws_route_table_association.pjhrtassociation1
aws_route_table_association.pjhrtassociation2
aws_subnet.pjhsubnet1
aws_subnet.pjhsubnet2
aws_vpc.pjhvpc

terraform state show aws_route.pjhdefaultroute

# graph 확인 > graph.dot 파일 선택 후 오른쪽 상단 DOT 클릭
terraform graph > graph.dot

# 라우팅 테이블 확인
#aws ec2 describe-route-tables --filters 'Name=tag:Name,Values=t101-rt' --query 'RouteTables[].Associations[].SubnetId'
aws ec2 describe-route-tables --filters 'Name=tag:Name,Values=t101-rt' --output table

B. 보안그룹/EC2 배포 : sg.tf, ec2.tf

  • sg.tf 파일 생성 : 보안그룹 생성
resource "aws_security_group" "pjhsg" {
  vpc_id      = aws_vpc.pjhvpc.id
  name        = "T101 SG"
  description = "T101 Study SG"
}

resource "aws_security_group_rule" "pjhsginbound" {
  type              = "ingress"
  from_port         = 80
  to_port           = 80
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.pjhsg.id
}

resource "aws_security_group_rule" "pjhsgoutbound" {
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.pjhsg.id
}
  • 보안그룹 배포
# 배포
terraform plan && terraform apply -auto-approve
terraform state list
....
aws_security_group.pjhsg
aws_security_group_rule.pjhsginbound
aws_security_group_rule.pjhsgoutbound
....

terraform state show aws_security_group.pjhsg
terraform state show aws_security_group_rule.pjhsginbound

# graph 확인 > graph.dot 파일 선택 후 오른쪽 상단 DOT 클릭
terraform graph > graph.dot
  • ec2.tf 파일 생성 : EC2 생성
data "aws_ami" "pjh_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" "pjhec2" {

  depends_on = [
    aws_internet_gateway.pjhigw
  ]

  ami                         = data.aws_ami.pjh_amazonlinux2.id
  associate_public_ip_address = true
  instance_type               = "t2.micro"
  vpc_security_group_ids      = ["${aws_security_group.pjhsg.id}"]
  subnet_id                   = aws_subnet.pjhsubnet1.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>" > index.html
              nohup ./busybox httpd -f -p 80 &
              EOF

  user_data_replace_on_change = true

  tags = {
    Name = "t101-pjhec2"
  }
}

output "pjhec2_public_ip" {
  value       = aws_instance.pjhec2.public_ip
  description = "The public IP of the Instance"
}
  • 배포 실행 후 EC2 확인
# 
terraform plan && terraform apply -auto-approve
terraform state list
data.aws_ami.pjh_amazonlinux2
aws_instance.pjhec2
....

terraform state show data.aws_ami.pjh_amazonlinux2
terraform state show aws_instance.pjhec2

# 데이터소스 값 확인
terraform console
> 
data.aws_ami.pjh_amazonlinux2
"ami-01c81850a6167bb81"
data.aws_ami.my_amazonlinux2.image_id
data.aws_ami.my_amazonlinux2.name
data.aws_ami.my_amazonlinux2.owners
data.aws_ami.my_amazonlinux2.platform_details
data.aws_ami.my_amazonlinux2.hypervisor
data.aws_ami.my_amazonlinux2.architecture
exit

# graph 확인 > graph.dot 파일 선택 후 오른쪽 상단 DOT 클릭
terraform graph > graph.dot

# 출력된 EC2 퍼블릭IP로 cul 접속 확인
terraform output -raw pjhec2_public_ip
54.180.139.163

MYIP=$(terraform output -raw pjhec2_public_ip)
while true; do curl --connect-timeout 1  http://$MYIP/ ; echo "------------------------------"; date; sleep 1; done
------------------------------
Fri Jul 14 17:38:35 KST 2023
<h1>RegionAz(apne2-az1) : Instance ID(i-02d3aaa5054f94c02) : Private IP(10.10.1.250) : Web Server</h1>
------------------------------

# 삭제
terraform destroy -auto-approve

[도전과제3] 입력변수를 활용해서 리소스(어떤 리소스든지 상관없음)를 배포

# 폴더 생성
mkdir s3 && cd s3
  • var.tf
variable "region" {
  description = "nomad server region"
  default     = "ap-northeast-2"
}
variable "nickname" {
  description = "my nick name"
  default     = "pjh"
}
  • s3.tf
provider "aws" {
  region = var.region
}

resource "aws_s3_bucket" "pjhs3bucket" {
  bucket = "${var.nickname}-t101study-tfstate"
}

resource "aws_s3_bucket_versioning" "pjhs3bucket_versioning" {
  bucket = aws_s3_bucket.pjhs3bucket.id
  versioning_configuration {
    status = "Enabled"
  }
}
  • 배포
# init
terraform init

# 배포
terraform plan && terraform apply -auto-approve

# 확인
terraform state list
aws_s3_bucket.pjhs3bucket
aws_s3_bucket_versioning.pjhs3bucket_versioning
  • 삭제
terraform destroy -auto-approve

[도전과제4] local를 활용해서 리소스(어떤 리소스든지 상관없음)를 배포

# 디렉터리 생성
mkdir psql && cd psql
  • psql.tf
#local 변수로 각 RDS 설정 값 정의
locals {
  instance_class = "db.t3.micro"
  rds_instances = {
    "pjh-postgres" = {
      identifier           = "pjh-postgres-terraform"
      db_name              = "pjhpostgres"
      engine               = "postgres"
      engine_version       = "13.10"
      parameter_group_name = "postgres13"
      port                 = 5432
      username             = "postgresadmin"
    }
  }
}

# Random password 생성
resource "random_password" "rds" {
  length           = 16
  special          = true
}

# parameter group 생성
resource "aws_db_parameter_group" "postgres_parameter_group" {
  name        = "postgres13"
  family      = "postgres13"
  description = "Parameter group for Postgres 13"
}

resource "aws_db_instance" "pjh_rds" {
  # local에 정의된 값을 활용하여 rds 반복적으로 생성
  for_each               = local.rds_instances
  identifier             = each.value.identifier
  db_name                = each.value.db_name
  engine                 = each.value.engine
  engine_version         = each.value.engine_version
  instance_class         = local.instance_class
  allocated_storage      = 20
  username               = each.value.username
  password               = random_password.rds.result
  parameter_group_name   = each.value.parameter_group_name
  skip_final_snapshot    = true
  vpc_security_group_ids = []
}
  • 배포(5분정도 소요됨)
# init
terraform init

# 배포
terraform plan && terraform apply -auto-approve

# 확인
terraform state list
aws_db_instance.pjh_rds["pjh-postgres"]
aws_db_parameter_group.postgres_parameter_group
random_password.rds
  • 삭제
terraform destroy -auto-approve

 

728x90
반응형

'Terraform' 카테고리의 다른 글

3주차 - 기본 사용 3/3 & 프로바이더  (0) 2023.07.21
1주차 - 테라폼 기본 사용 1/2  (0) 2023.07.05
[CHAPTER 2] ASG/ELB  (0) 2023.06.01
[CHAPTER 1&2] 기초  (0) 2023.05.23
Comments