Terraform Type Constraints: безпечний код для AWS

Якщо ви працюєте з Terraform для розгортання інфраструктури AWS, важливо, щоб ваш код був надійним, зрозумілим і простим у підтримці. Одним із ключових інструментів для цього є type constraints — обмеження типів змінних. 

У цьому гайді ми розберемося, що навіщо це, які типи варто застосовувати для різних сценаріїв в AWS та навіщо використовувати валідацію змінних.

Для чого потрібні type constraints?

Type constraints дозволяють Terraform перевіряти, що змінна отримує правильний тип даних. Якщо тип не вказано, легко передати неправильне значення, що призведе до помилок при розгортанні або непередбачуваних проблем у конфігурації.

Правильне використання типів дає:

  • перевірку очікувань: ви точно знаєте, що передаєте;
  • консистентний код: стандартизовані змінні у всіх модулях;
  • самодокументований код: новачки швидко розуміють структуру та призначення змінних.

Типи змінних у Terraform

Terraform має чітко визначену систему типів, яка допомагає описувати змінні зрозуміло та безпечно. Тип змінної визначає, які саме дані вона може приймати і в якій формі.

У Terraform існують такі групи типів:

  1. Primitive types — прості значення;
  2. Collection types — набори значень;
  3. Structural types — складні структури з фіксованою формою;
  4. Any type — універсальний тип без обмежень.

Розглянемо кожну групу окремо та пояснимо, де вона використовується в AWS.

Primitive types

Застосовуються для одиничних значень. Це базові типи, з яких починається більшість Terraform-конфігурацій.

string

Використовується для текстових значень: назв ресурсів, регіонів, ARN, назв середовищ.

Типові AWS-сценарії:

  • назва S3-бакета;
  • регіон розгортання;
  • ARN IAM-ролі.
variable "aws_region" {
  type        = string
  description = "AWS region for deployment"
}

number

Призначений для числових значень: лімітів, портів, розмірів, таймаутів.

Типові AWS-сценарії:

  • кількість інстансів в Auto Scaling Group;
  • розмір RDS-сховища;
  • порти для ALB або Security Group.
variable "max_instance_count" {
  type        = number
  description = "Maximum number of EC2 instances"
  default     = 5
}

bool

Логічний тип, який приймає true або false. Зручний для керування поведінкою ресурсів без зміни коду.

Типові AWS-сценарії:

  • увімкнення Multi-AZ для RDS;
  • створення NAT Gateway;
  • присвоєння публічної IP-адреси.
variable "enable_public_ip" {
  type        = bool
  description = "Assign public IP to EC2 instances"
  default     = false
}

Collection types

Використовуються, коли потрібно працювати з кількома значеннями одного типу.

list(T)

Впорядкований список, де порядок має значення.

Типові AWS-сценарії:

  • список зон доступності;
  • перелік підмереж;
  • порти у визначеній послідовності.
variable "availability_zones" {
  type        = list(string)
  description = "Availability zones in priority order"
}

set(T)

Набір унікальних значень, де порядок не важливий, а дублікати автоматично прибираються.

Типові AWS-сценарії:

  • CIDR-блоки;
  • IAM-дозволи;
  • списки доступів.
variable "allowed_cidrs" {
  type        = set(string)
  description = "CIDR blocks allowed to access the service"
}

map(T)

Колекція у форматі ключ → значення, де ключ завжди є рядком.

Типові AWS-сценарії:

  • теги ресурсів;
  • налаштування для різних середовищ;
  • відповідність імені до ARN.
variable "common_tags" {
  type = map(string)
  description = "Tags applied to all AWS resources"
}

Structural types

Застосовуються для складних конфігурацій, де важлива форма даних.

object({ … })

Описує структуру з іменованими полями та фіксованими типами.

Типові AWS-сценарії:

  • конфігурація RDS;
  • налаштування ECS Task;
  • опис S3-бакета.
variable "rds_config" {
  type = object({
    engine         = string
    engine_version = string
    instance_class = string
    storage_gb     = number
    multi_az       = bool
  })
}

tuple([ … ])

Фіксований набір значень, де важливий порядок і кількість елементів.

Типові AWS-сценарії:

  • мінімальне та максимальне значення;
  • жорстко задані набори параметрів.
variable "asg_capacity_bounds" {
  type        = tuple([number, number])
  description = "Min and max capacity for ASG"
}

Any type

Тип any означає, що Terraform не накладає обмежень на структуру даних. Його варто використовувати лише тоді, коли:

  • форма даних заздалегідь невідома;
  • змінна передається у підмодуль;
  • потрібна максимальна гнучкість.
variable "extra_user_data_context" {
  type        = any
  description = "Additional data passed to user_data templates"
}

Важливо: any вимикає перевірку типів, тому зменшує надійність конфігурації. У production-середовищах його використовують обережно.

Валідація змінних у Terraform

Type constraints визначають форму даних, але перевіряють їх зміст лише частково.
Наприклад, Terraform знає, що змінна — це string, але не знає, яке саме значення є допустимим.

Для цього в Terraform існує validation — вбудований механізм перевірки значень змінних.

Навіщо потрібна валідація?

Вона допомагає ловити помилки, забороняти небезпечні або некоректні значення та чітко пояснювати, чому конфігурація не проходить перевірку.

Це особливо важливо в AWS. Бо неправильне значення може створити ресурс у неправильному регіоні, відкрити доступ не тим CIDR чи навіть порушити політики безпеки або білінгу. 

Тому читайте далі.

Приклад простої валідації

Перевірка, що середовище має одне з дозволених значень:

variable "environment" {
  type = string

  validation {
    condition     = contains(["dev", "stage", "prod"], var.environment)
    error_message = "Environment must be dev, stage or prod."
  }
}

Що тут відбувається?

  1. Тип string гарантує, що це текст.
  2. Validation перевіряє конкретне значення.
  3. Terraform зупиняється зі зрозумілою помилкою, якщо умова не виконана.

Валідація числових значень

Приклад для Auto Scaling Group:

variable "max_instance_count" {
  type = number

  validation {
    condition     = var.max_instance_count >= 1 && var.max_instance_count <= 20
    error_message = "Max instance count must be between 1 and 20."
  }
}

Валідація складних структур

Validation працює і з object, і з map, і з list.

variable "rds_config" {
  type = object({
    engine         = string
    engine_version = string
    instance_class = string
    storage_gb     = number
    multi_az       = bool
  })

  validation {
    condition     = var.rds_config.storage_gb >= 20
    error_message = "RDS storage must be at least 20 GB."
  }
}

Тут Terraform:

  1. перевіряє структуру через object;
  2. додатково контролює бізнес-обмеження.

Післяслово

Чітко задані типи допомагають швидше знаходити проблеми та роблять код зрозумілим не лише для вас, а й для всієї команди.

Якщо ви тільки починаєте працювати з Terraform, варто закласти цю звичку одразу: обирати типи свідомо, не зловживати any і структурувати змінні так, щоб їх було легко розширювати. 

Для додаткової впевненості у своїй роботі рекомендуємо відвідати наш курс Full DevOps, де Terraform є одним із компонентів авторської програми. Переглядайте деталі та реєструйтесь!

Залишити відповідь

Дякуємо, що поділились