WAF-REL-030 โ Multi-AZ High Availability Deployment
Description
All production stateful and stateless workloads MUST be deployed across a minimum of two Availability Zones (AZs). Databases, caches, and message queues MUST use multi-AZ configurations with automatic failover. Load balancers MUST span at least two AZs. Auto-scaling groups and Kubernetes clusters MUST distribute nodes and pods across AZs. Single-AZ deployments in production are not acceptable without explicit documented risk acceptance.
Rationale
| Risk | Description |
|---|---|
Risk |
Single-AZ deployments become completely unreachable during zone failures. |
Risk |
Missing multi-AZ strategies violate availability requirements for critical workloads. |
Requirements
-
All production stateful and stateless workloads MUST be deployed across a minimum of two Availability Zones (AZs).
-
Databases, caches, and message queues MUST use multi-AZ configurations with automatic failover.
-
Load balancers MUST span at least two AZs.
-
Auto-scaling groups and Kubernetes clusters MUST distribute nodes and pods across AZs.
Implementation Guidance
-
Implement the required configuration as code in Terraform or equivalent IaC.
-
Add an automated check to CI/CD so deviations are caught before deployment.
Maturity Levels
| Level | Name | Criteria |
|---|---|---|
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-rel-030.tf.aws.rds-multi-az
Engine: terraform | Provider: aws | Automated: Yes
AWS RDS instances must have Multi-AZ enabled
Production RDS instances must have multi_az = true to enable automatic failover to a standby replica in a different AZ on failure.
Scope: resource โ aws_db_instance
Assertions:
* multi_az is true โ RDS instance must have multi_az = true for production availability.
Remediation: Set multi_az = true on the aws_db_instance resource. This creates a synchronous standby replica in a different AZ. Failover is automatic and typically completes within 1-2 minutes.
resource "aws_db_instance" "main" {
identifier = "payment-db-prod"
engine = "postgres"
engine_version = "15.4"
instance_class = "db.t3.medium"
allocated_storage = 100
multi_az = true
db_subnet_group_name = aws_db_subnet_group.main.name
tags = var.mandatory_tags
}
resource "aws_db_instance" "main" {
identifier = "payment-db-prod"
engine = "postgres"
instance_class = "db.t3.medium"
allocated_storage = 100
multi_az = false # WAF-REL-030 violation
db_subnet_group_name = aws_db_subnet_group.main.name
}
waf-rel-030.tf.aws.autoscaling-multi-az
Engine: terraform | Provider: aws | Automated: Yes
AWS Auto Scaling Group must span at least 2 availability zones
Auto Scaling Groups in production must reference subnets in at least 2 different AZs to ensure compute redundancy.
Scope: resource โ aws_autoscaling_group
Assertions:
* vpc_zone_identifier not empty โ Auto Scaling Group must specify VPC subnets across multiple AZs.
* min_size greater than or equal 1 โ Auto Scaling Group minimum size must be at least 2 for HA.
Remediation: Set vpc_zone_identifier to a list containing subnet IDs from at least 2 AZs. Increase min_size to 2 to ensure at least one instance per AZ during failures.
resource "aws_autoscaling_group" "api" {
name = "payment-api-asg"
min_size = 2
max_size = 10
desired_capacity = 2
vpc_zone_identifier = [
aws_subnet.private_az1.id,
aws_subnet.private_az2.id,
]
}
resource "aws_autoscaling_group" "api" {
name = "payment-api-asg"
min_size = 1
max_size = 4
desired_capacity = 1
vpc_zone_identifier = [aws_subnet.private_az1.id]
# Single AZ โ WAF-REL-030 violation
}
waf-rel-030.tf.azurerm.db-availability-zone
Engine: terraform | Provider: azurerm | Automated: Yes
Azure database services must use zone-redundant configuration
Azure SQL Database, PostgreSQL Flexible Server, and other managed databases must use zone_redundant = true or configure standby_availability_zone.
Scope: resource โ azurerm_postgresql_flexible_server, azurerm_mssql_database, azurerm_mysql_flexible_server
Assertions:
* high_availability attribute exists โ Database must define high_availability block with zone-redundant configuration.
Remediation: Add a high_availability block with mode = "ZoneRedundant" and configure standby_availability_zone to a different zone than the primary.
resource "azurerm_postgresql_flexible_server" "main" {
name = "payment-db-prod"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
version = "15"
sku_name = "GP_Standard_D2s_v3"
availability_zone = "1"
high_availability {
mode = "ZoneRedundant"
standby_availability_zone = "2"
}
}
resource "azurerm_postgresql_flexible_server" "main" {
name = "payment-db-prod"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
version = "15"
sku_name = "GP_Standard_D2s_v3"
# Missing: high_availability โ single zone deployment
}
waf-rel-030.tf.google.sql-availability-type
Engine: terraform | Provider: google | Automated: Yes
GCP Cloud SQL must use REGIONAL availability type
GCP Cloud SQL instances must use availability_type = REGIONAL for automatic failover across zones in the same region.
Scope: resource โ google_sql_database_instance
Assertions:
* settings.availability_type equals REGIONAL โ Cloud SQL must use REGIONAL availability type, not ZONAL.
Remediation: Set availability_type = "REGIONAL" in the settings block of google_sql_database_instance to enable automatic failover to a different zone.
resource "google_sql_database_instance" "main" {
name = "payment-db-prod"
database_version = "POSTGRES_15"
settings {
tier = "db-custom-2-7680"
availability_type = "REGIONAL"
backup_configuration {
enabled = true
start_time = "02:00"
}
}
}
resource "google_sql_database_instance" "main" {
name = "payment-db-prod"
database_version = "POSTGRES_15"
settings {
tier = "db-custom-2-7680"
availability_type = "ZONAL" # WAF-REL-030 violation
}
}
waf-rel-030.tf.stackit.ske-backup-enabled
Engine: terraform | Provider: stackit | Automated: Yes
STACKIT managed services must have backup configured
STACKIT managed services must have backup configured. This check validates the Terraform configuration uses the expected resource and attribute.
Scope: resource โ stackit_postgres_instance, stackit_mysql_instance, stackit_mariadb_instance, stackit_ske_cluster
Assertions:
* backup_schedule attribute exists โ STACKIT managed service must define a backup schedule.
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.
# Example resource meets the requirement.
resource "stackit_postgres_instance" "example" {
# required attribute/tag/label present
}
# Example resource does not meet the requirement.
resource "stackit_postgres_instance" "example" {
# Missing required attribute/tag/label
}
waf-rel-030.tf.ovh.database-backup-enabled
Engine: terraform | Provider: ovh | Automated: Yes
OVHcloud managed databases must have backup configured
OVHcloud managed databases must have backup configured. This check validates the Terraform configuration uses the expected resource and attribute.
Scope: resource โ ovh_cloud_project_database_postgresql, ovh_cloud_project_database_mysql
Assertions:
* backup_regions attribute exists โ OVHcloud managed database must configure backup regions.
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.
# Example resource meets the requirement.
resource "ovh_cloud_project_database_postgresql" "example" {
# required attribute/tag/label present
}
# Example resource does not meet the requirement.
resource "ovh_cloud_project_database_postgresql" "example" {
# Missing required attribute/tag/label
}
waf-rel-030.tf.hcloud.server-backup-plan
Engine: terraform | Provider: hcloud | Automated: Yes
Hetzner Cloud servers must have a backup plan configured
Hetzner Cloud servers must have a backup plan configured. This check validates the Terraform configuration uses the expected resource and attribute.
Scope: resource โ hcloud_server, hcloud_volume
Assertions:
* labels.backup-plan key exists โ Hetzner Cloud server/volume must declare a backup-plan label.
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.
# Example resource meets the requirement.
resource "hcloud_server" "example" {
# required attribute/tag/label present
}
# Example resource does not meet the requirement.
resource "hcloud_server" "example" {
# Missing required attribute/tag/label
}
Evidence
| Kind | Required | Description |
|---|---|---|
|
Required |
Terraform configuration implementing the control. |
|
Required |
Policy, ADR, or standard documenting the requirement. |
|
Optional |
Cloud console or API output confirming runtime state. |