Инфраструктуру можно описывать декларативным HCL, а можно — обычным кодом на языке, который команда уже знает. Pulumi идёт вторым путём: вы пишете программу на TypeScript, Python, Go, C#, Java или YAML, а инструмент строит граф ресурсов, сравнивает его с состоянием и применяет разницу. Для разработчика это означает циклы, функции, тесты и знакомые библиотеки вместо шаблонов и обёрток. Ниже — рабочий маршрут.
Как устроен Pulumi
Программа описывает желаемое состояние: ресурсы, их свойства и связи. При запуске Pulumi выполняет её, получает набор ресурсов, сверяет с сохранённым состоянием и показывает план. Состояние — это не файл конфигурации, а снимок того, что уже создано, с идентификаторами в облаке. Хранится он либо в Pulumi Cloud, либо в вашем бакете S3, либо локально.
Провайдеры — отдельные пакеты, например pulumi_aws или pulumi_azure_native. Они оборачивают API облака и дают типизацию: редактор подсказывает свойства, а ошибки в именах аргументов видны до запуска. Версии провайдеров фиксируются в файле зависимостей, поэтому сборка воспроизводима.
Установка и первый проект
CLI ставится одним скриптом и кладёт бинарник в домашний каталог. Для Windows есть пакетные менеджеры, для CI — официальный образ. После установки проверьте вход: при первом запуске pulumi login предложит создать аккаунт или подключить свой backend.
curl -fsSL https://get.pulumi.com | sh
pulumi version
pulumi login
pulumi new aws-python --name site-infra --stack dev --yes
Шаблон создаёт каталог с программой, файлом зависимостей и конфигурацией стека. В проект на Python попадают __main__.py, requirements.txt и Pulumi.dev.yaml. Виртуальное окружение ставьте сразу: python3 -m venv venv, затем source venv/bin/activate и pip install -r requirements.txt.
Программа на Python
Минимальный пример создаёт бакет и экспортирует два значения. Имя берётся из конфигурации, чтобы не хардкодить его в коде и менять по стекам. Теги помогают потом найти ресурсы в облачных аккаунтах.
import pulumi
import pulumi_aws as aws
config = pulumi.Config()
bucket_name = config.require("bucket_name")
bucket = aws.s3.BucketV2("assets",
bucket=bucket_name,
tags={"Project": "website", "ManagedBy": "pulumi"})
pulumi.export("bucket", bucket.id)
pulumi.export("bucket_arn", bucket.arn)
Обратите внимание на BucketV2: в провайдере AWS это актуальный ресурс для S3, старый Bucket остаётся для совместимости. Экспортированные значения видны в выводе команды и доступны другим стекам через ссылки. Если значение нужно только внутри программы, экспорт не обязателен.
Стеки и конфигурация
Стек — это отдельный экземпляр инфраструктуры с собственным состоянием и настройками. Типовая схема: dev, stage и prod в одном проекте. Код общий, различаются параметры: размер инстанса, регион, количество реплик.
pulumi stack init prod
pulumi stack select prod
pulumi config set aws:region eu-central-1
pulumi config set bucket_name assets-prod-2026
pulumi config set --secret db_password 'S3cret-Value'
pulumi config
Секреты шифруются ключом стека и попадают в файл конфигурации в зашифрованном виде. В программе их читают через config.require_secret("db_password") — значение не печатается в логах и не попадает в план. Для локального backend без Pulumi Cloud задайте PULUMI_CONFIG_PASSPHRASE, иначе при работе с секретами команда попросит пароль в интерактивном режиме.
Стеки можно переключать без пересоздания ресурсов: pulumi stack ls показывает список, pulumi stack output — экспортированные значения выбранного стека. Не храните в конфигурации пароли открытым текстом и не коммитьте Pulumi.*.yaml с секретами в публичный репозиторий.
Preview, up и разбор плана
Главная привычка при работе с Pulumi — всегда смотреть план до применения. preview выполняет программу, но не меняет облако; --diff показывает, какие свойства ресурса изменятся. Это спасает от неожиданной замены бакета или базы, когда правка одного поля требует пересоздания.
pulumi preview --diff
pulumi up
pulumi up --yes --skip-preview
pulumi destroy --yes
В плане ресурсы помечены как create, update, replace или delete. Замена — самый опасный вариант: для S3 это может означать потерю объектов, для базы — простой. Если замена нежелательна, используйте ignoreChanges для конкретных свойств или импортируйте ресурс с правильным именем.
В CI пригодится pulumi preview --expect-no-changes: команда завершится ошибкой, если кто-то изменил инфраструктуру руками. Для параллельных запусков включайте блокировку состояния — Pulumi Cloud делает это автоматически, а для S3 продумайте защиту от одновременных запусков.
Где хранить состояние
По умолчанию состояние лежит в Pulumi Cloud и привязано к организации и проекту. Альтернативы подключаются через pulumi login с URL: бакет S3, Azure Blob, GCS или локальный каталог. Свой backend удобен, когда данные не должны покидать контур, но тогда вы сами отвечаете за версионирование и блокировки.
pulumi login s3://company-pulumi-state
pulumi login --local
pulumi stack export --file backup.json
pulumi stack import --file backup.json
pulumi refresh
Перед рискованными изменениями делайте экспорт состояния: файл пригодится, если после up ресурсы перестали управляться. pulumi refresh синхронизирует снимок с реальностью и показывает drift — расхождения, появившиеся из-за правок в консоли облака. Не редактируйте состояние вручную без резервной копии.
Импорт существующих ресурсов
Переносить в код уже работающую инфраструктуру удобно постепенно. Сначала опишите ресурс в программе, затем импортируйте его по идентификатору: Pulumi добавит объект в состояние и напечатает код, который останется привести к своему стилю.
pulumi import aws:s3/bucket:Bucket assets assets-example-2026
pulumi stack output bucket
pulumi state delete 'urn:pulumi:prod::site-infra::aws:s3/bucket:Bucket::assets'
Команда state delete убирает ресурс из состояния, но не удаляет его в облаке. Это аварийный инструмент: после него ресурс становится неуправляемым, и его нужно либо импортировать заново, либо удалить вручную. Для обычной работы хватает pulumi destroy и аккуратного плана.
Чек-лист внедрения
- Зафиксируйте версии CLI и провайдеров, обновляйте их осознанно.
- Разделите стеки по средам и не используйте prod-состояние в локальных экспериментах.
- Секреты храните через
--secretили Pulumi ESC, а не в переменных в репозитории. - В CI запускайте
previewна pull request иupтолько после ревью плана. - Включите блокировку состояния и резервное копирование через
stack export. - Проверяйте drift командой
refreshперед крупными релизами.
Итог
Pulumi снижает порог входа для команд, которые уже пишут на Python или TypeScript: инфраструктура описывается теми же конструкциями, что и приложение, а план изменений остаётся наглядным. Ключевые привычки просты — отдельные стеки, секреты только в зашифрованном виде, просмотр preview --diff до применения и резервная копия состояния. Начните с одного некритичного проекта, сравните план с ожиданиями и только потом переносите на боевые ресурсы.