Тестирование Terraform
Зачем тестировать инфраструктурный код
IaC даёт уверенность, а не гарантию — если модуль прошёл plan и apply в test-аккаунте, высока вероятность, что тот же код сработает в prod. Тесты сокращают страх перед изменениями и позволяют чаще выкатывать.
Пирамида тестов IaC
┌─────────────┐
│ E2E / live │ curl ALB, полный стек в test-аккаунте
├─────────────┤
│ Integration │ Terratest: apply + assert + destroy
├─────────────┤
│ Static/unit │ validate, fmt, tflint, checkov, plan
└─────────────┘
| Уровень | Что проверяет | Стоимость |
|---|---|---|
| Static | Синтаксис, стиль, policy (открытые SG, публичный S3) | Секунды, без облака |
| Unit (plan) | terraform plan с mock tfvars — ожидаемые +/−/~/ destroy | Минуты, read-only или test account |
| Integration | Реальный apply модуля, проверка outputs/API | Деньги + минуты |
| E2E | Полный live-стек + HTTP/health checks | Максимум |
Статический уровень (CI на каждый PR)
terraform fmt -check -recursive
terraform init -backend=false
terraform validate
tflint --recursive
checkov -d .
terraform plan -input=false -var-file=test.tfvars -out=tfplan
Разбор:
-backend=falseна validate — без доступа к prod state.checkov/tflint— policy и устаревшие конструкции до apply.- Артефакт
tfplan— тот же план, что ревьюит человек в PR.
См. также эксплуатация CI/CD — IaC.
Ручное тестирование
После apply модуля ALB (из практического пути):
terraform output alb_dns_name
curl "http://$(terraform output -raw alb_dns_name)/"
Ожидаемый ответ — 200 и тело страницы. После проверки — terraform destroy в test-аккаунте, иначе останутся платные ресурсы.
Terratest (интеграционные тесты на Go)
Terratest поднимает реальную инфраструктуру в изолированном каталоге, проверяет outputs и всегда вызывает destroy в defer:
func TestWebserverCluster(t *testing.T) {
terraformOptions := &terraform.Options{
TerraformDir: "../modules/services/webserver-cluster",
Vars: map[string]interface{}{
"cluster_name": "test-" + random.UniqueId(),
},
}
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
url := terraform.Output(t, terraformOptions, "alb_url")
http_helper.HttpGetWithRetry(t, url, nil, 200, "Hello", 30, 5*time.Second)
}
Разбор:
defer Destroy— обязательная очистка даже при падении assert.- Уникальный
cluster_name— параллельные тесты не конфликтуют в одном аккаунте. - Go + AWS credentials на CI-runner; для команд без Go достаточно static + ручного staging.
Terraform 1.6+ — terraform test
Встроенные mock-провайдеры и .tftest.hcl позволяют unit-тестировать модули без облака (см. документацию HashiCorp). Подходит для проверки логики locals и условных ресурсов; E2E по-прежнему требует test-аккаунта.
Практические правила
- Отдельный AWS account / subscription для CI-тестов.
- Tag
Environment=testи auto-cleanup по расписанию на случай упавшего job. - Не гонять integration-тесты на каждый commit в main — nightly или перед release module version.
- Секреты в test — те же паттерны, что в аутентификации CI/CD.
См. также
- Terraform в команде — когда apply в prod.
- GitHub Actions — job
terraform planв workflow. - Модули и структура репозитория — что тестировать.