Terraform: создай сервер кодом
До этого Ansible настраивал серверы, которые уже существуют. Остался последний ручной кусок: сами серверы все еще появлялись кликами в панели облака. Terraform закрывает и его. Из первого шага модуля вы помните разделение ролей: Terraform отвечает на вопрос «что должно существовать», и сейчас вы увидите, как сервер, сеть и диск появляются из текстового файла.
Как устроен Terraform
Когда вы кликаете в панели облака «создать виртуальную машину», панель на самом деле отправляет запрос в API облака, программный интерфейс, через который облаком можно управлять без панели вообще. Terraform работает напрямую с этим API: вы описываете ресурсы в файле, а он отправляет облаку нужные запросы.

С каждым облаком Terraform общается через провайдера, плагин, который знает API конкретной платформы. Провайдеры есть под сотни сервисов: Yandex Cloud, AWS, Selectel, VK Cloud и дальше по списку. Сменилось облако, меняется провайдер и детали описания, а подход и команды остаются теми же.
Факт. Terraform сделала компания HashiCorp, первый релиз вышел в 2014 году. В 2023 году у проекта появился свободный форк OpenTofu, который работает с теми же
.tfфайлами и теми же командами.
И еще одна деталь, без которой Terraform не понять: он ведет состояние, то есть запоминает в специальном файле все, что создал. Благодаря этому он всегда может сравнить три вещи: что написано в ваших файлах, что он создавал раньше и что реально существует в облаке. На этом сравнении построена вся его работа.
Файл, те самые 15 строк
Файлы Terraform пишутся на языке HCL и имеют расширение .tf. Сначала служебная часть: какой провайдер нужен и где создавать ресурсы:
terraform {
required_providers {
yandex = {
source = "yandex-cloud/yandex"
}
}
}
provider "yandex" {
zone = "ru-central1-a" # доступ к облаку настроен заранее через токен
}Эта часть пишется один раз на проект. Дальше главное, описание сервера, тот фрагмент, который вы видели в первом шаге модуля, теперь в полном виде:
resource "yandex_compute_instance" "web" {
name = "web-server"
platform_id = "standard-v3"
resources {
cores = 2
memory = 4
}
boot_disk {
initialize_params {
image_id = "fd8kdq6d0p8sij7h5qe3" # Ubuntu 22.04
size = 20
}
}
network_interface {
subnet_id = "e9b0xxxxxxxxxxxxx"
nat = true # выдать внешний IP
}
}
output "external_ip" {
value = yandex_compute_instance.web.network_interface.0.nat_ip_address
}Блок resource описывает один ресурс. В кавычках две вещи: тип ресурса из провайдера (yandex_compute_instance, виртуальная машина в Yandex Cloud) и ваше внутреннее имя (web), по которому на ресурс можно ссылаться из других мест файла. Внутри анкета сервера, которая читается без перевода: 2 ядра, 4 ГБ памяти, диск 20 ГБ с Ubuntu, сетевой интерфейс с внешним адресом.
Блок output просит Terraform после работы напечатать значение, в данном случае внешний IP созданного сервера. Обратите внимание, как он записан: это ссылка на атрибут ресурса web, то самое внутреннее имя в деле.
Три команды, init, plan, apply
Работа с Terraform держится на трех командах.
Команда terraform init выполняется один раз в начале проекта и скачивает указанных провайдеров. Дальше про нее можно забыть.
Команда terraform plan это репетиция. Terraform сравнивает описанное в файлах с тем, что существует, и показывает, что собирается сделать, не делая ничего:
Terraform will perform the following actions:
# yandex_compute_instance.web will be created
+ resource "yandex_compute_instance" "web" {
+ name = "web-server"
+ platform_id = "standard-v3"
...
}
Plan: 1 to add, 0 to change, 0 to destroy.
Плюс перед строкой означает «будет создано», минус «будет удалено», тильда «будет изменено». По духу это git diff из модуля про Git, только разница показывается не между версиями файла, а между описанной и реальной инфраструктурой. Привычка читать plan перед применением это то же самое, что привычка смотреть diff перед коммитом: вы всегда знаете, что именно сейчас произойдет.
Команда terraform apply это применение. Она еще раз показывает план, спрашивает подтверждение, и после ввода yes идет создавать:
yandex_compute_instance.web: Creating...
yandex_compute_instance.web: Still creating... [20s elapsed]
yandex_compute_instance.web: Creation complete after 42s
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
external_ip = "51.250.x.x"
Меньше минуты назад этого сервера не существовало. Теперь он есть в панели облака, у него внешний адрес из output, и на него можно зайти по SSH. Вы не нажали в панели ни одной кнопки.
Изменения, правим файл, а не сервер
С этого момента панель облака становится местом, куда смотрят, но где не кликают. Нужно сделать сервер мощнее, меняете в файле cores = 2 на cores = 4, запускаете plan и видите, что изменится ровно этот параметр, затем apply. Нужен второй сервер, копируете блок resource с другим именем, и plan покажет 1 to add.
А если кто-то все-таки поменял настройки мышкой в обход файла? Terraform это заметит: при следующем plan он сравнит реальность с описанием и покажет расхождение, а apply вернет все как в коде. Configuration drift из первого шага модуля здесь не просто лечится, его видно в явном виде, строчкой в плане.
terraform destroy, убрать за собой
Четвертая команда нужна реже, но для обучения она едва ли не главная. Команда terraform destroy удаляет все ресурсы, которые создал этот проект, со списком и подтверждением, как у apply.
Это превращает облако в полигон. Подняли сервер для эксперимента, потрогали, вечером выполнили destroy, и деньги за простаивающую машину не капают. Поднять все заново завтра стоит один apply, потому что описание никуда не делось. Инфраструктура на вечер, на выходные, на демо это нормальный режим жизни с Terraform, немыслимый при ручной настройке.
Terraform и Ansible вместе
Теперь оба шага модуля складываются в целое. Terraform создает машину и отдает ее IP, Ansible по этому IP приходит и настраивает:

От пустого облака до работающего веб-сервера это два файла и две команды, ноль кликов. Оба файла лежат в Git со всей историей и ревью, и вся инфраструктура проекта читается как код, потому что она и есть код. Именно это имелось в виду в первом шаге модуля, когда подход назывался Infrastructure as Code.