WAF++ WAF++
Back to WAF++ Homepage

Control Schema Reference

WAF++ controls are machine-readable YAML files. They define what is checked, how it is checked, and what evidence is expected. This page documents the complete schema structure for control authors and contributors.

File Conventions

  • Storage location: modules/controls/controls/

  • Filename: WAF-{PILLAR}-{NNN}.yml (e.g. WAF-SOV-010.yml, WAF-COST-040.yml)

  • Encoding: UTF-8

  • Language: English (control files), independent of the language of narrative pages

ID Schema

WAF - \{PILLAR} - \{NNN}
 │       │         │
 │       │         └── Three-digit number (010, 020, ..., 100)
 │       └──────────── Pillar abbreviation (SOV, COST, SEC, REL, OPS, PERF, SUS)
 └──────────────────── Prefix: Well Architected Framework

Top-Level Fields

id:          # string  – unique control ID (WAF-SOV-010)
title:       # string  – short, active title ("Data Residency Policy Defined")
pillar:      # string  – pillar name (sovereign | cost | security | reliability | operations | architecture | governance)
status:      # string  – active | deprecated | draft
severity:    # string  – critical | high | medium | low
category:    # string  – categorization within the pillar (e.g. "data-residency", "cost-allocation")
type:        # list    – [governance, configuration, automation, process, architecture]
tags:        # list    – free tags for search and filtering

description: # >       – What this control requires (normative, e.g. "MUST carry tags")
rationale:   # >       – Why this control exists (risk justification)
threat:      # list    – Concrete threats when not compliant

regulatory_mapping:    # list – mapping to compliance frameworks (see below)
implementation_guidance: # list – concrete implementation steps (checklist)
maturity_levels:       # map  – 5 maturity levels (level_1 to level_5)
evidence:              # map  – required + optional evidence types

checks:      # list    – automated and manual check steps (see below)
references:  # map     – links to narrative pages and related controls

severity

Value Meaning

critical

Immediate security or compliance risk. No deploy without remediation.

high

Significant risk. Short-term remediation required (max. 2 weeks).

medium

Improvement needed. Address medium-term (next quarter).

low

Best-practice deviation. Fix opportunistically.

type

A control can have multiple types:

Value Meaning

governance

Organizational requirement (policies, processes, documentation)

configuration

Technical configuration in IaC or cloud services

automation

Requires automated checking or enforcement

process

Recurring process (e.g. monthly review)

architecture

Architectural decision at system or platform level


regulatory_mapping

Maps the control to one or more compliance frameworks.

regulatory_mapping:
  - framework: "GDPR"
    controls:
      - "Art. 44 – General principle for transfers"
      - "Art. 46 – Transfers subject to appropriate safeguards"
  - framework: "BSI C5:2020"
    controls:
      - "OPS-04 – Data management"
  - framework: "ISO 27001:2022"
    controls:
      - "A.5.12 – Classification of information"
  - framework: "EUCS (ENISA)"
    controls:
      - "DSP-01 – Data classification"
  - framework: "GAIA-X"
    controls:
      - "Sovereign Cloud – Data location requirements"

Supported frameworks: GDPR, BSI C5:2020, ISO 27001:2022, EUCS (ENISA), GAIA-X, FinOps Foundation, ISO 20000, Internal Governance.


maturity_levels

Five maturity levels with description and measurable criteria.

maturity_levels:
  level_1:
    description: "Ad hoc – no structured controls"
    criteria:
      - "First measurable condition at level 1"
  level_2:
    description: "Standardized – manually implemented"
    criteria:
      - "..."
  level_3:
    description: "Integrated – automated and checked in CI"
    criteria:
      - "..."
  level_4:
    description: "Sovereign – fully auditable"
    criteria:
      - "..."
  level_5:
    description: "Optimized – continuous improvement"
    criteria:
      - "..."

Each level builds on the previous one. Level 3+ generally requires automated checking (wafpass or equivalent).


evidence

Defines the evidence types expected during an audit.

evidence:
  required:
    - kind: "governance"
      description: "Policy document, version-controlled."
    - kind: "iac"
      description: "Terraform configuration implementing the requirement."
  optional:
    - kind: "architecture"
      description: "Architecture diagram with data flows."
    - kind: "process"
      description: "Minutes of the last review."
    - kind: "config"
      description: "Export from cloud console or inventory tool."

Allowed kind Values

kind Typical evidence

governance

Policy documents, ADRs, decision registers (version-controlled in Git)

iac

Terraform configuration, Helm charts, Kustomize manifests

architecture

Architecture diagrams, data flow diagrams, network topologies

process

Meeting minutes, review reports, audit logs, calendar entries

config

Cloud console screenshots, CLI outputs, inventory exports, API responses


checks

A list of check steps. Each check is either automated (executable by wafpass) or manual.

checks:
  - id: "waf-sov-010.tf.aws.provider-region-explicit"   # unique check ID
    engine: "terraform"                                  # terraform | manual | api
    provider: "aws"                                      # aws | azurerm | google | any
    automated: true                                      # true | false
    severity: "critical"                                 # critical | high | medium | low
    title: "AWS provider must have 'region' explicitly set"
    description: >
      Brief description of what this check verifies and why.
    scope:
      block_type: "provider"                             # resource | provider | variable | terraform | module
      provider_name: "aws"                               # only for block_type: provider
      resource_types:                                    # only for block_type: resource
        - "aws_s3_bucket"
    assertions:
      - attribute: "region"
        op: "attribute_exists"
        message: "AWS provider block is missing explicit 'region' attribute."
    on_fail: "violation"
    remediation: >
      Set 'region' explicitly in all AWS provider blocks.
    example:
      compliant: |
        provider "aws" {
          region = var.aws_region
        }
      non_compliant: |
        provider "aws" {
          # No region set
        }

ID Convention for Checks

{control-id}.{engine}.{provider}.{short-description}
      │           │        │              │
      │           │        │              └── kebab-case, max 40 characters
      │           │        └─────────────── aws | azurerm | google | any | manual
      │           └──────────────────────── terraform | manual | api
      └──────────────────────────────────── e.g. waf-sov-010 (lowercase)

Example: waf-cost-010.tf.aws.compute-mandatory-tags

engine

Value Meaning

terraform

Check is run against HCL2 Terraform files (wafpass)

manual

Manual review required; automated: false is always set

api

Check requires live query of cloud API (wafpass does not yet support this)

scope

Defines which Terraform blocks are checked:

block_type Additional fields Description

resource

resource_types: [list]

Terraform resource blocks of the specified type (e.g. aws_s3_bucket)

provider

provider_name: string

Terraform provider blocks (e.g. provider "aws" {})

variable

All variable blocks in the configuration

terraform

The terraform block (required_providers, required_version)

module

All module blocks


assertions

Each assertion defines a single condition that a Terraform block must satisfy. All assertions of a check must pass for the check to be considered PASS.

assertions:
  - attribute: "tags"            # attribute path (dot-notation for nested values)
    op: "key_exists"             # operator (see table below)
    key: "cost-center"           # for key_exists
    expected: "COST"             # for equals, not_equals, in, not_in, greater_than_or_equal, less_than_or_equal
    pattern: "^eu-"              # for matches, not_matches (regex)
    fallback_attribute: "region" # for attribute_exists_or_fallback
    message: "Resource is missing the mandatory 'cost-center' tag."

Assertion Fields

Field Required Description

attribute

Yes

Attribute path in the Terraform block. Dot-notation for nested values: tags.cost-center, logging_config.bucket.

op

Yes

Operator (see table below).

key

Only for key_exists

Key name within a map (e.g. tag key).

expected

Operator-dependent

Expected value. For in/not_in a list. For greater_than_or_equal/less_than_or_equal a number.

pattern

Only for matches/not_matches

Regular expression (Python re syntax).

fallback_attribute

Only for attribute_exists_or_fallback

Alternative attribute checked when the primary is missing.

message

No

Error message on FAIL. Displayed in wafpass output. If omitted, the engine generates a default message.

Operator Reference

Operator Required fields Checks

attribute_exists

attribute

Attribute present and not null

attribute_exists_or_fallback

attribute, fallback_attribute

Primary or fallback attribute present

not_empty

attribute

Attribute present, not null, not "" / [] / {}

equals

attribute, expected

Attribute value == expected

not_equals

attribute, expected

Attribute value != expected

in

attribute, expected (list)

Attribute value is in the allowed list

not_in

attribute, expected (list)

Attribute value is not in the prohibited list

is_true

attribute

Attribute value is true (also as string)

is_false

attribute

Attribute value is false (also as string)

greater_than_or_equal

attribute, expected (number)

Numeric attribute value >= expected

less_than_or_equal

attribute, expected (number)

Numeric attribute value ⇐ expected

matches

attribute, pattern

Attribute value matches the regex pattern

not_matches

attribute, pattern

Attribute value does not match the regex pattern

key_exists

attribute, key

Key key exists in map attribute (e.g. in tags)

block_exists

Terraform block of the specified type exists (scope match sufficient)

has_associated_resource

attribute

Another resource references this block (e.g. flow log references VPC)

not_contains

attribute, expected

Attribute value does not contain the substring expected


references

Links the control to the narrative documentation.

references:
  narrative: "modules/pillar-sovereign/pages/controls.adoc#WAF-SOV-010"
  best_practice: "modules/pillar-sovereign/pages/best-practices/bp-data-residency.adoc"
  related_controls:
    - "WAF-SOV-020"
    - "WAF-SOV-030"

Complete Example Control

The following example shows a minimally complete control containing all required fields and directly evaluable by wafpass:

id: WAF-COST-010
title: "Cost Allocation Tagging Enforced"
pillar: "cost"
status: "active"
severity: "high"
category: "cost-allocation"
type: ['governance', 'configuration']
tags: ['tagging', 'cost-allocation', 'finops']

description: >
  All cloud resources MUST carry mandatory cost allocation tags:
  cost-center, owner, environment, and workload.

rationale: >
  Without consistent tagging, cloud costs cannot be allocated to workloads or teams.
  Manual tagging is unreliable; IaC-enforced tags are the only sustainable approach.

threat:
  - "Unallocated cloud spend making workload cost ownership impossible"
  - "Budget overruns not attributable to specific teams"

regulatory_mapping:
  - framework: "FinOps Foundation"
    controls:
      - "Inform Phase – Cost Allocation and Showback"

implementation_guidance:
  - "Define a mandatory tag taxonomy: cost-center, owner, environment, workload."
  - "Use a shared Terraform module to output the complete tag map."
  - "Enforce tagging in CI/CD with wafpass --fail-on critical."

maturity_levels:
  level_1:
    description: "Ad-hoc tagging, no standard taxonomy"
    criteria:
      - "Some resources are tagged but no consistent taxonomy exists"
  level_2:
    description: "Taxonomy documented, manual tagging"
    criteria:
      - "Mandatory tag taxonomy is documented and version-controlled"
  level_3:
    description: "IaC-enforced with CI gate"
    criteria:
      - "Shared mandatory-tags module is used by all IaC deployments"
      - "CI pipeline checks tag compliance on every PR"
  level_4:
    description: "Automated remediation and chargeback"
    criteria:
      - "Automated remediation tags untagged resources within 24 hours"
  level_5:
    description: "Full cost allocation with automated attribution"
    criteria:
      - "100% cost allocation to workloads and teams"

evidence:
  required:
    - kind: "governance"
      description: "Tag taxonomy document (version-controlled)."
    - kind: "iac"
      description: "Shared mandatory-tags Terraform module."
  optional:
    - kind: "process"
      description: "Monthly tagging compliance report."

checks:
  - id: "waf-cost-010.tf.aws.compute-mandatory-tags"
    engine: "terraform"
    provider: "aws"
    automated: true
    severity: "high"
    title: "AWS resources must have all mandatory cost allocation tags"
    description: >
      Compute and data resources must carry cost-center, owner, environment, and workload tags.
    scope:
      block_type: "resource"
      resource_types:
        - "aws_instance"
        - "aws_s3_bucket"
        - "aws_lambda_function"
    assertions:
      - attribute: "tags"
        op: "key_exists"
        key: "cost-center"
        message: "Resource is missing the mandatory 'cost-center' tag."
      - attribute: "tags"
        op: "key_exists"
        key: "owner"
        message: "Resource is missing the mandatory 'owner' tag."
      - attribute: "tags"
        op: "key_exists"
        key: "environment"
        message: "Resource is missing the mandatory 'environment' tag."
      - attribute: "tags"
        op: "key_exists"
        key: "workload"
        message: "Resource is missing the mandatory 'workload' tag."
    on_fail: "violation"
    remediation: >
      Use the shared mandatory-tags module. Set tags = module.tags.tags on every resource.
    example:
      compliant: |
        resource "aws_instance" "app" {
          ami           = data.aws_ami.ubuntu.id
          instance_type = "t3.medium"
          tags = {
            cost-center = "fintech-platform"
            owner       = "payments-team"
            environment = "production"
            workload    = "payment-service"
          }
        }
      non_compliant: |
        resource "aws_instance" "app" {
          ami           = data.aws_ami.ubuntu.id
          instance_type = "t3.medium"
          tags = {
            Name = "payment-app"
            # Missing: cost-center, owner, environment, workload
          }
        }

references:
  narrative: "modules/pillar-cost/pages/controls.adoc#WAF-COST-010"
  best_practice: "modules/pillar-cost/pages/best-practices/bp-tagging.adoc"
  related_controls:
    - "WAF-COST-020"
    - "WAF-COST-030"

Submitting a New Control

  1. Assign an ID – next free number in the pillar (e.g. WAF-COST-110); numbers are not reused.

  2. Create the YAML – create the file according to the schema above, fill in all required fields.

  3. Validate automated checks – test locally:

    wafpass check ./tests/fixtures/ --controls WAF-COST-110
  4. Create the narrative page – create the matching .adoc file in the pillar module and link it in references.narrative.

  5. Open a pull request – submit control YAML + narrative page together. The PR title should contain the control ID.


Further Reading