Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .agents/rules/code-style.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Code Style Rules

## Terraform

- Run `terraform fmt` before committing. This is enforced by pre-commit.
- Use `snake_case` for resource names, variable names, and local values.
- Use 2-space indentation (Terraform default).
- Align `=` signs within a block for readability (as seen in `control_tower_parameters`).
- Prefix account names with `local.account_name_prefix` (`attest-ct`).
- Keep one module invocation per AWS account in `terraform/main.tf`.
- Define all module variables with explicit `type` and `description` in `variables.tf`.
- Use `default` values only for truly optional variables.

## Naming Conventions

- Account module names: lowercase, underscores (e.g., `shared_services`, `new_ventures`).
- Account customizations names: lowercase, hyphens (e.g., `shared-services`, `new-ventures`).
- Account aliases: `attest-ct-{environment}` (3-63 characters).
- Email pattern: `aws-attest-ct+{environment}@askattest.com`.

## Jinja Templates

- Files `aft-providers.jinja` and `backend.jinja` are auto-generated by AFT at runtime. Do not manually edit the generated output files (`backend.tf`, `providers.tf`).

## Pre-commit

All pre-commit hooks must pass before merging:
- `terraform_fmt` -- formatting
- `terraform_validate` -- syntax validation
- `terraform_tflint` -- linting
- `terraform_trivy` -- security scanning
- `terraform_docs` -- documentation generation
- `trailing-whitespace`, `end-of-file-fixer`, `check-yaml`, `check-added-large-files`
27 changes: 27 additions & 0 deletions .agents/rules/security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Security Rules

## Sensitive Data

- **Never** commit `.tfvars` files. They are gitignored and may contain secrets.
- **Never** commit `.tfstate` files. State is stored remotely in S3 with KMS encryption.
- **Never** commit `backend.tf` or `providers.tf`. These are auto-generated from Jinja templates by AFT at runtime.
- Email addresses in `control_tower_parameters` are organizational emails, not personal. This is acceptable for account provisioning.

## Terraform Security

- **Trivy scanning** is enforced via pre-commit (`terraform_trivy` hook). All findings must be resolved before merging.
- **TFLint** catches common misconfigurations.
- The AWS provider assumes an IAM role (`aft_admin_role_arn`) with least-privilege scoping managed by AFT.
- Backend state is encrypted at rest (S3 + KMS) and uses DynamoDB for state locking.

## Access Control

- This repository provisions AWS accounts via Control Tower. Changes have high blast radius.
- All changes should go through pull request review.
- The `SSOUserEmail` field determines the initial SSO admin for each account -- verify this is intentional before changing.

## Account Provisioning

- New accounts are automatically enrolled in AWS Control Tower guardrails.
- The `ManagedOrganizationalUnit` determines which OU policies (SCPs) apply. Changing this has security implications.
- `account_customizations_name` links to customizations in a separate AFT repo -- verify those customizations before associating them.
33 changes: 33 additions & 0 deletions .agents/rules/testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Testing Rules

## Validation

This repository has no unit tests in the traditional sense. Validation is performed via:

1. **`terraform validate`** -- Ensures HCL syntax and configuration are valid. Run via pre-commit.
2. **`terraform_tflint`** -- Lints Terraform files for errors, best practices, and provider-specific issues. Run via pre-commit.
3. **`terraform_trivy`** -- Scans for security misconfigurations. Run via pre-commit.

## Running Checks Locally

```bash
# Using mise task (runs all pre-commit hooks)
mise run precommit

# Or directly
pre-commit run --all-files
```

## Before Submitting Changes

- Run `mise run precommit` and ensure all hooks pass.
- Verify that new account modules have all required variables populated:
- `control_tower_parameters` (all 6 fields)
- `account_tags` (at minimum `Name` and `ManagedBy`)
- `change_management_parameters` (`change_requested_by`, `change_reason`)
- `account_customizations_name`
- Verify the `AccountEmail` is unique across all modules (it serves as the DynamoDB hash key).

## AFT Pipeline Validation

The AFT pipeline (AWS CodePipeline) runs `terraform plan` and `terraform apply` in the AFT management account. Changes merged to main are automatically picked up by the pipeline.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,5 @@ $RECYCLE.BIN/

# Windows shortcuts
*.lnk
CLAUDE.local.md
.claude/settings.local.json
60 changes: 60 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# AGENTS.md -- aft-account-request

## Project Overview

This repository defines **AWS account requests** for provisioning via [AWS Control Tower Account Factory for Terraform (AFT)](https://developer.hashicorp.com/terraform/tutorials/aws/aws-control-tower-aft). Each account is declared as a Terraform module invocation that writes a record to the AFT DynamoDB `aft-request` table.

## Tech Stack

| Layer | Technology |
|-------|-----------|
| IaC | Terraform >= 0.15.0 (pinned to 1.13.3 via mise) |
| Cloud | AWS (Control Tower, DynamoDB, S3, IAM, SSO) |
| Provider | hashicorp/aws >= 3.15 |
| Tooling | mise, pre-commit, tflint, trivy, terraform-docs |
| License | MPL 2.0 |

## Repository Structure

```
terraform/
main.tf # Account request declarations (one module per account)
versions.tf # Root Terraform + provider version constraints
aft-providers.jinja # Jinja template: auto-generated provider config
backend.jinja # Jinja template: auto-generated backend config
modules/
aft-account-request/
ddb.tf # Writes account request item to DynamoDB
variables.tf # Module input variables
versions.tf # Module version constraints
.pre-commit-config.yaml # Pre-commit hooks config
mise.toml # Tool version management
```

## How to Add a New Account

1. Add a new `module` block in `terraform/main.tf`.
2. Use `source = "./modules/aft-account-request"`.
3. Populate all required variables:
- `control_tower_parameters`: AccountEmail (unique), AccountName, ManagedOrganizationalUnit, SSO user info
- `account_tags`: at minimum `Name` and `ManagedBy = "AFT"`
- `change_management_parameters`: who requested and why (Shortcut link)
- `account_customizations_name`: links to customizations in the AFT account customizations repo
4. Follow naming conventions in `.agents/rules/code-style.md`.
5. Run `mise run precommit` to validate.
6. Open a pull request. Once merged, the AFT CodePipeline applies the change.

## Key Rules for AI Agents

- **Read-only awareness**: Jinja templates (`*.jinja`) are processed by AFT at runtime. Do not create or modify `backend.tf` or `providers.tf` directly.
- **DynamoDB key**: `AccountEmail` is the hash key in the `aft-request` table. It must be unique across all modules.
- **High blast radius**: This repo provisions AWS accounts. Every change should be reviewed carefully.
- Refer to `.agents/rules/` for code-style, testing, and security guidelines.
- Refer to `docs/ARCHITECTURE.md` for system architecture and data flow.

## Keeping Docs Current

When making changes:
- If you add/remove an account, update `README.md` account table and `docs/ARCHITECTURE.md` diagram.
- If you change tooling or versions, update `AGENTS.md` tech stack table.
- If you change module variables, update `AGENTS.md` "How to Add a New Account" section.
12 changes: 12 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@AGENTS.md
@.agents/rules/code-style.md
@.agents/rules/testing.md
@.agents/rules/security.md
@docs/ARCHITECTURE.md


### Response Preferences
- Be concise. Prefer code over prose.
- When uncertain about architecture, read `docs/ARCHITECTURE.md` before assuming.
- When a change would invalidate any section of `AGENTS.md` or `README.md`,
flag it and offer to update them per the instructions in AGENTS.md.
45 changes: 42 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,44 @@
# Account Factory for Terraform: Account Requests

Built using [Provision and Manage Accounts with
Control Tower Account Factory for Terraform
tutorial](https://developer.hashicorp.com/terraform/tutorials/aws/aws-control-tower-aft).
Defines AWS account requests for provisioning via [AWS Control Tower Account Factory for Terraform (AFT)](https://developer.hashicorp.com/terraform/tutorials/aws/aws-control-tower-aft).

Each module invocation in `terraform/main.tf` represents an AWS account to be created and managed by Control Tower.

## Accounts

| Account | Organizational Unit | Customizations |
|---------|---------------------|----------------|
| attest-ct-shared-services | Infrastructure | shared-services |
| attest-ct-dev | Workloads | dev |
| attest-ct-qa | Workloads | qa |
| attest-ct-prod | Workloads | prod |
| attest-ct-new-ventures | Workloads | new-ventures |
| attest-ct-dr | DisasterRecovery | dr |

## Prerequisites

Tool versions are managed by [mise](https://mise.jdx.dev/). Run `mise install` to set up:

- Terraform 1.13.3
- tflint 0.59.1
- trivy 0.66.0
- terraform-docs 0.20.0
- Python 3.13.7 + pre-commit

## Usage

```bash
# Run all pre-commit checks (fmt, validate, lint, security scan)
mise run precommit
```

To add a new account, see [AGENTS.md](AGENTS.md#how-to-add-a-new-account).

## Documentation

- [AGENTS.md](AGENTS.md) -- Project overview, structure, and contribution guide
- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) -- System architecture with diagrams

## License

[Mozilla Public License 2.0](LICENSE)
109 changes: 109 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Architecture -- aft-account-request

## Overview

This repository is one component of the AWS Account Factory for Terraform (AFT) system. It defines which AWS accounts should exist under AWS Control Tower. When changes are merged, the AFT pipeline reads the account requests from a DynamoDB table and provisions or updates accounts accordingly.

## System Context

```mermaid
graph LR
Dev["Developer"] -->|PR + merge| Repo["aft-account-request repo"]
Repo -->|triggers| Pipeline["AFT CodePipeline<br/>(AFT Management Account)"]
Pipeline -->|terraform apply| DDB["DynamoDB<br/>aft-request table"]
DDB -->|triggers| AFT["AFT Account Provisioning"]
AFT -->|creates/updates| CT["AWS Control Tower"]
CT -->|provisions| Accounts["AWS Accounts"]
```

## Data Flow

```mermaid
sequenceDiagram
participant Dev as Developer
participant GH as Git Repository
participant CP as AFT CodePipeline
participant TF as Terraform
participant DDB as DynamoDB (aft-request)
participant AFT as AFT Engine
participant CT as Control Tower

Dev->>GH: Merge PR to main
GH->>CP: Webhook trigger
CP->>TF: terraform plan + apply
TF->>DDB: Write/update account request item
DDB->>AFT: Change detected (stream)
AFT->>CT: Provision/update account
CT-->>AFT: Account ready
AFT->>AFT: Apply account customizations
```

## Terraform Module Structure

```mermaid
graph TD
Root["terraform/main.tf"] --> SS["module.shared_services"]
Root --> Dev["module.dev"]
Root --> QA["module.qa"]
Root --> Prod["module.prod"]
Root --> NV["module.new_ventures"]
Root --> DR["module.dr"]

SS --> Mod["modules/aft-account-request"]
Dev --> Mod
QA --> Mod
Prod --> Mod
NV --> Mod
DR --> Mod

Mod --> DDB["aws_dynamodb_table_item<br/>(aft-request table)"]
```

## Account Topology

```mermaid
graph TD
Org["AWS Organization"] --> Infra["OU: Infrastructure"]
Org --> Workloads["OU: Workloads"]
Org --> DRou["OU: DisasterRecovery"]

Infra --> SS["attest-ct-shared-services"]
Workloads --> DevAcc["attest-ct-dev"]
Workloads --> QAAcc["attest-ct-qa"]
Workloads --> ProdAcc["attest-ct-prod"]
Workloads --> NVAcc["attest-ct-new-ventures"]
DRou --> DRAcc["attest-ct-dr"]
```

## Module Interface

The `aft-account-request` module accepts:

| Variable | Type | Required | Description |
|----------|------|----------|-------------|
| `control_tower_parameters` | object | Yes | AccountEmail, AccountName, ManagedOrganizationalUnit, SSO user details |
| `account_tags` | map(any) | Yes | Account-level tags (Name, ManagedBy) |
| `change_management_parameters` | object | Yes | Who requested the change and why |
| `custom_fields` | map(any) | No | Arbitrary metadata (group, description, account_alias) |
| `account_customizations_name` | string | No | Name of customizations to apply from AFT customizations repo |

The module writes a single DynamoDB item to the `aft-request` table, keyed by `AccountEmail`.

## Related AFT Repositories

<!-- TODO: human please fill in the actual repo URLs -->

| Repository | Purpose |
|------------|---------|
| `aft-account-request` (this repo) | Defines which accounts to provision |
| `aft-account-customizations` | Per-account Terraform customizations |
| `aft-global-customizations` | Customizations applied to all accounts |
| `aft-account-provisioning-customizations` | Pre/post provisioning hooks |

## Backend Configuration

Terraform state is stored remotely:
- **S3 bucket** with KMS encryption (configured via `backend.jinja`)
- **DynamoDB table** for state locking
- IAM role assumption via `aft_admin_role_arn`
- Backend config is auto-generated by AFT from `backend.jinja` -- never edit `backend.tf` directly.