WAF-SOV-010 – Data Residency Policy Defined

Description

A documented data-residency policy MUST exist that explicitly defines allowed regions, data classifications, and storage rules for all data types: primary data, replicas, backups, logs, traces, metrics, and metadata. The policy MUST be version-controlled and technically reflected in IaC defaults and variable validation.

Rationale

Risk Description

Risk

Data may leave approved jurisdictions and violate regulatory requirements.

Risk

Vendor lock-in reduces future portability and sovereignty options.

Requirements

  • A documented data-residency policy MUST exist that explicitly defines allowed regions, data classifications, and storage rules for all data types: primary data, replicas, backups, logs, traces, metrics, and metadata.

  • The policy MUST be version-controlled and technically reflected in IaC defaults and variable validation.

Implementation Guidance

  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.

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-sov-010.tf.any.resource-tag-data-residency

Engine: terraform | Provider: any | Automated: Yes

Data resources must carry 'data-residency' tag

Primary data resources (object storage, relational DB, NoSQL, data warehouses) must be tagged with 'data-residency' to indicate the jurisdiction constraint for that resource.

Scope: resource β€” aws_s3_bucket, aws_rds_instance, aws_rds_cluster, aws_dynamodb_table, aws_elasticache_cluster, aws_elasticsearch_domain, aws_opensearch_domain, azurerm_storage_account, azurerm_sql_database, azurerm_cosmosdb_account, google_storage_bucket, google_sql_database_instance, google_bigtable_instance, stackit_container_registry, stackit_mysql_instance, stackit_opensearch_instance, stackit_redis_instance, stackit_secret_manager, ovh_cloud_project, ovh_vps, ovh_dedicated_server, ovh_ip_loadbalancing, hcloud_server, hcloud_volume, hcloud_network, hcloud_load_balancer, hcloud_dns_record

Assertions: * tags key exists β€” Resource is missing the 'data-residency' tag required by sovereignty policy. * tags key exists β€” Resource is missing the 'data-class' tag required by data classification policy.

Remediation: Add 'data-residency' and 'data-class' tags to all data-storing resources. Acceptable values for 'data-residency': eu-only, de-only, ch-only, at-only, global-approved. Acceptable values for 'data-class': pii, health, financial, operational, public, restricted.

Compliant
# AWS
resource "aws_s3_bucket" "customer_data" {
  bucket = "acme-customer-data-prod"
  tags = {
    data-class      = "pii"
    data-residency  = "eu-only"
    environment     = "production"
    owner           = "platform-team"
  }
}

# Azure
resource "azurerm_storage_account" "customer_data" {
  name             = "acmestorage"
  resource_group_name = azurerm_resource_group.main.name
  location         = "germanywestcentral"
  tags = {
    data-class      = "pii"
    data-residency  = "eu-only"
    environment     = "production"
    owner           = "platform-team"
  }
}

# GCP
resource "google_storage_bucket" "customer_data" {
  name     = "acme-customer-data-prod"
  location = "europe-west3"
  labels = {
    data-class      = "pii"
    data-residency  = "eu-only"
    environment     = "production"
    owner           = "platform-team"
  }
}

# StackIT (SchwarzIT)
resource "stackit_container_registry" "customer_data" {
  project_id = stackit_project.main.id
  name       = "acme-registry"
  labels = {
    data-class      = "pii"
    data-residency  = "eu-only"
  }
}

# OVH
resource "ovh_cloud_project" "customer_data" {
  description = "Sovereign workload"
  tags        = ["data-residency=eu-only", "data-class=pii"]
}

# Hetzner Cloud
resource "hcloud_server" "customer_data" {
  name        = "sovereign-server"
  server_type = "cx22"
  location    = "fsn1"
  labels = {
    data-class      = "pii"
    data-residency  = "eu-only"
  }
}
Non-Compliant
resource "aws_s3_bucket" "customer_data" {
  bucket = "acme-customer-data-prod"
  # Missing data-residency and data-class tags
  tags = {
    environment = "production"
  }
}

waf-sov-010.tf.aws.provider-region-explicit

Engine: terraform | Provider: aws | Automated: Yes

AWS provider must have 'region' explicitly set

AWS provider blocks must have an explicit 'region' attribute set to prevent unintended deployments to non-sovereign regions via environment variable fallback.

Scope: provider β€” aws

Assertions: * region attribute exists β€” AWS provider block is missing explicit 'region' attribute. * region not empty β€” AWS provider 'region' must not be empty.

Remediation: Set 'region' explicitly in all AWS provider blocks. Use a variable with a validation block to restrict to approved regions.

Compliant
variable "aws_region" {
  type    = string
  default = "eu-central-1"
  validation {
    condition     = contains(["eu-central-1", "eu-west-1"], var.aws_region)
    error_message = "Region must be in approved sovereign EU list."
  }
}
provider "aws" {
  region = var.aws_region
}
Non-Compliant
provider "aws" {
  # No region set – defaults to AWS_DEFAULT_REGION env var
}

waf-sov-010.tf.azurerm.location-explicit

Engine: terraform | Provider: azurerm | Automated: Yes

Azure resource groups must have 'location' explicitly set

Azure resource groups (and key resources) must have an explicit 'location' attribute rather than inheriting from parent or relying on environment defaults.

Scope: resource β€” azurerm_resource_group, azurerm_storage_account, azurerm_sql_server, azurerm_kubernetes_cluster

Assertions: * location attribute exists β€” Azure resource is missing explicit 'location' attribute. * location not empty β€” Azure resource 'location' must not be empty.

Remediation: Set 'location' explicitly on all Azure resources. Use a variable with a validation block to restrict to approved Azure regions.

Compliant
variable "azure_location" {
  type    = string
  default = "germanywestcentral"
  validation {
    condition     = contains(["germanywestcentral", "westeurope", "northeurope"], var.azure_location)
    error_message = "Location must be an approved sovereign Azure region."
  }
}
resource "azurerm_resource_group" "main" {
  name     = "rg-sovereign-prod"
  location = var.azure_location
}
Non-Compliant
resource "azurerm_resource_group" "main" {
  name     = "rg-sovereign-prod"
  # No location set
}

waf-sov-010.tf.google.region-explicit

Engine: terraform | Provider: google | Automated: Yes

GCP resources must have 'region' or 'location' explicitly set

GCP resources must have 'region' or 'location' explicitly set to prevent unintended deployment to non-sovereign regions via provider-level defaults.

Scope: resource β€” google_storage_bucket, google_sql_database_instance, google_compute_instance, google_container_cluster, google_bigquery_dataset

Assertions: * location attribute exists or fallback (fallback region) β€” GCP resource is missing explicit 'region' or 'location' attribute.

Remediation: Set 'location' or 'region' explicitly on all GCP data resources. Use a variable with validation to restrict to approved GCP regions.

Compliant
variable "gcp_region" {
  type    = string
  default = "europe-west3"
  validation {
    condition     = startswith(var.gcp_region, "europe-")
    error_message = "Only European GCP regions are approved for sovereign workloads."
  }
}
resource "google_storage_bucket" "data" {
  name     = "acme-sovereign-data"
  location = var.gcp_region
}
Non-Compliant
resource "google_storage_bucket" "data" {
  name = "acme-sovereign-data"
  # No location set – defaults to provider default
}

waf-sov-010.tf.stackit.region-explicit

Engine: terraform | Provider: stackit | Automated: Yes

STACKIT resources must have an explicit region

STACKIT resources must have an explicit region. This check validates the Terraform configuration uses the expected resource and attribute.

Scope: resource β€” stackit_project, stackit_server, stackit_ske_cluster, stackit_object_storage_bucket

Assertions: * region attribute exists β€” STACKIT resource must have an explicit region.

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 "stackit_project" "example" {
  # required attribute/tag/label present
}
Non-Compliant
# Example resource does not meet the requirement.
resource "stackit_project" "example" {
  # Missing required attribute/tag/label
}

waf-sov-010.config.ovh.data-residency-policy

Engine: config | Provider: ovh | Automated: Yes

OVHcloud data-residency policy must be documented

OVHcloud public-cloud resources do not expose a native region attribute consistently across all resource types. A version-controlled policy document must therefore define the approved OVHcloud regions, data classifications, and implementation rules for sovereign workloads.

Scope: file β€” docs/sovereign/waf-sov-010-ovh-data-residency.md

Assertions: * content matches pattern (?i)MUST|SHALL|required|mandatory|documented|approved β€” OVHcloud data-residency policy file must contain a normative requirement statement.

Remediation: Create or update docs/sovereign/waf-sov-010-ovh-data-residency.md. State the approved OVHcloud regions, data-class handling, review cadence, and owner.

Compliant
docs/sovereign/waf-sov-010-ovh-data-residency.md:

- Approved OVHcloud regions are documented (e.g. DE-FRA, DE-EDE, EU-CY).
- Data-class and data-residency handling for OVHcloud is defined.
- Owner and review cadence are recorded.
Non-Compliant
# `docs/sovereign/waf-sov-010-ovh-data-residency.md` is missing
# WAF-SOV-010 violation: OVHcloud data-residency policy is not documented

waf-sov-010.tf.hcloud.location-explicit

Engine: terraform | Provider: hcloud | Automated: Yes

Hetzner Cloud resources must have an explicit location

Hetzner Cloud resources must have an explicit location. This check validates the Terraform configuration uses the expected resource and attribute.

Scope: resource β€” hcloud_server, hcloud_volume, hcloud_network, hcloud_load_balancer

Assertions: * location attribute exists β€” Hetzner Cloud resource must have an explicit location.

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 "hcloud_server" "example" {
  # required attribute/tag/label present
}
Non-Compliant
# Example resource does not meet the requirement.
resource "hcloud_server" "example" {
  # Missing required attribute/tag/label
}

Evidence

Kind Required Description

iac

Required

Terraform configuration implementing the control.

governance

Required

Policy, ADR, or standard documenting the requirement.

config

Optional

Cloud console or API output confirming runtime state.

Framework Controls

ISO 27001:2022

* A.5.12 – Classification of information * A.5.33 – Protection of records * A.8.10 – Information deletion * A.5.34 – Privacy and PII protection

ISO 27017

* CLD.6.3 – Shared roles and responsibilities for cloud * CLD.8.1 – Asset management for cloud customer data

ISO 27018

* A.1 – Consent and choice for PII * A.11 – Information transfer and disclosure of PII

GDPR

* Art. 30 – Records of processing activities * Art. 44 – General principle for transfers * Art. 46 – Transfers subject to appropriate safeguards * Art. 5(1)(a) – Lawfulness, fairness and transparency

UK GDPR

* Art. 30 – Records of processing * Art. 44-46 – International transfers

NIS2

* Art. 21(2)(d) – Supply chain and data location requirements

DORA

* Art. 28 – ICT third-party risk management

EUCS (ENISA)

* DSP-01 – Data classification and handling * GEO-01 – Data geographic location

BSI C5:2020

* AM-04 – Data classification and handling * PI-02 – Location of data processing

BSI C3A:2026

* Dimension 4 – Data: Data residency, data classification

ANSSI SecNumCloud

* Domain – Data localization and sovereignty

BIO

* BIO – Dataclassificatie en residentie

ENS High

* mp.info.1 – Datos de carΓ‘cter personal * mp.info.6 – Limpieza de documentos

NIST SP 800-53

* RA-2 – Security Categorization * AC-21 – Information Sharing * SC-7 – Boundary Protection

FedRAMP

* RA-2, SC-7 (Moderate/High baseline – data location)

MAS TRM

* Ch.13 – Cloud computing – data location

ISMAP

* Data location and residency requirements