Перейти к основному содержимому

7.04. Справочник по Terraform

Разработчику Архитектору Инженеру

Справочник по Terraform

Общие сведения

Terraform — инструмент для декларативного управления инфраструктурой как кодом (Infrastructure as Code, IaC). Он позволяет описывать целевую инфраструктуру в текстовых файлах, применять изменения безопасно и воспроизводимо, а также отслеживать состояние ресурсов через файл состояния (state file).

Terraform использует язык конфигурации HCL (HashiCorp Configuration Language), который поддерживает читаемый человеком синтаксис и мощные конструкции программирования.


Структура конфигурационного файла Terraform

Файлы Terraform имеют расширение .tf. Основной файл обычно называется main.tf, но допустимо разделение логики по нескольким файлам:

  • main.tf — основная логика развертывания
  • variables.tf — объявление входных переменных
  • outputs.tf — определение выходных значений
  • versions.tf — указание версий провайдеров и самого Terraform
  • locals.tf — локальные значения
  • backend.tf — конфигурация бэкенда состояния

Все файлы в одной директории рассматриваются как единый модуль.


Блоки конфигурации

terraform

Блок terraform задает настройки самого Terraform и его взаимодействия с внешними системами.

Пример:

terraform {
required_version = ">= 1.5.0"
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-west-2"
}
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}

Поддерживаемые атрибуты:

  • required_version — минимальная или точная версия Terraform, необходимая для выполнения конфигурации.
  • backend — определяет место хранения файла состояния (local, s3, azurerm, gcs и другие).
  • required_providers — явное указание провайдеров с их источниками и версиями.
  • experiments — включение экспериментальных функций (например, module_variable_optional_attrs).

provider

Блок provider настраивает конкретный облачный или API-провайдер.

Пример:

provider "aws" {
region = "us-east-1"
profile = "default"
}

Общие параметры (зависят от провайдера):

  • region, zone, project, subscription_id — географические или проектные идентификаторы.
  • credentials, access_key, secret_key — данные аутентификации.
  • alias — позволяет использовать несколько экземпляров одного провайдера.

Провайдеры загружаются автоматически при первом запуске terraform init.


resource

Блок resource описывает управляемый объект инфраструктуры: виртуальную машину, базу данных, сеть и так далее.

Синтаксис:

resource "тип_ресурса" "локальное_имя" {
атрибут1 = значение1
атрибут2 = значение2
}

Пример:

resource "aws_instance" "web" {
ami = "ami-0c02fb55956c7d316"
instance_type = "t3.micro"
tags = {
Name = "WebServer"
}
}

Особенности:

  • Тип ресурса — комбинация имени провайдера и типа (например, aws_instance, google_compute_instance).
  • Локальное имя — используется внутри модуля для ссылок.
  • Атрибуты зависят от типа ресурса и документируются в спецификации провайдера.
  • Ресурсы могут содержать вложенные блоки (например, ebs_block_device внутри aws_instance).

data

Блок data извлекает информацию о существующих объектах инфраструктуры без их изменения.

Пример:

data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical

filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}

Результат доступен как data.aws_ami.ubuntu.id.


variable

Блок variable объявляет входной параметр модуля.

Пример:

variable "region" {
description = "AWS регион для развертывания"
type = string
default = "us-east-1"
}

Поддерживаемые атрибуты:

  • description — пояснение назначения переменной.
  • type — тип данных: string, number, bool, list(...), map(...), object({...}), tuple([...]).
  • default — значение по умолчанию.
  • validation — пользовательские правила проверки (начиная с Terraform 0.13).

Пример валидации:

variable "instance_type" {
type = string
validation {
condition = contains(["t3.micro", "t3.small", "t3.medium"], var.instance_type)
error_message = "Допустимые типы: t3.micro, t3.small, t3.medium."
}
}

output

Блок output определяет значения, которые будут показаны после применения конфигурации.

Пример:

output "public_ip" {
value = aws_instance.web.public_ip
description = "Публичный IP-адрес веб-сервера"
sensitive = false
}

Атрибуты:

  • value — выражение или ссылка на ресурс/переменную.
  • description — пояснение.
  • sensitive — скрывает значение в выводе при true.

locals

Блок locals определяет локальные значения, вычисляемые один раз и переиспользуемые в конфигурации.

Пример:

locals {
common_tags = {
Environment = "production"
Owner = "team-infra"
}
instance_name = "web-${var.environment}"
}

Используется как local.common_tags или local.instance_name.


module

Блок module вызывает другой модуль Terraform.

Пример:

module "vpc" {
source = "./modules/vpc"
cidr_block = "10.0.0.0/16"
public_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
}

Атрибуты:

  • source — путь к модулю: локальный (./path), Git (git::https://...), реестр Terraform (terraform-aws-modules/vpc/aws).
  • Параметры передаются как аргументы с именами, соответствующими variable в модуле.

Выражения и функции

Типы выражений

  • Литералы: "строка", 42, true, [1,2,3], {key = "value"}
  • Ссылки: var.name, local.value, aws_instance.web.id, module.vpc.vpc_id
  • Арифметика: +, -, *, /, %
  • Логика: &&, ||, !, ==, !=, <, <=, >, >=
  • Условные операторы: condition ? true_val : false_val
  • Циклы: for-выражения

Встроенные функции

Terraform предоставляет сотни встроенных функций. Ниже — наиболее часто используемые категории.

Строковые функции

  • upper("hello")"HELLO"
  • lower("HELLO")"hello"
  • replace("foo-bar", "-", "_")"foo_bar"
  • format("IP: %s", var.ip)"IP: 192.168.1.1"
  • join(",", ["a", "b", "c"])"a,b,c"
  • split(".", "192.168.1.1")["192", "168", "1", "1"]
  • substr("terraform", 0, 5)"terra"

Числовые функции

  • abs(-5)5
  • ceil(3.2)4
  • floor(3.9)3
  • log(100, 10)2
  • pow(2, 3)8

Коллекции и структуры

  • length([1,2,3])3
  • keys({a = 1, b = 2})["a", "b"]
  • values({a = 1, b = 2})[1, 2]
  • merge({a = 1}, {b = 2}){a = 1, b = 2}
  • lookup(map, key, default) — получение значения из map с fallback

Преобразования

  • jsonencode({...}) — сериализация в JSON
  • yamlencode({...}) — сериализация в YAML (начиная с Terraform 1.2)
  • tomlencode({...}) — сериализация в TOML (начиная с Terraform 1.3)
  • base64encode("text")"dGV4dA=="

Файловые операции

  • file("path.txt") — читает содержимое файла как строку
  • templatefile("template.tftpl", {vars}) — рендерит шаблон
  • fileexists("config.yaml")true/false

Дата и время

  • timestamp()"2026-02-24T12:34:56Z"
  • timeadd(timestamp(), "24h") — добавляет интервал
  • formatdate("YYYY-MM-DD", timestamp())"2026-02-24"

Криптография и хеширование

  • sha256("input") → хеш в hex
  • bcrypt("password", 10) — хеширование пароля
  • uuid() — генерация UUID v4
  • random_password (через ресурс) — безопасная генерация паролей

Управляющие конструкции

Условные выражения

count = var.create_instance ? 1 : 0

Циклы с for_each

resource "aws_iam_user" "users" {
for_each = toset(var.user_names)
name = each.value
}

Доступ к текущему элементу: each.key, each.value.

Циклы с count

resource "aws_instance" "server" {
count = var.instance_count
ami = var.ami
tags = {
Name = "server-${count.index}"
}
}

Индексы начинаются с 0.

Динамические блоки

Для повторяющихся вложенных блоков:

dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.from_port
to_port = ingress.value.to_port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}

Типы данных в Terraform

Terraform поддерживает богатую систему типов, позволяющую точно описывать структуру входных данных и результатов.

Примитивные типы

  • string — текстовая строка: "example", var.name
  • number — целое или дробное число: 42, 3.14, -10
  • bool — логическое значение: true, false

Коллекционные типы

  • list(type) — упорядоченный список элементов одного типа
    Пример: ["a", "b", "c"], [1, 2, 3]
  • set(type) — неупорядоченная коллекция уникальных элементов
    Пример: toset(["x", "y"])
  • map(type) — ассоциативный массив (ключ → значение)
    Пример: {name = "web", port = 80}

Структурные типы

  • object({ ... }) — именованная структура с фиксированными полями
    Пример:
    variable "server" {
    type = object({
    name = string
    cpu_cores = number
    public_ip = bool
    })
    }
  • tuple([type, ...]) — упорядоченная последовательность значений разных типов
    Пример: ["web", 2, true]

Специальные типы

  • any — принимает любой тип (используется редко, только для совместимости)
  • dynamic — не тип как таковой, а ключевое слово для динамических блоков

Преобразования типов

Terraform автоматически приводит типы в большинстве случаев, но явное преобразование повышает надёжность:

  • tostring(42)"42"
  • tonumber("100")100
  • tobool("true")true
  • tolist(map) → список значений
  • tomap(list_of_pairs) → карта из пар ключ-значение

Модули

Модуль — это переиспользуемый пакет конфигурации Terraform. Любой набор .tf-файлов в директории является модулем.

Структура модуля

/modules/
└── vpc/
├── main.tf
├── variables.tf
├── outputs.tf
└── README.md

Входные переменные (variables.tf)

Объявляются через блок variable. Все параметры вызова модуля должны соответствовать этим объявлениям.

Выходные значения (outputs.tf)

Определяют, какие данные модуль предоставляет внешнему миру:

output "vpc_id" {
value = aws_vpc.main.id
}

Вызов модуля

module "prod_vpc" {
source = "./modules/vpc"
cidr_block = "10.0.0.0/16"
enable_nat = true
}

Результат доступен как module.prod_vpc.vpc_id.

Источники модулей

  • Локальный путь: "./modules/web"
  • Git-репозиторий: "git::https://github.com/user/repo.git//path?ref=v1.2.0"
  • Terraform Registry: "terraform-aws-modules/vpc/aws"
  • HTTP-архив: "https://example.com/module.zip"

Версионирование

Для модулей из реестра или Git можно указать версию:

module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0"
}

Операторы сравнения:

  • ~> 5.0 — любая версия >= 5.0, но < 6.0
  • >= 4.0, < 5.0 — диапазон
  • = 3.2.1 — точное совпадение

Файл состояния (State)

Файл состояния (terraform.tfstate) хранит соответствие между описанием в коде и реальными ресурсами в облаке.

Содержимое state-файла

  • version — версия формата
  • terraform_version — версия Terraform
  • serial — порядковый номер изменения
  • lineage — уникальный идентификатор окружения
  • resources — список управляемых ресурсов с их атрибутами и зависимостями

Пример записи ресурса:

{
"mode": "managed",
"type": "aws_instance",
"name": "web",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"attributes": {
"ami": "ami-0c02fb55956c7d316",
"instance_type": "t3.micro",
"public_ip": "203.0.113.10"
}
}
]
}

Управление состоянием

  • terraform state list — показать все ресурсы
  • terraform state show aws_instance.web — детали конкретного ресурса
  • terraform state rm aws_instance.web — удалить из состояния (не удаляет реальный ресурс!)
  • terraform import aws_instance.web i-1234567890abcdef0 — импортировать существующий ресурс в состояние

Блокировки состояния

При использовании удалённого бэкенда (например, S3 + DynamoDB) Terraform автоматически блокирует файл состояния во время операций plan/apply, предотвращая одновременные изменения.


Бэкенды (Backends)

Бэкенд определяет, где хранится файл состояния и как выполняются операции.

Локальный бэкенд (local)

По умолчанию. Хранит terraform.tfstate в текущей директории.

Удалённые бэкенды

s3 (AWS)

backend "s3" {
bucket = "my-terraform-states"
key = "prod/web.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-locks"
}

Требует:

  • S3-бакет с включённой версионностью
  • DynamoDB-таблица для блокировок (опционально, но рекомендуется)

azurerm (Azure)

backend "azurerm" {
resource_group_name = "tfstate-rg"
storage_account_name = "tfstatestorage"
container_name = "tfstate"
key = "prod.terraform.tfstate"
}

gcs (Google Cloud)

backend "gcs" {
bucket = "my-terraform-state"
prefix = "prod"
}

Особенности бэкендов

  • После изменения бэкенда требуется terraform init -reconfigure
  • Нельзя менять бэкенд без миграции состояния
  • Некоторые бэкенды поддерживают workspace'ы — изолированные состояния в одном бэкенде

CLI-команды Terraform

Основные команды

  • terraform init — инициализация: загрузка провайдеров, модулей, настройка бэкенда
  • terraform validate — проверка синтаксиса и логики без подключения к облаку
  • terraform fmt — автоматическое форматирование кода по стандарту
  • terraform plan — предварительный расчёт изменений
  • terraform apply — применение плана (можно передать файл: apply plan.out)
  • terraform destroy — полное удаление всех ресурсов

Работа с состоянием

  • terraform state pull — выгрузить текущее состояние в stdout
  • terraform state push file.tfstate — загрузить состояние из файла (осторожно!)
  • terraform refresh — обновить состояние из реального облака (устаревшая команда; сейчас используется внутри plan)

Прочие полезные команды

  • terraform console — интерактивная REPL-сессия для тестирования выражений
  • terraform graph — генерация графа зависимостей в формате DOT
  • terraform taint aws_instance.web — пометить ресурс как «испорченный» (будет пересоздан при следующем apply)
  • terraform untaint aws_instance.web — снять пометку

Переменные в CLI

  • -var="key=value" — передача одной переменной
  • -var-file="prod.tfvars" — загрузка из файла
  • TF_VAR_name=value terraform apply — через переменную окружения

Формат .tfvars:

region = "eu-west-1"
instance_count = 3
tags = {
env = "production"
}

Workspace'ы и окружения

Workspace в Terraform — механизм изоляции состояний в рамках одного бэкенда. Он позволяет использовать одну и ту же конфигурацию для нескольких окружений: dev, stage, prod.

Создание и переключение

terraform workspace new dev
terraform workspace select prod
terraform workspace list

Каждый workspace хранит отдельный файл состояния на бэкенде (например, env:/prod/terraform.tfstate в S3).

Использование в коде

Текущее имя workspace доступно через встроенную переменную:

locals {
tags = {
Environment = terraform.workspace
ManagedBy = "Terraform"
}
}

Ограничения

  • Workspace не заменяет модульность: он не подходит для фундаментально разных архитектур.
  • Не рекомендуется использовать workspace для управления разными регионами или облачными провайдерами.
  • Все workspace'ы используют один и тот же набор .tf-файлов — логика должна быть параметризована.

Альтернатива: каталоги окружений

Более прозрачный подход — хранить каждое окружение в отдельной директории:

environments/
├── dev/
│ └── main.tf
├── stage/
│ └── main.tf
└── prod/
└── main.tf

Каждая директория вызывает общий модуль с разными входами. Такой подход лучше подходит для командной работы и ревью.


Управление секретами

Чувствительные данные (пароли, ключи API, сертификаты) не должны храниться в коде Terraform.

Рекомендуемые практики

  • Использовать внешние хранилища: HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, GCP Secret Manager.
  • Передавать секреты через переменные окружения только в CI/CD, не вручную.
  • Помечать чувствительные выходы как sensitive = true.
  • Избегать записи секретов в логи: Terraform автоматически скрывает значения, помеченные как sensitive.

Пример с AWS Secrets Manager

data "aws_secretsmanager_secret_version" "db_password" {
secret_id = "prod/db-password"
}

resource "aws_rds_cluster" "main" {
master_password = jsondecode(data.aws_secretsmanager_secret_version.db_password.secret_string)["password"]
}

Запрещено

  • Хранение секретов в .tf или .tfvars файлах.
  • Коммит секретов в Git, даже в зашифрованном виде без строгого контроля доступа.
  • Использование random_password без сохранения результата во внешнем хранилище.

Policy as Code

Terraform Enterprise/Cloud и Sentinel (HashiCorp) или Open Policy Agent (OPA) позволяют применять правила к планам и состояниям.

Пример политики Sentinel (запрет определённых типов инстансов)

import "tfplan"

main = rule {
all tfplan.resources as _, instances {
all instances as r {
not r.applied.instance_type in ["t2.micro", "t3.nano"]
}
}
}

Интеграция с OPA

Через conftest можно проверять планы:

terraform plan -out=tfplan.binary
terraform show -json tfplan.binary > tfplan.json
conftest test tfplan.json --policy policies/

Политики могут запрещать:

  • Публичные IP-адреса без обоснования
  • Отсутствие тегов
  • Использование устаревших AMI
  • Несоответствие стандартам безопасности

Продвинутые паттерны

Условное создание ресурсов

resource "aws_s3_bucket" "logs" {
count = var.enable_logging ? 1 : 0
bucket = "${var.project}-logs"
}

Динамические провайдеры

Можно использовать несколько провайдеров с разными учетными данными:

provider "aws" {
alias = "east"
region = "us-east-1"
}

provider "aws" {
alias = "west"
region = "us-west-2"
}

resource "aws_vpc" "east" {
provider = aws.east
cidr_block = "10.0.0.0/16"
}

Циклы вложенных блоков

Для повторяющихся секций (например, ingress-правила в security group):

variable "ingress_rules" {
type = list(object({
port = number
protocol = string
cidr_blocks = list(string)
}))
}

resource "aws_security_group" "web" {
name = "web-sg"

dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.port
to_port = ingress.value.port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}
}

Модуль с опциональными блоками

Использование try() и can() для безопасного доступа к атрибутам:

locals {
db_name = try(var.database.name, "default")
}

Отладка и профилирование

Логирование

Установка переменной окружения:

export TF_LOG=DEBUG
export TF_LOG_PATH=terraform.log

Уровни: TRACE, DEBUG, INFO, WARN, ERROR.

Интерактивная консоль

terraform console
> var.region
"eu-west-1"
> length(["a", "b", "c"])
3

Граф зависимостей

terraform graph | dot -Tpng > graph.png

Граф показывает порядок создания ресурсов и зависимости между ними.

Проверка плана вручную

terraform show tfplan.binary

Позволяет увидеть точные изменения до применения.


Интеграция с CI/CD

Общая схема

  1. terraform init — инициализация
  2. terraform validate — проверка синтаксиса
  3. terraform fmt -check — проверка форматирования
  4. terraform plan -out=tfplan — генерация плана
  5. (опционально) conftest test — проверка политик
  6. terraform apply tfplan — применение (только в защищённой ветке)

Безопасность в CI

  • Использовать отдельные IAM-роли с минимальными правами.
  • Хранить состояние только в удалённом бэкенде с включённой версионностью.
  • Блокировать прямые коммиты в main/prod.
  • Использовать pull request с обязательным ревью и автоматическим комментарием от terraform plan.

Пример GitHub Actions

- name: Terraform Plan
run: |
terraform init
terraform validate
terraform plan -out=tfplan
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

Встроенные функции Terraform

Terraform предоставляет более 130 встроенных функций. Ниже — систематизированный список по категориям.

Строковые функции

  • chomp("text\n") — удаляет завершающий символ перевода строки
  • format("Hello, %s!", "Alice")"Hello, Alice!"
  • formatlist("ID: %s", ["a", "b"])["ID: a", "ID: b"]
  • indent(4, "line1\nline2") — добавляет отступ ко всем строкам
  • join(",", ["a", "b"])"a,b"
  • lower("HELLO")"hello"
  • upper("hello")"HELLO"
  • replace("foo-bar", "-", "_")"foo_bar"
  • split(".", "192.168.1.1")["192", "168", "1", "1"]
  • strrev("abc")"cba"
  • substr("terraform", 0, 5)"terra"
  • title("hello world")"Hello World"
  • trim(" abc ", " ")"abc"
  • trimprefix("v1.2.0", "v")"1.2.0"
  • trimsuffix("file.txt", ".txt")"file"

Числовые функции

  • abs(-5)5
  • ceil(3.2)4
  • floor(3.9)3
  • log(100, 10)2
  • max(1, 5, 3)5
  • min(1, 5, 3)1
  • pow(2, 3)8
  • signum(-3)-1
  • sqrt(16)4

Коллекции и структуры

  • chunklist(["a", "b", "c", "d"], 2)[["a", "b"], ["c", "d"]]
  • coalesce("a", "", "b")"a" (первое непустое значение)
  • coalescelist(["x"], [], ["y"])["x"]
  • compact(["a", "", "b", null])["a", "b"]
  • concat([1], [2, 3])[1, 2, 3]
  • distinct([1, 2, 2, 3])[1, 2, 3]
  • flatten([[1, 2], [3]])[1, 2, 3]
  • keys({a = 1, b = 2})["a", "b"]
  • length([1, 2, 3])3
  • lookup({a = 1}, "a", 0)1
  • merge({a = 1}, {b = 2}){a = 1, b = 2}
  • reverse([1, 2, 3])[3, 2, 1]
  • setintersection(["a", "b"], ["b", "c"])["b"]
  • setsubtract(["a", "b"], ["b"])["a"]
  • setunion(["a"], ["b"])["a", "b"]
  • sort(["c", "a", "b"])["a", "b", "c"]
  • transpose({a = ["x"], b = ["y"]}){x = ["a"], y = ["b"]}
  • values({a = 1, b = 2})[1, 2]

Преобразования и кодировки

  • base64decode("dGVzdA==")"test"
  • base64encode("test")"dGVzdA=="
  • base64gzip("text") → закодированная и сжатая строка
  • csvdecode("a,b\n1,2")[[{a = "1", b = "2"}]]
  • jsondecode("{\"a\":1}"){a = 1}
  • jsonencode({a = 1})"{\"a\":1}"
  • urlencode("hello world")"hello+world"
  • yamldecode("a: 1\nb: 2"){a = 1, b = 2}
  • yamlencode({a = 1})"a: 1\n"
  • tomlencode({a = 1})"a = 1\n"

Файловые операции

  • abspath("./file") → абсолютный путь
  • dirname("/path/to/file")"/path/to"
  • basename("/path/to/file.txt")"file.txt"
  • file("config.txt") — читает содержимое файла
  • fileexists("config.txt")true/false
  • fileset("/dir", "*.tf") → список файлов
  • templatefile("template.tftpl", {name = "web"}) — рендерит шаблон

Дата и время

  • formatdate("YYYY-MM-DD", timestamp())"2026-02-24"
  • timeadd("2026-01-01T00:00:00Z", "72h")"2026-01-04T00:00:00Z"
  • timestamp()"2026-02-24T12:34:56Z"

Криптография и безопасность

  • bcrypt("password", 10) — хеширование с cost=10
  • md5("input") → hex-хеш
  • sha1("input") → hex-хеш
  • sha256("input") → hex-хеш
  • uuid()"f47ac10b-58cc-4372-a567-0e02b2c3d479"
  • uuidv5("ns", "name") — детерминированный UUID

Работа с IP-адресами

  • cidrhost("10.0.0.0/24", 5)"10.0.0.5"
  • cidrnetmask("10.0.0.0/24")"255.255.255.0"
  • cidrsubnet("10.0.0.0/8", 8, 1)"10.1.0.0/16"
  • cidrsubnets("10.0.0.0/24", 4, 4)["10.0.0.0/28", "10.0.0.16/28"]

Ключевые атрибуты ресурсов

AWS EC2 (aws_instance)

  • ami — идентификатор образа
  • instance_type — тип инстанса (t3.micro, m5.large)
  • key_name — имя SSH-ключа
  • user_data — скрипт инициализации
  • tags — метаданные
  • vpc_security_group_ids — список security group
  • subnet_id — подсеть
  • root_block_device — настройки корневого диска
  • ebs_block_device — дополнительные EBS-тома

AWS S3 (aws_s3_bucket)

  • bucket — имя бакета
  • acl — уровень доступа (private, public-read)
  • versioning — включение версионирования
  • server_side_encryption_configuration — шифрование
  • lifecycle_rule — правила удаления объектов
  • cors_rule — CORS-политики

AWS RDS (aws_db_instance)

  • engine — PostgreSQL, MySQL, Aurora
  • engine_version — версия СУБД
  • instance_class — класс инстанса
  • username, password — учётные данные
  • allocated_storage — размер хранилища
  • backup_retention_period — срок хранения бэкапов
  • multi_az — отказоустойчивость
  • vpc_security_group_ids — группы безопасности

Azure VM (azurerm_linux_virtual_machine)

  • name — имя ВМ
  • resource_group_name — группа ресурсов
  • location — регион
  • size — размер (Standard_B1s)
  • admin_username — имя администратора
  • network_interface_ids — сетевые интерфейсы
  • os_disk — конфигурация диска
  • source_image_reference — образ ОС

GCP Compute (google_compute_instance)

  • name — имя инстанса
  • machine_type — тип машины (e2-micro)
  • zone — зона
  • boot_disk — загрузочный диск
  • network_interface — сеть и внешний IP
  • metadata — пользовательские метаданные
  • service_account — учётная запись сервиса

Настройка провайдеров

Аутентификация

  • AWS: IAM-роли, shared credentials file, environment variables (AWS_ACCESS_KEY_ID)
  • Azure: Service Principal, Managed Identity, CLI credentials
  • GCP: Service Account key file, Application Default Credentials

Retry-логика

Провайдеры автоматически повторяют запросы при временных ошибках. Поведение можно настроить через переменные окружения:

  • AWS_MAX_ATTEMPTS=10
  • AZURE_RETRY_COUNT=5

Custom endpoints

Для частных облаков или mock-серверов:

provider "aws" {
region = "custom"
endpoints {
ec2 = "https://ec2.custom-cloud.local"
s3 = "https://s3.custom-cloud.local"
}
}

Шаблонизация через templatefile

Файл nginx.conf.tftpl:

server {
listen ${port};
server_name ${hostname};
root /var/www/${app_name};
}

Использование:

locals {
nginx_config = templatefile("${path.module}/nginx.conf.tftpl", {
port = var.nginx_port
hostname = var.domain
app_name = var.app_name
})
}

resource "aws_instance" "web" {
user_data = base64encode(local.nginx_config)
}

Поддерживаемые конструкции в шаблонах:

  • ${variable} — подстановка
  • %{ for item in list }%...%{ endfor }% — циклы
  • %{ if condition }%...%{ endif }% — условия

Миграция между версиями

Terraform

  • При переходе с 0.12 → 1.x: требуется обновление синтаксиса (например, listlist(string))
  • При переходе с 1.0 → 1.5+: проверка совместимости провайдеров
  • Использовать terraform 0.13upgrade для автоматической миграции старых конфигураций

Провайдеры

  • Всегда указывать required_providers с версиями
  • Избегать ~> 4.0 без тестирования — breaking changes возможны даже в патч-версиях
  • Перед обновлением провайдера: выполнить terraform plan в staging-окружении

Работа с большими состояниями

Partitioning

Разделение инфраструктуры на модули:

  • network/ — VPC, подсети, маршруты
  • compute/ — ВМ, autoscaling
  • database/ — RDS, кластеры

Каждый модуль имеет своё состояние.

Remote state как data source

data "terraform_remote_state" "network" {
backend = "s3"
config = {
bucket = "my-states"
key = "network/terraform.tfstate"
region = "us-east-1"
}
}

resource "aws_instance" "web" {
subnet_id = data.terraform_remote_state.network.outputs.public_subnet_id
}

Это позволяет связывать модули без жёсткой зависимости.


CLI-флаги Terraform

Terraform предоставляет широкий набор флагов для тонкой настройки поведения команд.

Общие флаги

  • -chdir=DIR — изменяет рабочую директорию перед выполнением
  • -help — вывод справки по команде
  • -version — показывает версию Terraform и провайдеров

Флаги для init

  • -backend=true|false — инициализировать бэкенд (по умолчанию true)
  • -backend-config=path — указывает файл конфигурации бэкенда (можно несколько раз)
  • -reconfigure — игнорирует существующую конфигурацию бэкенда и перезаписывает её
  • -upgrade — обновляет все провайдеры до последней совместимой версии

Флаги для plan

  • -out=path — сохраняет план в бинарный файл для последующего apply
  • -var="key=value" — задаёт значение переменной
  • -var-file=path — загружает переменные из файла
  • -destroy — генерирует план полного удаления инфраструктуры
  • -refresh-only — обновляет состояние без изменения ресурсов
  • -target=resource — ограничивает план только указанным ресурсом (и его зависимостями)

Флаги для apply

  • -auto-approve — пропускает интерактивное подтверждение
  • -lock=true|false — блокирует состояние во время операции (по умолчанию true)
  • -lock-timeout=0s — максимальное время ожидания блокировки (например, 10m)
  • -parallelism=n — ограничивает количество одновременных операций (по умолчанию 10)

Флаги для destroy

  • Все флаги apply, плюс:
  • -refresh=true|false — обновлять ли состояние перед удалением (по умолчанию true)

Флаги для state

  • -state=path — указывает путь к файлу состояния (для локального бэкенда)
  • -state-out=path — путь для записи изменённого состояния

Флаги для validate

  • -json — выводит результат в формате JSON (полезно для CI)

Флаги для fmt

  • -check — проверяет форматирование без изменения файлов
  • -diff — показывает различия
  • -write=false — не записывает изменения на диск

.terraform.lock.hcl

Файл .terraform.lock.hcl фиксирует точные версии провайдеров и их хеши. Он гарантирует воспроизводимость: все участники проекта используют одинаковые двоичные файлы провайдеров.

Структура файла

provider "registry.terraform.io/hashicorp/aws" {
version = "5.34.0"
constraints = "~> 5.0"
hashes = [
"h1:abc123...",
"h1:def456..."
]
}

Поведение

  • Файл создаётся при первом terraform init
  • Обновляется при terraform init -upgrade
  • Должен быть закоммичен в Git
  • Terraform проверяет хеш при загрузке провайдера — несоответствие вызывает ошибку

Управление

  • Чтобы обновить только один провайдер:
    terraform providers lock -platform=linux_amd64 -fs-mirror=./mirror hashicorp/aws
  • Для работы в offline-режиме: используйте -plugin-dir или filesystem mirror

Блок moved

Блок moved позволяет безопасно переименовывать или перемещать ресурсы без потери состояния.

Пример

moved {
from = aws_instance.old_name
to = aws_instance.new_name
}

После terraform apply Terraform переносит состояние из старого адреса в новый, не удаляя и не создавая ресурс.

Использование

  • При рефакторинге модулей
  • При изменении for_each или count с сохранением существующих ресурсов
  • При миграции между модулями

Ограничения

  • Нельзя использовать moved для ресурсов с разными типами
  • Нельзя указывать один и тот же адрес в нескольких from или to

Блок check

Начиная с Terraform 1.7, доступен блок check для валидации после применения.

Пример

check "database_reachable" {
data "http" "db_health" {
url = "http://${aws_db_instance.main.address}:8080/health"
}

assert {
condition = data.http.db_health.status_code == 200
error_message = "Database health check failed"
}
}

Особенности

  • Выполняется после apply, но до завершения операции
  • При провале проверки применение откатывается (если поддерживается провайдером)
  • Может содержать data-блоки и assert-утверждения

Примеры модулей

Модуль VPC (AWS)

# modules/vpc/main.tf
resource "aws_vpc" "main" {
cidr_block = var.cidr_block
tags = merge(var.tags, { Name = var.name })
}

resource "aws_subnet" "public" {
for_each = toset(var.public_subnets)
vpc_id = aws_vpc.main.id
cidr_block = each.value
availability_zone = each.key
}

Модуль Kubernetes Cluster (EKS)

module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "20.0.0"

cluster_name = var.cluster_name
cluster_version = "1.29"
subnets = module.vpc.private_subnets
vpc_id = module.vpc.vpc_id

node_groups = {
general = {
instance_types = ["m5.large"]
desired_capacity = 2
}
}
}

Модуль CI Pipeline (GitHub Actions + AWS)

resource "github_repository_file" ".github/workflows/deploy.yml" {
repository = var.repo_name
file = ".github/workflows/deploy.yml"
content = templatefile("${path.module}/deploy.yml.tftpl", {
tf_version = var.terraform_version
env = var.environment
})
}

Интеграция с Terragrunt

Terragrunt — тонкая надстройка над Terraform для управления DRY-конфигурацией.

Основные возможности

  • Наследование конфигурации через include
  • Автоматическое создание бэкендов
  • Зависимости между модулями через dependency
  • CLI-аргументы через generate

Пример terragrunt.hcl

include "root" {
path = find_in_parent_folders()
}

terraform {
source = "../modules/vpc"
}

inputs = {
cidr_block = "10.0.0.0/16"
region = "eu-west-1"
}

Когда использовать

  • При управлении десятками окружений с минимальными отличиями
  • При необходимости строгого порядка применения (например, сначала сеть, потом базы)
  • При централизованном управлении бэкендами и провайдерами