Инфраструктура как код: Bicep и ARM
Учимся описывать облако текстовым файлом, который можно положить в git, отревьюить и развернуть одной командой — хоть в dev, хоть в прод.
Инфраструктура как код (IaC, Infrastructure as Code) — подход, при котором облачные ресурсы описываются в файлах-шаблонах и создаются автоматически, а не руками через веб-портал.
Почему кликанье в портале не масштабируется
Первое приложение в Azure почти все разворачивают одинаково: заходят в портал, нажимают «Create resource», заполняют десяток полей, ставят галочки. Это отличный способ познакомиться с сервисом. И совершенно тупиковый способ работать дальше.
Смотрите, что происходит через пару месяцев. Вам нужны три одинаковых окружения — dev, test, prod. Вы кликаете их по очереди и где-то на третьем забываете включить HTTPS-only. Прод внезапно отличается от теста, и «у меня локально работает» превращается в «у нас на тесте работает». Это называется дрейф конфигурации (configuration drift), и он всегда обнаруживается в худший момент.
Дальше хуже. Кто-то удалил правило файрвола — и никто не знает кто, когда и зачем. Новый разработчик спрашивает: «А как поднять окружение?» — и получает в ответ Confluence-страницу с 47 скриншотами, устаревшую на полгода. А ещё вы не можете сделать code review на инфраструктуру, потому что ревьюить нечего.
IaC решает всё это одним движением: инфраструктура становится обычным кодом в репозитории. У неё есть история изменений, автор каждой правки, pull request, откат к предыдущей версии и возможность развернуть точную копию продакшена за пять минут.
ARM-шаблоны и почему появился Bicep
Любое действие в Azure — из портала, из CLI, из SDK — в итоге превращается в вызов Azure Resource Manager (ARM). Это единый управляющий слой облака: он принимает описание желаемого состояния и приводит ресурсы к нему. Родной язык ARM — JSON-шаблоны. Выглядит это так:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageName": { "type": "string" }
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2023-01-01",
"name": "[parameters('storageName')]",
"location": "[resourceGroup().location]",
"sku": { "name": "Standard_LRS" },
"kind": "StorageV2"
}
]
}
Работоспособно — и абсолютно нечитаемо. Строковые функции в квадратных скобках, вложенность на пять уровней, никакой подсказки от редактора. Поэтому Microsoft сделал Bicep — компактный язык, который транспилируется в тот же самый ARM JSON. Bicep — это не отдельная система, а удобный синтаксис поверх ARM: никакого нового движка, никакого файла состояния, никаких дополнительных прав.
Первый шаблон Bicep
Тот же storage account, только по-человечески — файл main.bicep:
@description('Префикс имён ресурсов')
param prefix string
@allowed(['dev', 'test', 'prod'])
param env string = 'dev'
param location string = resourceGroup().location
var storageName = toLower('${prefix}${env}st${uniqueString(resourceGroup().id)}')
resource storage 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: storageName
location: location
sku: {
name: env == 'prod' ? 'Standard_GRS' : 'Standard_LRS'
}
kind: 'StorageV2'
properties: {
supportsHttpsTrafficOnly: true
minimumTlsVersion: 'TLS1_2'
}
tags: {
env: env
owner: 'platform-team'
}
}
output storageId string = storage.id
output blobEndpoint string = storage.properties.primaryEndpoints.blob
Здесь уже видно почти всё, что нужно для реальной работы. param — входные значения, var — вычисляемые внутри. Декоратор @allowed не даст передать prd вместо prod. Тернарный оператор задаёт георепликацию только продакшену — потому что Standard_GRS примерно вдвое дороже Standard_LRS, и платить за неё в dev-окружении смысла нет. Функция uniqueString() добавляет детерминированный хвост, потому что имя storage account должно быть уникальным на весь Azure. output отдаёт наружу значения, которые понадобятся дальше — например, пайплайну.
Деплой через CLI
Шаблон сам по себе ничего не делает. Его нужно отправить в ARM:
az group create --name rg-shop-dev --location westeurope
# Сначала СМОТРИМ, что изменится — и только потом деплоим
az deployment group what-if \
--resource-group rg-shop-dev \
--template-file main.bicep \
--parameters prefix=shop env=dev
az deployment group create \
--name deploy-$(date +%Y%m%d-%H%M) \
--resource-group rg-shop-dev \
--template-file main.bicep \
--parameters prefix=shop env=dev
what-if — самая недооценённая команда во всём Azure. Она показывает дифф между тем, что есть, и тем, что будет: что создастся, что изменится, что удалится. Приучите себя запускать её перед каждым деплоем в прод, и вы сэкономите себе минимум один инцидент.
Результат:
Resource and property changes are indicated with these symbols:
+ Create
~ Modify
The deployment will update the following scope:
Scope: /subscriptions/.../resourceGroups/rg-shop-dev
+ Microsoft.Storage/storageAccounts/shopdevst4kj2mn7q
sku.name: "Standard_LRS"
kind: "StorageV2"
Resource changes: 1 to create.
Параметры для разных сред удобно держать не в командной строке, а в отдельных файлах dev.bicepparam и prod.bicepparam — тогда команда деплоя отличается одной строкой, а всё различие сред лежит в git и видно на ревью.
Идемпотентность — главное свойство
Идемпотентность означает: запусти деплой один раз или десять раз подряд — результат будет одинаковым. Bicep описывает не «действия», а желаемое состояние. Если storage account с такими свойствами уже есть, ARM просто ничего не делает. Если кто-то руками выключил HTTPS-only — следующий деплой вернёт его обратно.
Это и есть лекарство от дрейфа: шаблон в репозитории становится единственным источником правды, а регулярный деплой — способом принудительно вернуть реальность к нему.
Но есть нюанс — режим деплоя:
| Режим | Что делает | Риск |
Incremental (по умолчанию) | создаёт и обновляет ресурсы из шаблона, остальное не трогает | в группе копится «мусор», которого нет в коде |
Complete | приводит группу ресурсов к шаблону точь-в-точь | удаляет всё, чего нет в шаблоне — включая прод-базу |
Режим Complete звучит красиво и убил не одну базу данных. Если решились — только вместе с what-if и блокировками ресурсов (az lock create).
Модули: не пишите один гигантский файл
Когда ресурсов становится больше десятка, main.bicep распиливают на модули — переиспользуемые куски:
module db 'modules/postgres.bicep' = {
name: 'db-deploy'
params: {
location: location
env: env
adminPassword: kv.getSecret('pgAdminPassword')
}
}
module app 'modules/webapp.bicep' = {
name: 'app-deploy'
params: {
location: location
dbHost: db.outputs.fqdn
}
}
Обратите внимание на две вещи. Первая: app ссылается на db.outputs.fqdn — и Bicep сам понимает, что базу надо развернуть раньше приложения. Никаких ручных dependsOn. Вторая: пароль не написан в файле, а берётся из Key Vault функцией getSecret() — секрет вообще не попадает ни в git, ни в историю деплоев.
Bicep или Terraform?
Вопрос, который обязательно зададут на собеседовании.
| Критерий | Bicep | Terraform |
| Облака | только Azure | Azure, AWS, GCP, Cloudflare и сотни провайдеров |
| Состояние | хранит сам Azure (история деплоев) | файл tfstate, который надо где-то хранить и блокировать |
| Новые фичи Azure | в день релиза (API-версии доступны сразу) | с задержкой — ждём обновления провайдера |
| Удаление ресурсов | только в режиме Complete (грубо) | аккуратно и по плану — terraform destroy |
| Экосистема | скромнее | огромная: модули, инструменты, найм |
Практическое правило: если вся компания живёт в Azure — берите Bicep, он бесплатен, встроен и не требует возиться с состоянием. Если облаков несколько или команда уже знает Terraform — берите Terraform. Оба варианта нормальные; ненормально — кликать в портале.
Как это работает
Под капотом цепочка простая. Команда az deployment group create запускает компилятор Bicep, который превращает .bicep в обычный ARM JSON (можете посмотреть сами: az bicep build --file main.bicep). JSON уходит в Azure Resource Manager. ARM строит граф зависимостей, разбивает работу на части и раздаёт задачи resource provider'ам — отдельным службам, отвечающим за свой тип ресурсов: Microsoft.Storage, Microsoft.Web, Microsoft.Sql. Каждый провайдер приводит свои ресурсы к нужному состоянию и отчитывается.
Отсюда два практических следствия. Во-первых, деплой асинхронный: CLI просто опрашивает статус, и «зависший» деплой обычно означает, что провайдер долго создаёт что-то тяжёлое (кластер AKS, SQL Managed Instance — это десятки минут, и это нормально). Во-вторых, каждый деплой сохраняется в истории группы ресурсов: az deployment group list -g rg-shop-dev покажет, кто, когда и что развернул, включая упавшие попытки с текстом ошибки. Это ваш чёрный ящик при разборе полётов.
Про деньги приятная новость: сам ARM, Bicep и деплои бесплатны. Платите только за созданные ресурсы. Никакого лимита на количество деплоев нет.
Частые ошибки
- Деплой в режиме
Complete«чтобы навести порядок». Классика: инженер добавил флаг, шаблон описывал только веб-приложение — и ARM снёс из группы базу с данными. Самая дорогая ошибка в этом уроке. Всегда сначалаwhat-if. - Секреты прямо в шаблоне.
param adminPassword string = 'Qwerty123!'навсегда останется в истории git. Используйте@secure()и Key Vault. - Дорогой SKU по умолчанию. Скопировали пример из интернета, а там
P1v3илиPremium— и dev-окружение внезапно жрёт сотню долларов в месяц. Проверяйте SKU у каждого ресурса и делайте их зависимыми отenv. - Забытые теги. Без тегов
env/owner/projectчерез полгода никто не сможет объяснить, за что вы платите, и «осиротевшие» ресурсы будут крутиться годами. - Правки руками поверх шаблона. Быстренько поменяли настройку в портале — следующий деплой её откатит, и вы полдня будете искать «мистику». Правки только через код.
- Жёстко зашитый регион.
location: 'westeurope'в модуле вместо параметра — и однажды вы получите ресурсы, размазанные по трём регионам, с оплатой трафика между ними.
Итоги
- IaC превращает инфраструктуру в код: ревью, история, откат, точная копия окружения одной командой.
- Bicep — читаемая надстройка над ARM JSON; компилируется в него же, состояние хранит сам Azure.
- Шаблон описывает желаемое состояние, поэтому деплой идемпотентен и лечит дрейф конфигурации.
az deployment group what-ifперед каждым деплоем в прод — привычка, экономящая нервы и деньги.- Режим
Completeудаляет всё, чего нет в шаблоне. Относитесь к нему как кrm -rf. - Bicep — если вы только в Azure; Terraform — если облаков несколько. Сам ARM и деплои не стоят ничего.