WAF-OPS-020 – Infrastructure as Code Enforced

Beschreibung

All cloud infrastructure MUST be defined as code using an IaC tool (Terraform, Pulumi, AWS CDK, or equivalent). Manual resource creation through cloud consoles MUST be prohibited for all production and staging environments. Infrastructure definitions MUST be version-controlled, reviewed through pull requests, and deployed exclusively through CI/CD pipelines.

Begründung

Risiko Beschreibung

Risk

Manually managed infrastructure drifts from documentation and is hard to restore.

Risk

Missing IaC practices prevent versioning, peer review, and disaster recovery.

Anforderungen

  • All cloud infrastructure MUST be defined as code using an IaC tool (Terraform, Pulumi, AWS CDK, or equivalent).

  • Manual resource creation through cloud consoles MUST be prohibited for all production and staging environments.

  • Infrastructure definitions MUST be version-controlled, reviewed through pull requests, and deployed exclusively through CI/CD pipelines.

Implementierungsleitfaden

  1. Document the requirement in a version-controlled policy or standard.

  2. Assign ownership and establish a review cadence (e.g., quarterly).

  3. Implement the required configuration as code in Terraform or equivalent IaC.

Reifegrade

Level Name Kriterien

L1

Ad hoc – control not consistently applied

* Manual evidence exists for some workloads, but coverage is unknown.

L2

Standardised – policy defined and communicated

* A version-controlled policy, ADR, or standard describes the requirement.

L3

Integrated – automated validation in CI/CD

* Terraform checks run automatically on pull requests and report results.

L4

Auditable – metrics and exceptions tracked

* Control coverage, failures, and approved exceptions are reported quarterly.

L5

Optimised – continuous improvement based on data

* Control effectiveness is measured and iteratively improved.

Checks

waf-ops-020.tf.aws.s3-terraform-state-backend

Engine: terraform | Provider: aws | Automated: Ja

Terraform state must use S3 backend with DynamoDB locking

Terraform remote state MUST be stored in an S3 bucket with versioning enabled and a DynamoDB table for state locking. This prevents concurrent state modifications and enables state history and rollback.

Scope: terraform

Assertions: * backend attribute exists — Terraform must configure a remote backend (not local state).

Remediation: Configure the S3 backend in your Terraform root module’s terraform block. Create the S3 bucket with versioning enabled and a DynamoDB table for locking.

Compliant
terraform {
  backend "s3" {
    bucket         = "my-org-terraform-state"
    key            = "payment-service/production/terraform.tfstate"
    region         = "eu-central-1"
    dynamodb_table = "terraform-state-lock"
    encrypt        = true
  }
}

resource "aws_s3_bucket_versioning" "state" {
  bucket = "my-org-terraform-state"
  versioning_configuration {
    status = "Enabled"
  }
}
Non-Compliant
terraform {
  # No backend configured – local state only
  # WAF-OPS-020 Violation: no remote state
}

waf-ops-020.tf.aws.s3-state-bucket-versioning

Engine: terraform | Provider: aws | Automated: Ja

Terraform state S3 bucket must have versioning enabled

The S3 bucket used for Terraform remote state MUST have versioning enabled to allow state history, audit, and rollback capabilities.

Scope: resource — aws_s3_bucket_versioning

Assertions: * versioning_configuration.status equals Enabled — S3 state bucket versioning must be Enabled.

Remediation: Add an aws_s3_bucket_versioning resource with status = "Enabled" for your Terraform state bucket.

Compliant
resource "aws_s3_bucket_versioning" "state" {
  bucket = aws_s3_bucket.terraform_state.id
  versioning_configuration {
    status = "Enabled"
  }
}
Non-Compliant
resource "aws_s3_bucket_versioning" "state" {
  bucket = aws_s3_bucket.terraform_state.id
  versioning_configuration {
    status = "Suspended"
  }
}

waf-ops-020.tf.azurerm.terraform-state-storage

Engine: terraform | Provider: azurerm | Automated: Ja

Azure Terraform state must use Azure Storage Account backend

Terraform state for Azure deployments MUST be stored in an Azure Storage Account with blob versioning enabled. This ensures state durability and collaboration.

Scope: resource — azurerm_storage_account

Assertions: * blob_properties attribute exists — Storage account must have blob_properties configured.

Remediation: Configure blob versioning on the Storage Account used for Terraform state.

Compliant
resource "azurerm_storage_account" "terraform_state" {
  name                     = "myorgtfstate"
  resource_group_name      = azurerm_resource_group.tfstate.name
  location                 = var.location
  account_tier             = "Standard"
  account_replication_type = "GRS"

  blob_properties {
    versioning_enabled = true
  }
}
Non-Compliant
resource "azurerm_storage_account" "terraform_state" {
  name                     = "myorgtfstate"
  resource_group_name      = azurerm_resource_group.tfstate.name
  location                 = var.location
  account_tier             = "Standard"
  account_replication_type = "LRS"
  # No blob versioning – WAF-OPS-020 Violation
}

waf-ops-020.tf.google.gcs-state-backend

Engine: terraform | Provider: google | Automated: Ja

GCP Terraform state must use Cloud Storage backend with versioning

Terraform state for GCP deployments MUST be stored in a google_storage_bucket configured as a backend with uniform_bucket_level_access and versioning enabled. This prevents concurrent state modifications and provides state history.

Scope: resource — google_storage_bucket

Assertions: * versioning attribute exists — GCP Terraform state bucket must have versioning enabled.

Remediation: Configure the gcs backend in the Terraform root module and enable versioning on the state bucket. Use a dedicated project for state storage with restricted IAM access.

Compliant
terraform {
  backend "gcs" {
    bucket = "my-org-terraform-state"
    prefix = "payment-service/production"
  }
}

resource "google_storage_bucket" "terraform_state" {
  name          = "my-org-terraform-state"
  location      = "EU"
  storage_class = "STANDARD"

  versioning {
    enabled = true
  }

  uniform_bucket_level_access = true
}
Non-Compliant
terraform {
  # No backend configured – local state only
  # WAF-OPS-020 Violation: no remote state
}

waf-ops-020.tf.stackit.provider-version-constrained

Engine: terraform | Provider: stackit | Automated: Ja

STACKIT Terraform provider must have version constraint

STACKIT Terraform provider must have version constraint. This check validates the Terraform configuration uses the expected resource and attribute.

Scope: terraform

Assertions: * required_providers.stackit.source attribute exists — STACKIT provider source must be declared. * required_providers.stackit.version attribute exists — STACKIT provider version must be constrained.

Remediation: Review the Terraform configuration for the listed resource types and ensure the required attribute or tag is present. See the control narrative for provider-specific guidance.

Compliant
# Example resource meets the requirement.
resource "provider" "example" {
  # required attribute/tag/label present
}
Non-Compliant
# Example resource does not meet the requirement.
resource "provider" "example" {
  # Missing required attribute/tag/label
}

waf-ops-020.tf.ovh.provider-version-constrained

Engine: terraform | Provider: ovh | Automated: Ja

OVHcloud Terraform provider must have version constraint

OVHcloud Terraform provider must have version constraint. This check validates the Terraform configuration uses the expected resource and attribute.

Scope: terraform

Assertions: * required_providers.ovh.source attribute exists — OVHcloud provider source must be declared. * required_providers.ovh.version attribute exists — OVHcloud provider version must be constrained.

Remediation: Review the Terraform configuration for the listed resource types and ensure the required attribute or tag is present. See the control narrative for provider-specific guidance.

Compliant
# Example resource meets the requirement.
resource "provider" "example" {
  # required attribute/tag/label present
}
Non-Compliant
# Example resource does not meet the requirement.
resource "provider" "example" {
  # Missing required attribute/tag/label
}

waf-ops-020.tf.hcloud.provider-version-constrained

Engine: terraform | Provider: hcloud | Automated: Ja

Hetzner Cloud Terraform provider must have version constraint

Hetzner Cloud Terraform provider must have version constraint. This check validates the Terraform configuration uses the expected resource and attribute.

Scope: terraform

Assertions: * required_providers.hcloud.source attribute exists — Hetzner Cloud provider source must be declared. * required_providers.hcloud.version attribute exists — Hetzner Cloud provider version must be constrained.

Remediation: Review the Terraform configuration for the listed resource types and ensure the required attribute or tag is present. See the control narrative for provider-specific guidance.

Compliant
# Example resource meets the requirement.
resource "provider" "example" {
  # required attribute/tag/label present
}
Non-Compliant
# Example resource does not meet the requirement.
resource "provider" "example" {
  # Missing required attribute/tag/label
}

Evidenz

Art Erforderlich Beschreibung

iac

Erforderlich

Terraform configuration implementing the control.

governance

Erforderlich

Policy, ADR, or standard documenting the requirement.

config

Optional

Cloud console or API output confirming runtime state.

Verwandte Controls

Framework Controls

ISO/IEC 20000-1

* Configuration management * Change and deployment management

ITIL 4

* Infrastructure and platform management * Configuration management

CIS Controls v8

* CIS-4.6: Securely Manage Enterprise Assets and Software