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— указание версий провайдеров и самого Terraformlocals.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)→5ceil(3.2)→4floor(3.9)→3log(100, 10)→2pow(2, 3)→8
Коллекции и структуры
length([1,2,3])→3keys({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({...})— сериализация в JSONyamlencode({...})— сериализация в 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")→ хеш в hexbcrypt("password", 10)— хеширование пароляuuid()— генерация UUID v4random_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.namenumber— целое или дробное число:42,3.14,-10bool— логическое значение: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")→100tobool("true")→truetolist(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— версия Terraformserial— порядковый номер изменения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— выгрузить текущее состояние в stdoutterraform state push file.tfstate— загрузить состояние из файла (осторожно!)terraform refresh— обновить состояние из реального облака (устаревшая команда; сейчас используется внутриplan)
Прочие полезные команды
terraform console— интерактивная REPL-сессия для тестирования выраженийterraform graph— генерация графа зависимостей в формате DOTterraform 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
Общая схема
terraform init— инициализацияterraform validate— проверка синтаксисаterraform fmt -check— проверка форматированияterraform plan -out=tfplan— генерация плана- (опционально)
conftest test— проверка политик 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)→5ceil(3.2)→4floor(3.9)→3log(100, 10)→2max(1, 5, 3)→5min(1, 5, 3)→1pow(2, 3)→8signum(-3)→-1sqrt(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])→3lookup({a = 1}, "a", 0)→1merge({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/falsefileset("/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=10md5("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 groupsubnet_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, Auroraengine_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— сеть и внешний IPmetadata— пользовательские метаданные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=10AZURE_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: требуется обновление синтаксиса (например,
list→list(string)) - При переходе с 1.0 → 1.5+: проверка совместимости провайдеров
- Использовать
terraform 0.13upgradeдля автоматической миграции старых конфигураций
Провайдеры
- Всегда указывать
required_providersс версиями - Избегать
~> 4.0без тестирования — breaking changes возможны даже в патч-версиях - Перед обновлением провайдера: выполнить
terraform planв staging-окружении
Работа с большими состояниями
Partitioning
Разделение инфраструктуры на модули:
network/— VPC, подсети, маршрутыcompute/— ВМ, autoscalingdatabase/— 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"
}
Когда использовать
- При управлении десятками окружений с минимальными отличиями
- При необходимости строгого порядка применения (например, сначала сеть, потом базы)
- При централизованном управлении бэкендами и провайдерами