Короткий ответ
Нужно разделить модули на уровни обязательности: небольшой набор "golden" модулей с жёстко зафиксированными небезопасными-по-умолчанию настройками (например, S3-бакет с обязательным шифрованием и блокировкой публичного доступа) — обязателен и проходит через централизованный review и SLA на обновления; остальная логика остаётся в свободном использовании командами, с возможностью прислать свой модуль в общий каталог через стандартизированный, но лёгковесный процесс (CI-проверки + review от платформенной команды, а не ручное одобрение каждой мелочи); единственно верного баланса между контролем и автономией нет, и выбор нужно явно обсуждать с учётом размера организации, зрелости команд и регуляторных требований.
Как это работает подробнее
governance здесь означает комбинацию технического контроля (policy as code, проверяющая, что критичные ресурсы созданы только через одобренные модули/с одобренными настройками) и процесса (кто может публиковать и обновлять модуль, как версионируется breaking change, как потребители узнают об устаревших версиях); без этого каждая из 15 команд предсказуемо воспроизводит свою версию одних и тех же решений с разным уровнем качества, потому что для них это не основная экспертиза.
Сравнение
- Компромисс. Жёсткий централизованный контроль всех модулей снижает риск небезопасных конфигураций, но масштабируется плохо: платформенная команда становится бутылочным горлышком, а команды-потребители обходят процесс через shadow-IaC; полностью децентрализованный подход быстр для команд, но воспроизводит разнородные и местами небезопасные решения. Разумный компромисс — малый обязательный набор policy-as-code правил и golden-модулей для реальных рисков, и самообслуживание (self-service) с лёгким review для всего остального, пересматриваемое по метрикам нагрузки на платформенную команду и числу инцидентов безопасности.
Пример кода
# пример "golden" модуля с безопасным дефолтом, который нельзя обойти
module "secure_bucket" {
source = "registry.acme.local/platform/secure-s3-bucket"
version = "2.3.0"
name = "team-orders-bucket"
# encryption и public access block зашиты внутри модуля без возможности отключить
}
# policy as code (Conftest/OPA): блокирует bucket, созданный не через golden-модуль
deny[msg] {
input.resource_type == "aws_s3_bucket"
not input.module_source_matches_approved_list
msg := "S3-бакеты должны создаваться через platform/secure-s3-bucket"
}Что использовать на практике
- Практика. на практике работает модель "тонкой талии": платформенная команда владеет малым числом модулей, покрывающих реальные риски (secrets, сетевой периметр, хранение данных), публикует их в registry с версионированием и явным deprecation-процессом, а для остальной логики даёт командам шаблоны и lint-правила, но не блокирует их решения; важно иметь обратную связь от команд-потребителей в roadmap платформенных модулей, иначе платформенная команда начинает навязывать решения, не подходящие под реальные кейсы использования.
Подводные камни
- Ошибка. Не выполняйте изменение без проверки плана, резервной копии состояния и понятного способа отката; для production согласуйте окно работ и исключите параллельные операции.