terraform
Инфраструктура как код — init/plan/apply/destroy, провайдеры, ресурсы, переменные, outputs и state
Terraform описывает инфраструктуру в файлах .tf на языке HCL и приводит реальный мир к описанию: создаёт, меняет и удаляет ресурсы через провайдеры (облака, DNS, Kubernetes, локальные файлы). Что уже создано, Terraform помнит в файле состояния — terraform.tfstate.
В песочнице нет доступа в интернет. Провайдер hashicorp/local (версия 2.5.1) заранее положен в локальное зеркало, поэтому terraform init работает с ним офлайн. Встроенный ресурс terraform_data не требует провайдера вовсе. Другие провайдеры (random, aws и т. д.) скачать не получится.
Синтаксис
terraform [-chdir=каталог] <команда> [опции]
Terraform читает все .tf в текущем каталоге как одну конфигурацию; порядок файлов и блоков не важен.
Команды
Основной цикл
| Команда | Что делает |
|---|---|
init | скачать провайдеры и модули, создать .terraform/ и .terraform.lock.hcl; -upgrade — обновить версии |
validate | проверить конфигурацию (нужен init) |
fmt | выровнять форматирование; -check — только проверить (код 3, если есть что править), -diff, -recursive |
plan | показать, что будет создано (+), изменено (~), пересоздано (-/+), удалено (-) |
plan -out=tf.plan | сохранить план; apply tf.plan применит ровно его, без вопросов |
apply | показать план и применить после ответа yes; -auto-approve — без вопроса |
destroy | удалить всё, чем управляет конфигурация (plan -destroy — посмотреть заранее) |
Флаги plan / apply
| Флаг | Что делает |
|---|---|
-var 'имя=значение' | значение переменной |
-var-file=файл.tfvars | значения из файла |
-target=адрес | работать только с одним ресурсом (для исключительных случаев) |
-replace=адрес | пересоздать ресурс, даже если он не изменился |
-refresh-only | только обновить state по реальному миру, ничего не меняя |
-no-color | без цветов — для логов и скриптов |
State и вывод
| Команда | Что делает |
|---|---|
output / output имя | все outputs / одно; -raw — без кавычек, -json — в JSON |
show | текущее состояние (или сохранённый план: show tf.plan) |
state list | ресурсы в state |
state show адрес | атрибуты одного ресурса |
state rm адрес | забыть ресурс, не удаляя его в реальности |
state mv старый новый | переименовать ресурс в state (после переименования в коде) |
import адрес id | взять существующий объект под управление |
console | интерактивно вычислять выражения: echo 'upper("x")' | terraform console |
providers | какие провайдеры нужны конфигурации |
workspace list/new/select | несколько независимых state для одной конфигурации |
graph | граф зависимостей в формате DOT |
Блоки HCL
| Блок | Для чего |
|---|---|
terraform { required_providers { … } } | какие провайдеры и версии нужны; там же required_version и backend (где хранить state) |
provider "имя" { … } | настройки провайдера (регион, токены) |
resource "тип" "имя" { … } | объект, которым управляет Terraform; адрес — тип.имя |
data "тип" "имя" { … } | прочитать существующий объект, не управляя им; адрес — data.тип.имя |
variable "имя" { … } | входной параметр: type, default, description, sensitive, validation |
output "имя" { value = … } | значение, которое печатается после apply и доступно через terraform output |
locals { имя = … } | вычисляемые значения внутри конфигурации: local.имя |
module "имя" { source = "./путь" } | подключить набор .tf-файлов как модуль |
Мета-аргументы ресурса
| Аргумент | Что делает |
|---|---|
count = 3 | несколько экземпляров; номер — count.index, адрес — тип.имя[0] |
for_each = map или set | экземпляр на каждый элемент; each.key, each.value, адрес — тип.имя["ключ"] |
depends_on = [ресурс] | явная зависимость, если её не видно по ссылкам |
lifecycle { … } | create_before_destroy, prevent_destroy, ignore_changes |
Выражения
| Запись | Что значит |
|---|---|
var.имя, local.имя | переменная, локальное значение |
тип.имя.атрибут | атрибут другого ресурса — заодно создаёт зависимость |
"${var.env}-app" | подстановка в строку |
path.module | каталог текущей конфигурации |
[for x in список : upper(x)] | преобразование списка |
условие ? a : b | выбор значения |
upper(), lower(), join(), split(), format(), length(), contains(), lookup(), file(), templatefile() | встроенные функции |
Типы переменных: string, number, bool, list(string), map(string), set(string), object({…}).
Откуда берутся значения переменных (от слабого к сильному): default → переменные окружения TF_VAR_имя → terraform.tfvars → *.auto.tfvars → -var и -var-file в командной строке.
Примеры
main.tf с локальным провайдером:
terraform {
required_providers {
local = {
source = "hashicorp/local"
version = "~> 2.5"
}
}
}
variable "greeting" {
type = string
default = "hello"
description = "Текст для файла"
}
variable "files" {
type = map(string)
default = { a = "alpha", b = "beta" }
}
resource "local_file" "note" {
filename = "${path.module}/out/note.txt"
content = "${var.greeting}\n"
file_permission = "0644"
}
resource "local_file" "many" {
for_each = var.files
filename = "${path.module}/out/${each.key}.txt"
content = upper(each.value)
}
output "note_path" {
value = local_file.note.filename
}
# подготовить каталог: провайдер возьмётся из локального зеркала
terraform init
# проверить и отформатировать
terraform validate
terraform fmt
# посмотреть план, сохранить и применить именно его
terraform plan -out=tf.plan
terraform apply tf.plan
# что Terraform сейчас знает о ресурсах
terraform state list
terraform state show 'local_file.many["a"]'
terraform output -raw note_path
# изменить переменную — план покажет пересоздание файла
terraform plan -var 'greeting=hi'
# значения из файла
echo 'greeting = "from file"' > terraform.tfvars
terraform apply -auto-approve
# посчитать выражение
echo 'upper(var.greeting)' | terraform console
# убрать всё созданное
terraform destroy -auto-approve
Подводные камни
- Без
initничего не работает.plan/apply/validateв свежем каталоге падают с ошибкой про lock-файл или неустановленные провайдеры. После правкиrequired_providersили модулей снова запустиinit. - State — источник правды для Terraform. Если ресурс удалить руками,
planпредложит создать его заново. Если удалитьterraform.tfstate, Terraform «забудет» всё созданное и попробует создать заново. - Не редактируй state вручную. Для переименований есть
state mv, для «забыть» —state rm. - Многие изменения — это пересоздание. У
local_fileсменаcontentилиfilenameдаёт-/+ must be replaced: старый объект удаляется. В плане ищиforces replacement. sensitive = trueпрячет значение только в выводеplan/apply.terraform output имяего покажет, а вterraform.tfstateоно лежит открытым текстом.terraform.tfvarsсильнееTF_VAR_…. Переменная окружения не сработает, если то же имя задано в tfvars.countадресует по номеру. Удаление элемента из середины списка сдвинет индексы и пересоздаст «чужие» ресурсы. Для именованных наборов бериfor_each.applyбез-auto-approveждёт ответаyes. Любой другой ответ — отмена, а в скрипте без вводаapplyупадёт сerror asking for approval. Если изменений нет, вопроса не будет..terraform/иterraform.tfstateне коммить: первое скачивается черезinit, второе может содержать секреты..terraform.lock.hcl— коммить.