Что это и зачем
terraform-compliance, open-source утилита, написанная на Python. На вход
принимает Terraform plan в JSON, на вход принимает набор .feature файлов
в Gherkin (как у Cucumber/behave). Проверяет каждый шаг feature против
plan.
Запуск:
terraform plan -out=plan.tfplan
terraform show -json plan.tfplan > plan.json
terraform-compliance --planfile plan.json --features ./policies
Идея: правила пишутся языком, который читает security/compliance-команда, не инженер. Они могут сами добавлять policies без знания HCL.
Минимальный feature-файл
policies/s3-buckets.feature:
Feature: S3 buckets must be encrypted
Scenario: Encryption is required
Given I have aws_s3_bucket defined
Then it must contain server_side_encryption_configuration
Scenario: Versioning is required
Given I have aws_s3_bucket defined
Then it must contain versioning
And it must contain enabled
And its value must be true
Выполнение пройдётся по каждому aws_s3_bucket в plan и проверит, что
правила соблюдены. Если хоть один не encryption'нут, exit 1, CI ломается.
Структура Gherkin для terraform-compliance
Базовые шаги:
| Шаг | Что делает |
|---|---|
Given I have <resource_type> defined | Фильтр: рассматриваем только ресурсы такого типа. |
When its <property> is "value" | Дополнительный фильтр, только ресурсы с таким свойством. |
Then it must contain <key> | Утверждение: ресурс должен иметь это поле. |
Then it must not contain <key> | Утверждение наоборот. |
Then its value must be <X> | Сравнение значения. |
Then its value must match the "regex" | Regex-match по значению. |
Расширенные:
| Шаг | Что делает |
|---|---|
Given I have any resource defined | Все ресурсы. |
Given I have <module> action_name defined | Фильтр по модулю. |
Then it must have tags | Распространённая проверка тегов. |
Then it must contain tags ([k1,k2,k3]) | Конкретные ключи в тегах. |
Теги в фичах
Можно пометить scenario и запускать выборочно:
@encryption @critical
Scenario: Encryption is required
Given I have aws_s3_bucket defined
Then it must contain server_side_encryption_configuration
Запуск только критичных:
terraform-compliance -p plan.json -f ./policies --tags critical
CI-интеграция
GitHub Actions:
jobs:
plan-and-validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- run: terraform init
- run: terraform plan -out=plan.tfplan
- run: terraform show -json plan.tfplan > plan.json
- name: Install terraform-compliance
run: pip install terraform-compliance==1.3.50
- name: Validate
run: terraform-compliance -p plan.json -f ./policies/
Возвращает non-zero exit на любой failed scenario → PR красный.
Когда terraform-compliance, когда OPA
| Аспект | terraform-compliance | OPA + Rego (conftest) |
|---|---|---|
| Синтаксис | Gherkin, читается английским | Rego, читается как-то |
| Кривая обучения | Низкая для писателя правил | Заметная, другой язык |
| Cross-resource проверки | Слабо, каждое правило локальное | Сильно, можно проверять связи |
| Внешние данные (lookup) | Нет | Да, через data |
| Зрелость / экосистема | Меньшая, узко-фокусированная | Шире, OPA общий policy-engine |
| Скорость на больших plan'ах | Заметно медленнее | Быстрее |
| Поддержка vendor | Open-source, есть мейнтейнер | Open-source, CNCF, vendor-support через Styra |
Для маленькой команды, где политики простые («все бакеты с тегом CostCenter») terraform-compliance быстрее запустить. Для зрелой инфраструктуры с десятками сложных правил, для cross-account/cross-region логики, OPA. См. tf-policy-as-code.
Пример развитой policy-suite
policies/
├── tagging.feature
├── encryption.feature
├── naming.feature
├── public-access.feature
└── networking.feature
tagging.feature:
Feature: All resources must have mandatory tags
Scenario: CostCenter tag
Given I have any resource defined
When it contains tags
Then it must contain CostCenter
Scenario Outline: Allowed environments
Given I have any resource defined
When it contains tags
Then it must contain Environment
And its value must be one of <envs>
Examples:
| envs |
| dev,stage,prod |
Обрати внимание на Scenario Outline, таблично-параметризованные правила.
Подводные камни
-
Проверяет только то, что в plan. Если ресурс уже создан и в plan нет изменений, terraform-compliance его не увидит. Это не инструмент аудита текущего state, это gate на новые изменения.
-
Не понимает выражения.
tags = local.standard_tagsв plan'е уже раскрыто в map ({"CostCenter": "foo"}), это ок. Но если значение(known after apply), terraform-compliance не имеет данных для проверки и считает scenario как «не применимо». Это окно для обхода правила. -
Конкурирующий маркап. Некоторые шаги от старых версий не работают в новых, документация хорошо отстаёт от changelog'а. Перед adoption зафиксируй версию (
==1.3.50) и обновляй сознательно. -
CI-integration требует plan-файла. Это значит pipeline: plan → show -json → terraform-compliance. Если plan уже артефакт между jobs (см. tf-plan-apply-ci) встраивается легко. Если pipeline линейный и plan живёт только в памяти, переделай.
-
Не заменяет статический анализ HCL.
terraform validate,tflint,checkovработают на сорсах. terraform-compliance, на plan'е. Это разные слои; они должны быть оба.