- Дата публикации
Как сделать деплой Azure Functions на Terraform предсказуемым: валидация ещё до apply
Что нового
Команда Microsoft описала практический подход к Terraform для Azure Functions, который меняет точку отказа: ошибки всплывают не на стадии terraform apply, а ещё на этапе pull request и terraform plan.
Ключевые изменения:
-
Валидация на уровне PR:
- автоформатирование и линтинг Terraform-кода;
- security-сканирование;
- контрактные тесты модулей.
-
Pre-flight-проверки в конвейере (Azure DevOps):
- зарегистрирован ли нужный Azure provider;
- создано ли и доступно ли хранилище (storage account);
- есть ли базовые права (RBAC), чтобы вообще что-то развернуть.
-
Встроенные проверки в самом Terraform:
validationдля входных переменных (environment, runtime, соглашения по именованию);preconditionдля обязательных настроек и ограничений по планам (SKU, возможности плана, нужные флаги).
Результат — terraform apply превращается в «скучный» шаг без сюрпризов. Если что-то сломается, это произойдёт раньше, с понятным сообщением.
Отдельные эффекты, которые команда явно зафиксировала:
- перенос части сбоев с apply-времени на plan-время;
- экономия 2+ часов на каждый сбойный цикл деплоя в одном из кейсов;
- полное исчезновение повторных инцидентов определённого класса после добавления конкретной проверки.
Как это работает
Подход строится на трёх слоях «ограждений» вокруг Terraform.
1. Проверки на уровне pull request
Цель — не пропускать в main-ветку код, который гарантированно сломает пайплайн.
Что добавляют в PR-проверки:
- Форматирование и линтинг Terraform-кода, чтобы модуль оставался читаемым и предсказуемым.
- Security-сканирование — поиск очевидных проблем безопасности ещё до запуска инфраструктуры.
- Контрактные тесты модулей — автоматическая проверка, что модуль соблюдает ожидаемый интерфейс (входы/выходы, базовые инварианты).
2. Pre-flight-проверки в Azure DevOps
Terraform не всегда может сам понять, существует ли внешний ресурс и есть ли к нему доступ. Поэтому в конвейер добавляют лёгкий pre-flight шаг до plan/apply.
Примеры проверок:
- зарегистрирован ли нужный Azure Resource Provider;
- существует ли указанный storage account и не удалили ли его раньше;
- есть ли доступ к этому storage (ключи, роли, сетевые ограничения);
- базовая проверка RBAC: достаточно ли прав у сервисного аккаунта, чтобы выполнять действия плана.
Если что-то не так, пайплайн падает ещё до Terraform. Сообщение в логе сразу указывает на проблему, без долгих расследований.
3. Валидации и preconditions внутри Terraform
Здесь команда использует встроенные механизмы Terraform:
-
validationу переменных — проверка значений входных параметров.- environment соответствует допустимым значениям;
- runtime попадает в список поддерживаемых;
- имена ресурсов соответствуют корпоративным правилам.
-
preconditionв ресурсах и модулях — проверка инвариантов перед созданием ресурса.- если включена возможность, требующая определённого плана (SKU), Terraform прерывает план с понятным сообщением;
- обязательные настройки (например, storage или app settings) должны быть заданы, иначе план не строится.
Вся логика — это небольшие куски кода, а не огромный фреймворк. Модуль остаётся компактным, пайплайн — быстрым.
Что это значит для вас
Когда это нужно
Если вы:
- деплоите Azure Functions через Terraform;
- работаете через Azure DevOps или другой CI/CD;
- уже сталкивались с тем, что пайплайн падает на
terraform applyпосле всех согласований и начала change window,
то вам стоит перенести максимум проверок на этап PR и terraform plan.
Типичные проблемы, которые всплывают слишком поздно:
-
Неверный план/SKU:
- команда ожидает одну сетевую модель (например, VNET-интеграцию), а выбранный план её не поддерживает;
- ошибка всплывает уже при применении или в рантайме.
-
Проблемы со storage:
- Function App опирается на storage, которого уже нет, или у пайплайна нет к нему доступа;
- деплой рушится в середине, подключаются несколько команд, время горит.
-
Неподдерживаемый runtime:
- выбранный стек и версия в принципе валидны для Azure, но не соответствуют стандартам вашей платформы или региональным ограничениям;
- ошибка проявляется на apply или уже после релиза.
-
Несогласованные app settings:
- организация требует определённый набор переменных окружения, но модуль их не проверяет;
- приложение уходит в прод в полубитом состоянии.
Что делать на практике
-
Перенесите проверки в PR и plan:
- добавьте форматирование, линтинг и security-сканирование Terraform в обязательные проверки pull request;
- не допускайте ручного обхода этих чеков.
-
Внедрите pre-flight шаги в пайплайн:
- отдельный шаг в Azure DevOps, который проверяет наличие и доступность storage, регистрацию провайдера и базовый RBAC;
- при провале — сразу красный пайплайн с чёткой причиной.
-
Опишите правила платформы в коде Terraform:
- заведите централизованный список допустимых runtime для Azure Functions по стандартам вашей организации;
- добавьте
validationдля переменных, чтобы запрещать всё, что не попадает в этот список; - добавьте
preconditionдля связки «включён флаг → требуется конкретный план/SKU».
-
Цель — скучный
terraform apply:- treat apply как механический шаг, который не должен открывать новых фактов о платформе;
- если apply всё ещё используется как способ «посмотреть, что скажет Azure», значит, валидаций мало.
Когда это может быть избыточно
- Маленькие pet-проекты, где один инженер делает всё сам и легко откатывается вручную.
- Одноразовые среды, где сбой деплоя почти ничего не стоит.
Но как только у вас появляются:
- несколько команд;
- формальные change windows;
- согласования и аудит,
каждый поздний сбой превращается в дорогой инцидент. Валидация до apply перестаёт быть оптимизацией и становится обязательной частью платформы.
Ограничения
Подход завязан на Azure Functions, Terraform и Azure DevOps. Если вы работаете в другой облачной среде или с другим CI/CD, идею придётся переносить руками.
Если у вас нет доступа к Azure или он ограничен по региону, реализовать всё в таком же виде не получится. Но сами принципы — перенос проверки в PR/plan, pre-flight шаги, централизованный список допустимых конфигураций — легко адаптируются к любому облаку.
Место на рынке
Речь не о новом продукте, а о способе использования Terraform и Azure DevOps.
Если сравнивать с типичным подходом «Terraform как есть»:
- стандартные пайплайны часто используют
terraform applyкак механизм исследования: запускают и смотрят, что сломается; - описанный подход превращает Terraform в инструмент с жёсткими правилами платформы, зашитыми в код.
По сравнению с тяжёлыми внутренними платформами, которые строят вокруг Terraform собственные абстракции и DSL, здесь другой баланс:
- минимум дополнительного кода: несколько
validation, несколькоprecondition, один лёгкий pre-flight шаг в Azure DevOps; - модуль остаётся читаемым, без толстого фреймворка вокруг;
- правила платформы не размазаны по wiki и устным договорённостям, а живут в коде.
Чётких числовых сравнений с другими инструментами инфраструктуры (Pulumi, Bicep и так далее) авторы не приводят. Акцент на том, как выстроить процесс вокруг уже существующей связки Terraform + Azure Functions + Azure DevOps.
Как запустить: практические элементы
Исходный текст не даёт готового куска кода, но описывает, из чего состоит «минимальный» набор.
1. Валидации входных переменных
Примерно так могут выглядеть проверки в variables.tf:
variable "environment" {
type = string
description = "Deployment environment"
validation {
condition = contains(["dev", "test", "prod"], var.environment)
error_message = "Environment must be one of: dev, test, prod."
}
}
variable "function_runtime" {
type = string
description = "Azure Functions runtime stack"
validation {
condition = contains(["dotnet-isolated", "node", "python"], var.function_runtime)
error_message = "Runtime is not allowed by platform standards."
}
}
2. Preconditions для планов и обязательных настроек
Внутри модуля Azure Function можно добавить preconditions для ресурсов:
resource "azurerm_app_service_plan" "this" {
name = var.plan_name
location = var.location
resource_group_name = var.resource_group_name
kind = "FunctionApp"
sku {
tier = var.plan_tier
size = var.plan_size
}
lifecycle {
precondition {
condition = !(var.enable_vnet_integration && var.plan_tier == "Consumption")
error_message = "VNET integration requires Premium plan or higher."
}
}
}
resource "azurerm_storage_account" "this" {
# ...
lifecycle {
precondition {
condition = var.storage_account_name != ""
error_message = "Storage account name must be provided."
}
}
}
3. Pre-flight шаг в Azure DevOps
В конвейере можно добавить шаг, который до запуска Terraform проверяет существование storage и базовый доступ. Это может быть скрипт на Bash или PowerShell, который:
- опрашивает Azure CLI на наличие нужного storage account;
- проверяет, что сервисный principal видит этот ресурс;
- при провале завершает задачу с ненулевым кодом.
Логика проста: не давать Terraform тратить время на план и apply, если базовая инфраструктура уже не совпадает с ожиданиями.
Главный вывод: проблемы с Azure Functions часто связаны не с багами в коде или Terraform, а с тем, что платформа «отвечает» слишком поздно. Перенеся проверки на уровень PR, terraform plan и лёгких pre-flight шагов, вы получаете предсказуемые деплои и меньше ночных инцидентов.