Skip to main content
Latest Documentation
This is the latest documentation for the Cloud Posse Reference Architecture. To determine which version you're currently using, please see Version Identification.

Use Folder Structure for Compliance Components

Date: 21 Mar 2022

Rejected!

The proposal in this ADR was rejected! For questions, please reach out to Cloud Posse.

Status

DECIDED

Problem

  • Too many files clutter the stack config folders

  • Files are very terse and seldom edited

  • So many files that code generation of YAML is the only practical way of managing them

Context

Considered Options

Option 1: Use Virtual Components

Define one file for each default region (17). Having multiple files is convenient for GitOps and detecting what files changed in order to trigger CI.

# stacks/catalog/compliance/ue1.yaml
components:
terraform:
compliance-ue1:
component: compliance
vars:
region: us-east-1
environment: ue1

The root component should follow the same pattern, with one file for each default region.

# stacks/catalog/compliance/root/ue1.yaml
components:
terraform:
compliance-root-ue1:
component: compliance-root
vars:
region: us-east-1
environment: ue1
caution

There’s one downside with this naming convention, is that the final stack names (e.g. in Spacelift) will look like acme-ue1-root-compliance-ue1, with the ue1 being repeated in the name because it is pulled from the component. This will be fixed in .

Define a baseline that imports all 17 default regions

# stacks/catalog/compliance/baseline.yaml
imports:
- catalog/compliance/ue1
- catalog/compliance/ue2
...

Repeat for the “root” baseline should import all 17 default regions

# stacks/catalog/compliance/root/baseline.yaml
imports:
- catalog/compliance/root/ue1
- catalog/compliance/root/ue2
...

Then in each account-level stack configuration, import the compliance baseline.

Here are some examples:

# stacks/globals.yaml
imports:
- catalog/compliance/baseline

# stacks/plat/prod.yaml
imports:
- globals

# stacks/plat/staging.yaml
imports:
- globals

# stacks/core/security.yaml
imports:
- globals

# stacks/core/dns.yaml
imports:
- globals

The root account is the only exception, which would look like this:

# stacks/core/root.yaml
imports:
- globals
- compliance/root/baseline

Option 2: Use YAML Separators

# mock stack config

stages:
- a
- b
- c

components:
terraform:
compliance:
vars:
region: us-east-1
environment: ue1
---
# mock stack config

stages:
- a
- b
- c

components:
terraform:
compliance:
vars:
environment: ue2
region: us-east-1
---
# mock stack config

stages:
- a
- b
- c

components:
terraform:
compliance:
vars:
region: us-west-1
environment: uw1

Option 2: Current Solution


###
### stack: stacks/mdev/euw3/mdw3-audit.yaml
### chain: stacks/mdev/euw3/mdw3-audit.yaml
###
import:
- mdev/mdev-globals
- euw3/euw3-audit
###
### stack: stacks/mdev/mdev-globals.yaml
### chain: stacks/mdev/euw3/mdw3-audit.yaml > stacks/mdev/mdev-globals.yaml
###

import:
- globals

vars:
tenant: mdev

terraform:
backend:
s3:
bucket: "vygr-mdev-use2-root-tfstate"
dynamodb_table: "vygr-mdev-use2-root-tfstate-lock"
role_arn: "arn:aws:iam::807952753552:role/vygr-mdev-gbl-root-terraform"
remote_state_backend:
s3:
bucket: "vygr-mdev-use2-root-tfstate"
dynamodb_table: "vygr-mdev-use2-root-tfstate-lock"
role_arn: "arn:aws:iam::807952753552:role/vygr-mdev-gbl-root-terraform"

settings:
spacelift:
worker_pool_name: vygr-mdev-use2-auto-spacelift-worker-pool
###
### stack: stacks/globals.yaml
### chain: stacks/mdev/euw3/mdw3-audit.yaml > stacks/mdev/mdev-globals.yaml > stacks/globals.yaml
###

vars:
namespace: vygr
required_tags:
- Team
- Service
tags:
# We set the default team here, this means everything will be tagged Team:sre unless otherwise specified.
# This is used because it is the default alerted team.
Team: sre

terraform:
vars:
label_order: ["namespace", "tenant", "environment", "stage", "name", "attributes"]
descriptor_formats:
stack:
format: "%v-%v-%v"
labels: ["tenant", "environment", "stage"]
# This is needed for the transit-gateway component
account_name:
format: "%v"
labels: ["stage"]

backend_type: s3 # s3, remote, vault, etc.
backend:
s3:
encrypt: true
key: "terraform.tfstate"
acl: "bucket-owner-full-control"
region: "us-east-2"

remote_state_backend_type: s3 # s3, remote, vault, etc.
remote_state_backend:
s3:
encrypt: true
key: "terraform.tfstate"
acl: "bucket-owner-full-control"
region: "us-east-2"

###
### stack: stacks/euw3/euw3-audit.yaml
### chain: stacks/mdev/euw3/mdw3-audit.yaml > stacks/euw3/euw3-audit.yaml
###

import:
- euw3/euw3-globals

vars:
stage: audit

terraform:
vars: {}

helmfile:
vars: {}

components:
terraform:
compliance:
settings:
spacelift:
workspace_enabled: false

aws-inspector:
settings:
spacelift:
workspace_enabled: false
###
### stack: stacks/euw3/euw3-globals.yaml
### chain: stacks/mdev/euw3/mdw3-audit.yaml > stacks/euw3/euw3-audit.yaml > stacks/euw3/euw3-globals.yaml
###

import:
- catalog/compliance/compliance
# - catalog/aws-inspector
# @TODO aws-inspector is not yet supported in Paris, it is likely this will change in the future
# https://docs.aws.amazon.com/inspector/v1/userguide/inspector_rules-arns.html

vars:
region: eu-west-3
environment: euw3

components:
terraform:
vpc:
vars:
availability_zones:
- "eu-west-3a"
- "eu-west-3b"
- "eu-west-3c"

eks/eks:
vars:
availability_zones:
- "eu-west-3a"
- "eu-west-3b"
- "eu-west-3c"
###
### stack: stacks/catalog/compliance/compliance.yaml
### chain: stacks/mdev/euw3/mdw3-audit.yaml > stacks/euw3/euw3-audit.yaml > stacks/euw3/euw3-globals.yaml > stacks/catalog/compliance/compliance.yaml
###

components:
terraform:
compliance:
settings:
spacelift:
workspace_enabled: true
vars:
enabled: true
tags:
Team: sre
Service: compliance
config_bucket_env: use2
config_bucket_stage: audit
config_rules_paths:
- https://raw.githubusercontent.com/cloudposse/terraform-aws-config/0.14.1/catalog/account.yaml
...
central_logging_account: audit
central_resource_collector_account: security
cloudtrail_bucket_stage: audit
cloudtrail_bucket_env: use2
create_iam_role: true
global_resource_collector_region: us-east-2
guardduty_admin_delegated: true
securityhub_admin_delegated: true
securityhub_create_sns_topic: true
securityhub_enabled_standards:
- ruleset/cis-aws-foundations-benchmark/v/1.2.0
securityhub_opsgenie_sns_topic_subscription_enabled: false # TODO, enable this once /opsgenie/opsgenie_securityhub_uri SSM param is set
securityhub_opsgenie_integration_uri_ssm_account: corp
securityhub_opsgenie_integration_uri_ssm_region: us-east-2
default_vpc_deletion_enabled: true
az_abbreviation_type: short

Decision

DECIDED: Use Option 1 with virtual components

Consequences

  • Refactor configurations to be more DRY

  • to avoid duplication of the environment in stack names

References