Дата публикации
coding

Как сделать деплой Azure Functions на Terraform предсказуемым: валидация ещё до apply

Что нового

Команда Microsoft описала практический подход к Terraform для Azure Functions, который меняет точку отказа: ошибки всплывают не на стадии terraform apply, а ещё на этапе pull request и terraform plan.

Ключевые изменения:

  1. Валидация на уровне PR:

    • автоформатирование и линтинг Terraform-кода;
    • security-сканирование;
    • контрактные тесты модулей.
  2. Pre-flight-проверки в конвейере (Azure DevOps):

    • зарегистрирован ли нужный Azure provider;
    • создано ли и доступно ли хранилище (storage account);
    • есть ли базовые права (RBAC), чтобы вообще что-то развернуть.
  3. Встроенные проверки в самом 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:

    • организация требует определённый набор переменных окружения, но модуль их не проверяет;
    • приложение уходит в прод в полубитом состоянии.

Что делать на практике

  1. Перенесите проверки в PR и plan:

    • добавьте форматирование, линтинг и security-сканирование Terraform в обязательные проверки pull request;
    • не допускайте ручного обхода этих чеков.
  2. Внедрите pre-flight шаги в пайплайн:

    • отдельный шаг в Azure DevOps, который проверяет наличие и доступность storage, регистрацию провайдера и базовый RBAC;
    • при провале — сразу красный пайплайн с чёткой причиной.
  3. Опишите правила платформы в коде Terraform:

    • заведите централизованный список допустимых runtime для Azure Functions по стандартам вашей организации;
    • добавьте validation для переменных, чтобы запрещать всё, что не попадает в этот список;
    • добавьте precondition для связки «включён флаг → требуется конкретный план/SKU».
  4. Цель — скучный 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 шагов, вы получаете предсказуемые деплои и меньше ночных инцидентов.


Читайте также