Terraform HCL 기초 문법

GreenBean·2023년 3월 2일
0
post-thumbnail

Terraform HCL 기초 문법

이전 작성 글: Terraform 이란?

Terraform Language Documentation

HCL 문법 형태

# 기본 문법

<BLOCK TYPE> "<BLOCK LABEL>" "<BLOCK LABEL>" {
  # Block body
  <IDENTIFIER> = <EXPRESSIONS> # Argument
}
# 문법 예시

resource "aws_vpc" "main" {
  cidr_block = var.base_cidr_block
}
  • Block Type 에 따라서 Label 이 몇 개 올지 결정됨
    • 0개 또는 1개 이상
  • Block 안에는 IdentifierExpressionkey-value 형식으로 들어갈 수 있게 됨
    • Block 의 종류는 terraformproviderresourcedatamodulelocalvariableoutput

Arguments 와 Block

  • BlockLabel 을 작성할 때 그 Label 의 값을 Identifiers 라고 하는데 제약사항이 존재
    • Identifiers 에 들어올 수 있는 값은 문자열 ∙ 숫자 ∙ _- 이렇게 4가지
    • 하지만 처음에 숫자가 들어올 수 없음

Tip! HCL 에서 지향하는 Style Convention
Terraform Configuration Syntax

  • Terraform 은 명령어를 통해 자동으로 포멧팅 가능
    • 명령어: terraform fmt -diff
  • Indent 는 tab 이 아닌 two space
  • Block 내에 여러 arguments 를 정의할 때는 = 을 기준으로 key와 value를 정의
  • Resource 를 정의할 때는 meta-argument 들이 있는데 count ∙ for each 는 머리 부분에 위치하는 것을 선호하고 lifecycle 이나 depends on 은 하단부에 위치하는 것을 선호

Terraform 파일 구조

  • 디렉토리 내에 .tf 파일을 작성하게 되는데 tf 파일은 HCL 문법을 따르지만 .json 확장자를 이용하여 json 으로도 이용 가능
  • Terraform 으로 initplanapply 명령을 하게 되면 현재 사용자의 디렉토리 상에서 .tf 파일과 .tf.json 파일을 찾게 되는데 하위 디렉토리에 대해서는 탐색을 하지 않음
  • tf 파일을 작성할 때는 인코딩을 항상 UTF-8 로 설정해야 함
  • tf 파일을 가지고 있는 디렉토리를 Module 이라고 부름
    • Module 은 또한 Root Module 과 Child Module 로 나뉨
# 구조 예시

|-- terraform
|   |-- a
|   |-- b
|   |-- c
  • 위의 예시처럼 terraform 디렉토리에 abc 모듈 디렉토리가 있고 c 모듈이 ab 모듈을 가져다 쓴다고 했을 때 c 위치에서 terraform apply 를 수행하면 c 라는 워크스페이스는 Root Module 이 되고 c 가 사용하는 ab 모듈은 Child Module 이 됨
    • c 에서 terraform apply 하는 것이 아니라 a 에서 terraform apply 를 한다면 a 가 Root Modul 이 됨

Terraform 반복문

  • Terraform 코드를 사용하다보면 반복되는 리소스 생성 및 리소스에 대한 관리가 필요할 때가 있음
    • 적은 양의 코드면 쉽게 작성이 가능 하겠지만 몇 십 개의 리소스를 관리하게 되면 각각의 리소스 생성 코드를 만드는 것은 쉽지 않은 일
    • 이를 효율적으로 사용하기 위해서 Terraform 에선 반복문을 지원
# 예시

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

resource "aws_iam_user" "user_1" {
	name = "user-1"
}

resource "aws_iam_user" "user_2" {
	name = "user-2"
}

resource "aws_iam_user" "user_1" {
	name = "user-2"
}

output "user_arns" {
  value = [
    aws_iam_user.user_1.arn,
    aws_iam_user.user_2.arn,
    aws_iam_user.user_3.arn,
  ]
}

count 표현식 사용

  • meta-arguments 는 Resource Block Body 의 최상단 count
    • count.index 를 사용하여 인덱스 정보를 가져 올 수 있음
    • resourcemoduledata 에서 사용 가능
# count 적용

resource "aws_iam_user" "count" {
  count = 3 # meta-argument
  
  name = "count-user-${count.index}"
}

output "count_user_arns" {
  value = aws_iam_user.count.*.arn
}

set 사용

  • set 은 List 형식으로 받기 때문에 형 변환이 필요하며 toset 을 사용
    • 값을 처리 하기 위해 eachkeyeach.value 로 데이터에 접근 할 수 있음
# set 적용

resource "aws_iam_user" "for_each_set" {
  for_each = toset([
    "for-each-set-user-1",
    "for-each-set-user-2",
    "for-each-set-user-3",
  ])
  
  name = each.key
}

output "for_each_set_user_arns" {
  value = values(aws_iam_user.for_each_set).*.arn
}

map 사용

  • 정상적으로 생성 되면 each.keyeach.value 값이 생성되어서 활용 가능
# map 적용

resource "aws_iam_user" "for_each_map" {
  for_each = {
    cab = {
      level = "low"
      manager = "man1"
    }
    bob = {
      level = "mid"
      manager = "man2"
    }
    louis = {
      level = "high"
      manager = "man2"
    }
  }

  name = each.key
  tags = each.value
}

for 표현식 사용

  • List 를 갖는 코드에서 특정 업무를 수행하기 위해서 Terraform 은 for 표현식 을 제공
    • countfor-eachdataresourcemodule 에서만 사용 가능한 표현식
    • for 표현식 은 Terraform 에서 Expression 을 사용 가능 한 모든 곳에서 사용 가능한 문법
# 기본 문법

[for <ITEM> in <LIST> : <OUTPUT>]

[for k, v in var.map : length(k) + length(v)]
  • List : 반복할 리스트
  • Item : list 의 각 항목에 할당할 변수의 이름
  • Output : item 을 어떤 식으로든 변환하는 표현식
  • map 데이터 타입의 key-value 값을 활용해서 사용 가능
# 문법 예시

variable "names" {
  description = "A list of names"
  type        = list(string)
  default     = ["gasida", "akbun", "fullmoon"]
}

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

Terraform 조건문

  • 다른 프로그래밍에서 제공하는 3항 연산자 사용 문법에 대해서 Terraform 에서도 동일 문법으로 제공
    • 3항 연산자를 활용해서 원하는 조건이 true 일 때와 false 를 구분해서 리소스 배포 및 프로세스에 대해서 정의 할 수 있음
# 기본 문법

condition ? true_val : false_val
  • condition 이 참이면 true_val 을 거짓이면 fals_val 을 할당하는게 가능
# 문법 예시

resource "aws_iam_user_policy_attachment" "gurumee_cloudwatch_full_access" {
  count      = var.give_gurumee_cloudwatch_full_access ? 1 : 0
  user       = aws_iam_user.example.0.name
  policy_arn = aws_iam_policy.cloudwatch_full_access.arn
}

resource "aws_iam_user_policy_attachment" "gurumee_cloudwatch_read_only" {
  count      = var.give_gurumee_cloudwatch_full_access ? 0 : 1
  user       = aws_iam_user.example.0.name
  policy_arn = aws_iam_policy.cloudwatch_read_only.arn
}
  • give_gurumee_cloudwatch_full_access 변수 값에 따라서 액세스 권한을 부여하는 코드

if 문자열 지시자

  • 3한 연산자 표현식과 함께 if 표현식도 제공
    • 조건에 해당 할 경우 정해진 코드가 실행이 되고 그 조건에 해당하지 않을 경우 종료가 실행하지 않고 지나가거나 다른 프로세스가 실행 되게 표현 할 수 있음
# 기본 문법

%{ if <CONDITION> }<TRUEVAL>%{ else }<FALSEVAL>%{ endif }
  • FALSEVAL 은 CONDITION 이 false 로 평가되면 렌더링할 표현식
# 문법 예시

variable "names" {
  description = "Names to render"
  type        = list(string)
  default     = ["gasida", "akbun", "fullmoon"]
}

output "for_directive" {
  value = "%{ for name in var.names }\${name}, %{ endfor }"
}

output "for_directive_index" {
  value = "%{ for i, name in var.names }(\${i}) \${name}, %{ endfor }"
}

output "for_directive_index_if" {
  value = <<EOF
%{ for i, name in var.names }
  \${name}%{ if i < length(var.names) - 1 }, %{ endif }
%{ endfor }
EOF
}

Outputs:

for_directive = "gasida, akbun, fullmoon, "
for_directive_index = "(0) gasida, (1) akbun, (2) fullmoon, "
for_directive_index_if = <<EOT

  gasida,

  akbun,

  fullmoon

Tip! 추가 내용

Terraform Module

  • Terraform 모듈 내에 원하는 리소스들을 구성해놓으면 여러 다른 위치에서 다른 변수값을 사용해서 리소스를 생성할 수 있음
# 기본 문법

module "<NAME>" {
  source = "<MODULE_PATH>"
  cluster_name = "cluster-a"
}
  • 모듈은 다음과 같이 사용할 수 있음
    • 리소스를 구성해놓은 모듈 코드가 있는 PATH 를 설정
    • Module 이 필요로 하는 변수들을 적어주면 됨
# 문법 예시

resource "aws_security_group" "elb-sg" {
  name = "${var.cluster_name}-elb-sg"
  ingress {
    from_port   = 80
    to_port     = 80
    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"]
  }
}
  • 모듈이 생성하는 resource 는 보통의 리소스를 생성하는 코드와 유사하지만 모듈을 사용하는 코드에서 건네주는 변수들을 사용해서 리소스를 생성해야 함
    • var.cluster_name 과 같이 모듈을 사용하는 곳에서 설정한 변수를 받아와 리소스를 생성할 수 있음
# 문법 예시

variable "cluster_name" {
  default = "cluster_default"
}
  • 이러한 변수를 받아오기 위해서는 모듈에서 variable 을 설정해주어야 함
    • default 값을 설정할 수도 있고 비워둬도 됨
# 문법 예시

output "elb_sg_name" {
  value = aws_secruity_group.elb-sg.name 
}
  • 모듈에서 리소스를 생성한 후 생성한 리소스에 대한 정보 ( arn ∙ id 등 ) 를 받기 위해 모듈에서 output 을 통해 리소스 정보를 출력해주어야 함
    • 모듈을 사용하는 곳에서는 module.<MODULE_NAME>.elb_sg_name 으로 output 을 받아올 수 있음
profile
🌱 Backend-Dev | hwaya2828@gmail.com

0개의 댓글