5주차 - Terraform의 반복문과 조건문 (2) - 조건문

이 내용은 CloudNet@ 에서 진행하는 테라폼 기초 입문 스터디에 대한 연재글입니다.

스터디에서 사용하는 교재는 Terraform Up & Running 2nd Edition 입니다.


Prerequisites

아래에서 5주차 스터디 내용을 공유합니다.

교재의 5장 내용을 다루고 있습니다.

본문

테라폼을 통해 마치 프로그램을 작성하듯 코드를 작성할 수 있습니다. 이번 챕터에서는 아래의 내용을 학습할 예정입니다:

조건문

테라폼이 제공하는 조건문은 아래와 같이 구성되어 있습니다.

count 매개변수

앞서 살펴보았듯, count 매개변수를 사용하면 반복문을 사용할 수 있습니다. 이를 응용하여 조건문 작업 또한 수행할 수 있습니다.

count 매개변수를 사용한 if 구문

일부 사용자들에게는 모듈을 생성하고, 나머지 사용자에게는 생성해주지 않기 위해선 분기 처리를 수행합니다.

이를 위해서는 Boolean 입력 변수를 변수값으로 추가해주고, 아래와같은 조건 표현식(conditional expression)을 추가합니다.

variable "enable_autoscaling" {
  description = "If set to true, enable auto scaling"
  type        = bool
}

<CONDITION> ? <TRUE_VAL> : <FALSE_VAL>

상기 조건을 조합하면 아래값처럼 webserver-cluster 모듈을 업데이트 할 수 있지요.

resource "aws_autoscaling_schedule" "scale_out_during_business_hours" {
  # enable_autoscaling 값이 참/거짓일 때에 따라 auto scaling을 허용/불허할 수 있습니다.
  count = var.enable_autoscaling ? 1 : 0

  scheduled_action_name  = "${var.cluster_name}-scale-out-during-business-hours"
  min_size               = 2
  max_size               = 10
  desired_capacity       = 10
  recurrence             = "0 9 * * *" # 이 값은 cron 양식을 따릅니다!
  autoscaling_group_name = aws_autoscaling_group.example.name
}

resource "aws_autoscaling_schedule" "scale_in_at_night" {
  count = var.enable_autoscaling ? 1 : 0

  scheduled_action_name  = "${var.cluster_name}-scale-in-at-night"
  min_size               = 2
  max_size               = 10
  desired_capacity       = 2
  recurrence             = "0 17 * * *"
  autoscaling_group_name = aws_autoscaling_group.example.name
}

이러한 조건을 staging 서버에서는 false로, production 서버에서는 true 로 두기 위해선 아래와 같이 사용하면 됩니다.

module "webserver_cluster" {
  source = "../../../../modules/services/webserver-cluster"

  cluster_name           = "webservers-stage"
  db_remote_state_bucket = "(YOUR_BUCKET_NAME)"
  db_remote_state_key    = "stage/data-stores/mysql/terraform.tfstate"

  instance_type        = "t2.micro"
  min_size             = 2
  max_size             = 2
  # boolean 타입이므로, 해당 값을 지정해서, 설정이 구동되게 지정해줄 수 있습니다.
  enable_autoscaling   = false
}
module "webserver_cluster" {
  source = "../../../../modules/services/webserver-cluster"

  cluster_name           = "webservers-prod"
  db_remote_state_bucket = "(YOUR_BUCKET_NAME)"
  db_remote_state_key    = "prod/data-stores/mysql/terraform.tfstate"

  instance_type        = "m4.large"
  min_size             = 2
  max_size             = 10
  # boolean 타입이므로, 해당 값을 지정해서, 설정이 구동되게 지정해줄 수 있습니다.
  enable_autoscaling   = true

  custom_tags = {
    Owner     = "team-foo"
    ManagedBy = "terraform"
  }
}

count 매개변수를 사용한 if-else 구문

특정 IAM 사용자에게 CloudWatch에 대한 액세스 권한을 부여하는 예시로 살펴보겠습니다. 이 파일은 별도의 정책관련 테라폼 파일로 만들면 되겠지요. (s3ich4n이란 유저에 대해 권한을 부여해보죠)

resource "aws_iam_policy" "cloudwatch_read_only" {
  name   = "cloudwatch-read-only"
  policy = data.aws_iam_policy_document.cloudwatch_read_only.json
}

data "aws_iam_policy_document" "cloudwatch_read_only" {
  statement {
    effect    = "Allow"
    # cloudwatch의 일부 기능들(read 파트)에 대해서만 권한을 허용하였습니다.
    actions   = [
      "cloudwatch:Describe*",
      "cloudwatch:Get*",
      "cloudwatch:List*"
    ]
    resources = ["*"]
  }
}
resource "aws_iam_policy" "cloudwatch_full_access" {
  name   = "cloudwatch-full-access"
  policy = data.aws_iam_policy_document.cloudwatch_full_access.json
}

data "aws_iam_policy_document" "cloudwatch_full_access" {
  statement {
    effect    = "Allow"
    # cloudwatch의 전체 기능에 대해 권한을 허용하였습니다.
    actions   = ["cloudwatch:*"]
    resources = ["*"]
  }
}

give_s3ich4n_cloudwatch_full_access 이라는 변수값에 기반하여, 리소스를 어떻게 적용할지 살펴봅시다.

variable "give_s3ich4n_cloudwatch_full_access" {
  description = "If true, s3ich4n gets full access to CloudWatch"
  type        = bool
}

리소스 생성 시, 동작수행을 위해 count 매개변수와 조건 표현식을 모두 사용해봅시다.

resource "aws_iam_user_policy_attachment" "neo_cloudwatch_full_access" {
  count = var.give_neo_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" "neo_cloudwatch_read_only" {
  count = var.give_neo_cloudwatch_full_access ? 0 : 1

  user       = aws_iam_user.example[0].name
  policy_arn = aws_iam_policy.cloudwatch_read_only.arn
}

for_eachfor 표현식

for_each 표현식을 빈 컬렉션으로 전달하면, 0개의 리소스 또는 0개의 인라인 블록을 생성합니다. 비어있지 않은 컬렉션을 전달하면 하나 이상의 리소스 또는 인라인 블록을 만듭니다.

이를 표현하기 위해서는 조건부로 어떻게 표현할까요? for_each 표현식과 for 표현식을 결합하여 사용할 수 있습니다. 예시값을 살펴보시지요.

dynamic "tag" {
    for_each = {
    	for key, value in var.custom_tags:
    	key => upper(value)
    	if key != "Name"
    }

    content {
    	key                 = tag.key
    	value               = tag.value
    	propagate_at_launch = true
    }
}

if 문자열 지시자

if 문자열 지시자를 살펴봅시다.

# if 구문의 사용방법 입니다.
%{if <CONDITION> }<TRUEVAL>%{endif}

# if-else 구문의 사용방법입니다.
%{ for <INDEX>, <ITEM> in <COLLECTION> }<BODY>%{if <EXPRESSION>}%{ else }<FALSEVAL>%{ endif }%{ endfor }

예시를 보면서 함께 살펴봅시다.

variable "names" {
  description = "A list of names"
  type        = list(string)
  default     = ["alice", "bob", "charlie"]
}

# if 구문을 사용하는 방법입니다.
output "for_directive_index_if" {
  value = <<EOF
%{ for i, name in var.names }
  ${name}%{ if i < length(var.names) - 1 }, %{ endif }
%{ endfor }
EOF
}

# 마지막 `,` 문자에 대해 strip을 할 수도 있습니다.
output "for_directive_index_if_strip" {
  value = <<EOF
%{~ for i, name in var.names ~}
${name}%{ if i < length(var.names) - 1 }, %{ endif }
%{~ endfor ~}
EOF
}

# 마지막 `,` 문자에 대해 strip을 할 수도 있습니다.
output "for_directive_index_if_else_strip" {
  value = <<EOF
%{~ for i, name in var.names ~}
${name}%{ if i < length(var.names) - 1 }, %{ else }.%{ endif }
%{~ endfor ~}
EOF
}

terraform init && terraform apply를 수행하면 아래와 같은 결과값을 보실 수 있습니다.

$ terraform init && terraform apply

(생략)

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

Outputs:

for_directive_index_if = <<EOT

  alice,

  bob,

  charlie


EOT
for_directive_index_if_else_strip = "alice, bob, charlie."
for_directive_index_if_strip = "alice, bob, charlie"

Lessons Learned

제 5장에서는 아래의 내용을 반드시 기억하셨으면 좋겠습니다.

  1. (중요!) Prerequisite를 반드시 읽어주세요. 테라폼의 타입과 값에 대한 내용은 알고있어야 앞으로의 진행이 수월할 것입니다!

    1. 프로그래밍 언어처럼 다루려면, 어떤 타입과 값을 사용할 수 있는지는 기본적으로 알아야 하기 때문입니다.
  2. 조건문의 사용방법과 주의사항에 대해 배웠습니다.

이것으로 제 5장, 조건문 설명을 마칩니다. 긴 글 읽어주셔서 감사합니다.