Начало работы с Terraform¶
В этой статье
- Что необходимо для работы с Terraform
- Шаг 1. Устанавливаем Terraform
- Шаг 2. Создаем API-ключ
- Шаг 3. Готовим конфигурацию
- Шаг 4. Инициализируем провайдер
- Шаг 5. Проверяем конфигурацию
- Шаг 6. Заказ сервера
- Шаг 7. Изменяем конфигурацию
- Шаг 8. Удаляем ресурсы
- Импорт существующих серверов
- Устранение неполадок
Информация
Terraform - инструмент управления инфраструктурой как кодом (Infrastructure as Code), разработанный компанией HashiCorp. Нужное состояние инфраструктуры описывается на языке HCL в конфигурационных файлах, а Terraform приводит к этому описанию реальные ресурсы, обращаясь к API поставщика услуг через специальный модуль, который называется провайдером. Выполненные операции записываются в файл состояния, поэтому инструмент знает, что уже создано, и при повторном запуске выполняет только недостающие изменения. Terraform поддерживает предварительный просмотр изменений до их применения, хранение конфигурации в системе контроля версий и воспроизведение одинаковых окружений, что делает его удобным средством для управления серверами, сетями и службами вне зависимости от того, у какого поставщика они размещены.
Пока сервер один, заказать его быстрее руками. Когда серверов полтора десятка и появлялись они в разное время у разных администраторов, вспомнить, почему на одном стоит Ubuntu 20.04, а на соседнем 22.04, уже не выходит. Конфигурационный файл эту историю хранит сам, а заодно позволяет поднять такое же окружение заново, не восстанавливая последовательность кликов по памяти.
С нашим провайдером работа выглядит следующим образом. В файле перечислены пресет, локация, операционная система и тариф трафика, после чего команда terraform apply заказывает сервер и дожидается завершения развёртывания. Повторный запуск ничего не продублирует, ведь Terraform помнит созданные ресурсы. Команда terraform plan показывает предстоящие изменения, ничего при этом не выполняя.
Провайдер работает со всем каталогом, включая VPS, VDS, выделенные и GPU-серверы, дополнительные IP-адреса, SSH-ключи и DNS-зоны. Полное описание ресурсов и источников данных доступно в Terraform Registry и в репозитории hostkey-cloud-ru/terraform-provider-hostkey-ru. Дальше разберём базовый сценарий, заказ виртуального сервера.
Что необходимо для работы с Terraform¶
- Аккаунт в личном кабинете Invapi и средства на балансе, поскольку заказ сервера является платной операцией;
- API-ключ;
- SSH-ключ;
- Terraform версии 1.0 или новее.
Внимание
На аккаунте должен быть хотя бы один сервер. Invapi не выдаёт сессию для аккаунта без единой услуги, и авторизация завершается ошибкой No appropriate servers found. Если аккаунт новый, закажите первый сервер через личный кабинет, а последующие уже создавайте через Terraform.
Шаг 1. Устанавливаем Terraform¶
Terraform работает на Linux, macOS и Windows, а установить его можно двумя способами, через пакетный менеджер либо вручную, загрузив и распаковав готовый бинарный файл.
Примечание
HashiCorp ограничивает доступ к своим ресурсам с российских IP-адресов, причём это касается и загрузки самого Terraform, и реестра провайдеров registry.terraform.io. Для установки провайдера мы используем публичное зеркало, настройка которого описана в шаге 4.
Windows¶
Скачайте архив с бинарным файлом, распакуйте terraform.exe в отдельный каталог, например C:\terraform, и добавьте этот каталог в переменную среды Path.
После этого закройте терминал и откройте его заново, поскольку без перезапуска новое значение Path не применится. Проверить установку можно так:
Linux¶
wget https://releases.hashicorp.com/terraform/1.15.8/terraform_1.15.8_linux_amd64.zip
unzip ./terraform_1.15.8_linux_amd64.zip
sudo mv ./terraform /usr/local/bin
terraform -v
macOS¶
Шаг 2. Создаем API-ключ¶
Ключ создаётся в личном кабинете Invapi. Нажмите на имя пользователя в правом верхнем углу и выберите API ключи:

Нажмите Добавить новый и заполните форму.

| Поле | Значение |
|---|---|
| Название ключа | 5-30 символов, только латиница, цифры, _ и - |
| Ограничить новый ключ API для сервера | Любой |
| IP ACL | Оставьте пустым для доступа с любого адреса |
| Установить метод уведомления о входе | Нет |
| Активный | Отметить |
Поле Ограничить новый ключ API для сервера привязывает ключ к конкретной услуге, а провайдеру требуется заказывать новые серверы, поэтому значение должно быть Любой.
Поле IP ACL ограничивает доступ перечисленными адресами. Это повышает безопасность, однако при динамическом IP-адресе ключ перестанет работать после его смены, так что для первого знакомства поле оставляют пустым, а для запуска из систем непрерывной интеграции указывают адреса серверов сборки.
Нажимаем Создать. Ключ отобразится один раз.

Внимание
Сохраните ключ сразу, поскольку мы храним только его хеш и восстановить значение невозможно. При утере придётся создавать новый ключ.
Шаг 3. Готовим конфигурацию¶
Примеры конфигурации
Готовые примеры конфигураций доступны в репозитории провайдера. Для базового сценария можно использовать пример из каталога examples/basic.
Создайте каталог проекта, например hostkey-terraform. Файлы конфигурации имеют расширение .tf, а их имена произвольны, поскольку Terraform объединяет все .tf-файлы каталога в одну конфигурацию. В нашем примере используются три файла.
main.tf¶
Первый блок фиксирует, какой провайдер и какая версия Terraform требуются.
terraform {
required_providers {
hostkey = {
source = "hostkey-cloud-ru/hostkey-ru"
version = "~> 0.2"
}
}
required_version = ">= 1.0"
}
provider "hostkey" {}
Примечание
Начиная с версии 0.2 провайдер выпускается отдельно для каждого биллинга. Пакет hostkey-ru работает с invapi.hostkey.ru, поэтому адрес API выбирать не нужно и блок провайдера остаётся пустым.
Далее следует сверка каталога. Эти блоки не создают ресурсов и не тарифицируются, а только запрашивают у API списки доступных для заказа пресетов и тарифы трафика:
data "hostkey_presets" "selected" {
location = var.location
name = var.preset_name
}
data "hostkey_traffic_plans" "for_preset" {
location = var.location
instance_id = data.hostkey_presets.selected.presets[0].id
}
output "catalog_preset" {
value = data.hostkey_presets.selected.presets
}
output "catalog_traffic_plans" {
value = data.hostkey_traffic_plans.for_preset.traffic_plans
}
Сверка нужна по двум причинам. Провайдер требует точного совпадения имён, а в каталоге встречаются похожие названия тарифов, например 3Tb @1Gbps VPS RU и 3Tb VPS RU. Кроме того, состав каталога зависит от локации и со временем меняется, из-за чего пресет, доступный сегодня, завтра может быть недоступен.
Затем описывается сам сервер:
resource "hostkey_server" "web" {
preset_name = var.preset_name
location_name = var.location
traffic_plan_name = var.traffic_plan_name
deploy_period = "monthly"
os_name = "Ubuntu 22.04"
root_pass = var.root_pass
ssh_key = file(pathexpand(var.ssh_public_key_path))
power_state = "on"
cancellation_type = 1
cancellation_reason = "terraform"
tags = {
env = "demo"
}
timeouts {
create = "90m"
update = "90m"
delete = "30m"
}
}
Блок timeouts задаёт, сколько Terraform ждёт завершения операции. Развёртывание обычно занимает пару минут, но при обрыве по таймауту заказ останется оплаченным, а Terraform потеряет с ним связь.
Параметр cancellation_type определяет поведение при удалении, где 1 отменяет услугу немедленно, а 0 в конце оплаченного периода.
Примечание
Параметр hostname в примере намеренно не задан. Если его не указывать, провайдер сгенерирует уникальное имя вида tf-44067425, под которым услуга будет видна в личном кабинете. Внутри операционной системы имя при этом может отличаться, поскольку Invapi не передаёт его гостевой системе, и проверить его можно только командой hostname на самом сервере.
Отдельно создаётся SSH-ключ в хранилище аккаунта:
resource "hostkey_ssh_key" "deploy" {
name = "tf-deploy"
key = file(pathexpand(var.ssh_public_key_path))
}
Это не то же самое, что атрибут ssh_key ресурса hostkey_server. Атрибут сервера прописывает ключ на машину при установке операционной системы, а ресурс hostkey_ssh_key сохраняет ключ в аккаунте для дальнейшего использования.
В конце файла идут блоки output. После заказа Terraform выведет в терминал адрес сервера, его идентификатор и номер счёта, а terraform output main_ipv4 вернёт адрес в любой момент, что удобно, когда следом запускается что-то ещё:
output "server_id" {
value = hostkey_server.web.id
}
output "main_ipv4" {
value = hostkey_server.web.main_ipv4
}
output "invoice" {
value = hostkey_server.web.invoice
}
variables.tf¶
variable "location" {
type = string
default = "RU"
}
variable "preset_name" {
type = string
default = "vm.pico"
}
variable "traffic_plan_name" {
type = string
default = "3Tb @1Gbps VPS RU"
}
variable "root_pass" {
type = string
sensitive = true
}
variable "ssh_public_key_path" {
type = string
default = "~/.ssh/id_ed25519.pub"
}
terraform.tfvars¶
Файл содержит пароль, поэтому в систему контроля версий его не добавляют:
root_pass = "StrongPass1%"
# Значения по умолчанию можно переопределить здесь
# location = "NL"
# preset_name = "vm.v2-pico"
# traffic_plan_name = "3 TB / 1 Gbps VM"
Внимание
Пароль root должен содержать от 8 до 30 символов, включая заглавную букву, строчную, цифру и один из символов %, -, _, +. Символы @ и # не допускаются, кириллица не поддерживается. Пароль сохраняется в файле состояния Terraform и приходит открытым текстом в письме о готовности сервера, поэтому после развёртывания его стоит сменить.
Шаг 4. Инициализируем провайдер¶
Ключ передают через переменную окружения, чтобы он не попал в файлы проекта:
В командной строке Windows используется set HOSTKEY_API_KEY=ваш-ключ, а в Linux и macOS export HOSTKEY_API_KEY="ваш-ключ". Переменная действует только в текущем сеансе терминала.
Примечание
Ключ требуется уже на этапе планирования, поскольку провайдер сверяет имена из конфигурации с каталогом и без доступа к API завершается ошибкой.
Поскольку реестр registry.terraform.io недоступен с российских IP-адресов, провайдер устанавливают через публичное зеркало Yandex Cloud. Создайте файл %APPDATA%\terraform.rc в Windows либо ~/.terraformrc в Linux и macOS:
provider_installation {
network_mirror {
url = "https://terraform-mirror.yandexcloud.net/"
}
direct {
exclude = ["registry.terraform.io/*/*"]
}
}
Аккаунт в Yandex Cloud для этого не требуется. Далее необходимо загрузить провайдер:

Terraform скачает провайдер и создаст файл .terraform.lock.hcl с точной версией. Этот файл добавляют в репозиторий, поскольку он гарантирует, что у всех участников проекта будет установлена одинаковая версия.
Примечание
При установке через зеркало контрольные суммы рассчитываются только для текущей платформы. Если конфигурация будет использоваться на других операционных системах, выполните terraform providers lock -platform=linux_amd64, указав нужные платформы.
Шаг 5. Проверяем конфигурацию¶
Синтаксис проверяется без обращения к API:
При успешной проверке выводится сообщение Success! The configuration is valid.
Затем строится план, который показывает предстоящие изменения, но ничего не выполняет:

Итоговая строка. Ожидается Plan: 2 to add, 0 to change, 0 to destroy, то есть сервер и SSH-ключ.
Разрешённые идентификаторы. Провайдер подставляет preset_id, os_id и traffic_plan_id рядом с именами, а если имя в каталоге не найдено, план завершится ошибкой ещё до списания средств.
Каталог. В блоке Changes to Outputs выводятся списки пресетов и тарифов, и нужные строки должны быть записаны в конфигурации в точности так же.
Шаг 6. Заказ сервера¶
Terraform повторно покажет план и запросит подтверждение, необходимо ввести yes и нажать Enter.

Внимание
С этого момента заказ оплачен. Не закрывайте окно терминала и не прерывайте команду, иначе заказ останется в личном кабинете, а Terraform потеряет связь с ним.
Сначала создаётся SSH-ключ, затем начинается заказ сервера. В данном примере эти операции не связаны явной зависимостью в конфигурации Terraform, поэтому их порядок не гарантирован. Далее выводятся строки Still creating... с обновлением каждые десять секунд, пока провайдер опрашивает API и ожидает завершения установки.
По завершении выводятся значения:

Лучше проверить доступ по SSH, подставив полученный адрес:
Ключ, указанный в атрибуте ssh_key, уже прописан на сервере, поэтому пароль не запрашивается.
Шаг 7. Изменяем конфигурацию¶
Изменения делятся на три категории:
-
Безопасные изменения. Теги и состояние питания применяются к работающему серверу, а в плане такие изменения отображаются как
update in-place. -
Переустановка системы. Изменение
os_name,soft_name,root_passилиssh_keyпереустанавливает операционную систему на том же сервере, из-за чего все данные на диске теряются. -
Новый заказ. Изменение
preset_name,location_name,traffic_plan_nameилиdeploy_periodозначает, что прежний сервер отменяется, а новый заказывается заново, то есть средства списываются повторно. В плане такие изменения помечаются какforces replacement.
Внимание
Изменения, ведущие к переустановке, отображаются в плане как update in-place, точно так же, как безобидное изменение тега. Провайдер выводит отдельное предупреждение о потере данных, поэтому перед подтверждением стоит читать не только план, но и предупреждения.
Шаг 8. Удаляем ресурсы¶
terraform destroy удаляет все ресурсы, которые находятся в текущем Terraform state для этой конфигурации. Terraform покажет список удаляемых ресурсов и запросит подтверждение, необходимо ввести yes и нажать Enter. Если нужно удалить только один ресурс, можно указать его через -target. Например:
Здесь web — имя ресурса из блока resource "hostkey_server" "web", а не ID сервера из панели управления.
Другой вариант — удалить ресурс из .tf-файлов и выполнить:
Terraform увидит, что ресурса больше нет в конфигурации, и удалит его из инфраструктуры.
Отмена услуги выполняется через Invapi, а способ отмены определяется параметром cancellation_type: при значении 1 услуга отменяется сразу, при 0 — в конце оплаченного периода.
Информация
При немедленной отмене (cancellation_type = 1) неиспользованная часть оплаченного периода возвращается на баланс аккаунта пропорционально фактическому времени работы.
Импорт существующих серверов¶
Серверы, заказанные через личный кабинет, можно передать под управление Terraform. Идентификатор берётся из колонки ID в списке серверов:
После импорта в состояние попадают фактические данные, то есть идентификатор, адрес, статус и состояние питания. Параметры заказа из личного кабинета не переносятся, поэтому их описывают в конфигурации самостоятельно. Первый запуск terraform apply после импорта к переустановке системы не приводит.
Устранение неполадок¶
Примечание
Ошибка No appropriate servers found при выполнении plan. Проверьте, что на аккаунте есть хотя бы одна услуга, поскольку Invapi не выдаёт сессию для аккаунта без единой услуги.
Примечание
Ошибка Catalog name resolve failed. Указанное имя пресета, операционной системы или тарифа отсутствует в каталоге для выбранной локации. Выведите доступные значения через источники данных hostkey_presets и hostkey_traffic_plans и приведите конфигурацию в соответствие.
Примечание
Состояние pending:<номер счёта>. Заказ оплачен, но развёртывание не завершилось, как правило из-за обрыва связи. Повторный запуск terraform apply продолжит ожидание и не создаст новый заказ. Если счёт не оплачен, его необходимо оплатить и повторно запустить terraform apply. Текущий статус услуги в этот момент отображается в личном кабинете.
Информация
Подробнее об инструкциях Terraform можно прочитать в официальной документации HashiCorp, а специфичные параметры ресурсов и источников данных описаны в Terraform Registry.