본문 바로가기
공부/IaC

[Terraform] map 타입 변수 validation 설정하기

by haejang 2022. 2. 21.
728x90
728x90

 

아래와 같은 변수를 사용한다고 생각해보자

 

variable "subnet_cidrs" {
  description = "Subnet CIDRs"
  type        = map(string)
  
  default = {
    pub-bastion = "10.0.0.0/24"
    pri-db      = "10.0.1.0/24"
    pri-glue    = "10.0.2.0/24"
    pri-ecs     = "10.0.3.0/24"
  }
}

 

위 변수를 가지고 for_each를 돌리게 된다면,

pub-bastionpri-db 등의 이름들은 모두 each.key 로 분류되고, CIDR들은 모두 each.value로 분류된다

 

이 때, 각 key들의 이름이 pub 또는 pri로 시작하게 강제하고 싶으므로 validation 구문을 사용해보겠다

 

Terraform Validation


https://www.terraform.io/language/values/variables#custom-validation-rules

 

Input Variables - Configuration Language | Terraform by HashiCorp

Input variables allow you to customize modules without altering their source code. Learn how to declare, define, and reference variables in configurations.

www.terraform.io

 

예시 코드는 아래와 같다

# 가이드 제공 Code
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 && substr(var.image_id, 0, 4) == "ami-"
    error_message = "The image_id value must be a valid AMI id, starting with \"ami-\"."
  }
}

variable "image_id" {
  type        = string
  description = "The id of the machine image (AMI) to use for the server."

  validation {
    # regex(...) fails if it cannot find a match
    condition     = can(regex("^ami-", var.image_id))
    error_message = "The image_id value must be a valid AMI id, starting with \"ami-\"."
  }
}

 

variable blockvalidation block을 넣을 수 있으며, condition 조건에 맞지 않는 경우 error_message 의 문구를 내뱉는다

표현식이 실패했는지 아닌지로 condition 조건을 걸고 싶다면 can 함수를 사용할 수 있다

https://www.terraform.io/language/functions/can

 

can - Functions - Configuration Language | Terraform by HashiCorp

The can function tries to evaluate an expression given as an argument and indicates whether the evaluation succeeded.

www.terraform.io

 

+ can은 이 validation 구문에서만 사용되도록 권장되는 듯 하다. 다른 부분에서는 try 함수를 사용하도록 하자.

 

 


 

아무튼 예시는 string 형식의 변수를 검사하는 것인데, 나는 map 변수의 모든 값을 검사하고 싶다

-> 당연히...validation 구문에서 dynamic block을 사용하면 되지 않을까? 싶어서 아래 코드를 작성해봤다

 

variable "subnet_cidrs" {
  description = "Subnet CIDRs"
  type        = map(string)

  default = {
    pub-bastion = "10.0.0.0/24"
    pri-db      = "10.0.1.0/24"
    pri-glue    = "10.0.2.0/24"
    pri-ecs     = "10.0.3.0/24"
  }

  dynamic "validation" {
    for_each = var.subnet_cidrs
    content {
      condition     = contains(["pub", "pri"], substr(validation.key, 0, 3))
      error_message = "Subnet Name must start with pub or pri."
    }
  }
}

 

key의 앞 3글자를 따 왔을 때, pub or pri 인지를 확인할 수 있도록 했다

can은 오류가 있는지를 찾는 함수이기 때문에 여기선 필요가 없다

 

subnet_cidrs 변수엔 1이란 값이 없으므로 오류가 발생한다

즉 false는 오류가 아니기 때문에, can(false)는 true를 반환하므로, 위와 같이 contains 함수 등을 통해 true or false를 내뱉는 경우라면 can을 사용하면 안된다

+ 참고로 error_message. 또는 ? 로 끝나는 완전한 문장이지 않으면 오류가 난다

 

그러나,,,역시나 쉽게 될리가 없다

 

대체 왜,,,,,,하며 찾다가 아래 토론을 보게 되었다

 

https://github.com/hashicorp/terraform/issues/26205

 

Validate each list item in a Custom Validation Rule condition · Issue #26205 · hashicorp/terraform

Current Terraform Version Terraform v0.13.1 Use-cases With the release of Terraform v0.13.1 we started using a lot Custom Validation Rules to check whether an input value for a variable has the exp...

github.com

 

 

음,,,암튼 이유가 있어서 validation에서 dynamic을 못쓰게 해둔 것 같다

그래도 for 표현식 사용해서 모두 맞는 표현인지를 검사할 수 있다는 솔루션을 얻었으므로 다시 시도해 보았다

 

우선 for 표현식부터 제대로 사용해보자

참조 : https://www.terraform.io/language/expressions/for

참조 (홍랩) : https://honglab.tistory.com/216

 

 

key값(인덱스)를 반환하기 위해선 한 쌍의 임시 기호를 써줘야 하더라

아무튼 이제 인덱스를 반환할 수 있게 되었으니, 인덱스를 검사하기만 하면 끝이다

 

 

> [for k in var.subnet_cidrs : k]
[
  "10.0.1.0/24",
  "10.0.3.0/24",
  "10.0.2.0/24",
  "10.0.0.0/24",
]
> [for k,s in var.subnet_cidrs : k]
[
  "pri-db",
  "pri-ecs",
  "pri-glue",
  "pub-bastion",
]


> [for k,s in var.subnet_cidrs : substr(k,0,3)]
[
  "pri",
  "pri",
  "pri",
  "pub",
]
> [for k,s in var.subnet_cidrs : contains(["pub","pri"],substr(k,0,3))]
[
  true,
  true,
  true,
  true,
]
> alltrue([for k,s in var.subnet_cidrs : contains(["pub","pri"],substr(k,0,3))])
true

 

참고로 위와 같이 테라폼 식 확인을 위한 대화형 cli는 terraform console 명령으로 사용할 수 있다

 

아무튼 alltrue 함수까지 사용해서 모든 인덱스 값이 pub or pri 로 시작하는지를 검사할 수 있게 되었으니, 실제 validation 구문에 적용해보자

 

variable "subnet_cidrs" {
  description = "Subnet CIDRs"
  type        = map(string)

  default = {
    pub-bastion = "10.0.0.0/24"
    pri-db      = "10.0.1.0/24"
    pri-glue    = "10.0.2.0/24"
    pri-ecs     = "10.0.3.0/24"
  }

  validation {
    condition     = alltrue([for k,s in var.subnet_cidrs : contains(["pub", "pri"], substr(k, 0, 3))])
    error_message = "Subnet Name must start with pub or pri."
  }
}

 

굿

일부러 서브넷 이름 하나를 바꿔서 plan을 때려보았다

 

 

에러 메세지도 잘 나오는걸 확인할 수 있었다

 

 

 

 

 

 

728x90
728x90

댓글