# CLAUDE.md

## Repository Overview

This is a Terraform monorepo that centralizes infrastructure-as-code for PDEGO. It uses Atlantis for automated `plan` and `apply` operations via pull requests. The repository consolidates previously separate Terraform projects into a single, manageable codebase.

### Directory Structure

- `prod/` - Production environment configurations for the legacy Orchard AWS account (AWS account name: `prod`)
- `qa/` - QA environment configurations for the legacy Orchard AWS account (AWS account name: `prod` 
- `dev/` - Development environment configurations for the Orchard dev AWS account (AWS account name: `dev`)
- `<account_name>/prod/` - Production configurations for shared infrastructure accounts (e.g., `shared/prod`) (AWS account name: `<account_name`)
- `<application>/<environment>/` - Application-specific account configurations (e.g., `permissions-platform/qa`, `permissions-platform/prod`) (AWS account name: `<application>-<environment>`)

When creating new configurations, always prompt for the target AWS account if not explicitly provided.

Terraform code should be as consistent as possible across environments, but code is never shared between environments. In particular, **never use local modules**.

### Key Directories

- `prod/github` - GitHub repository management using the `terraform-github` module
- `prod/snowflake` - Snowflake infrastructure management. When working with Snowflake, always read `prod/snowflake/CLAUDE.md`.
- `shared/prod/ecr/repos` - ECR repository management using the `terraform-ecr` module.
- `ecommerce/` - Application-specific infrastructure. See subdirectory `CLAUDE.md` files for application-specific guidance.

### Terraform Change Workflow

All changes must go through pull requests. The general workflow is as follows:

- Automatic checks run in parallel when PR is opened:
  - Atlantis runs `terraform plan` in all modified Terraform directories and posts the output(s) as a comment.
  - Checkov runs security scans and posts results as a comment.
  - AI PR review runs and posts a review comment.
- PR is reviewed and approved by a member of the infra-maintainers GitHub team. **All status checks must be passing before requesting review**.
- Changes are applied by commenting `atlantis apply` on the PR.
- PR is merged using the squash and merge strategy. **PRs must not be merged until the apply has completed successfully**.

#### PR Structure Requirements
- **Keep PRs atomic** - ideally limited to single directory, max 10 services
- **Single environment per PR** - avoid mixing QA and Prod changes

## Required File Structure

Always follow the below guidelines when creating new Terraform directories. **NOTE: existing configurations may not follow these guidelines, but all new code must**.

### `main.tf` (Required)
Contains provider blocks and backend configuration. For small configurations all resources
can be defined here; for larger ones, organize resources into separate files that logically
group related resources (e.g. `iam.tf`, `lambda.tf`, `datadog.tf`).

Always define `provider` blocks at the top of the file, immediately followed by backend configuration.
**Never configure credentials in `provider` blocks**, these are injected via environment variables.

#### Backend Configuration

Use the following S3 backend configuration for all environments. 

```hcl
terraform {
  backend "s3" {
    bucket  = "<STATE_BUCKET_NAME>"
    key     = "<STATE_KEY>"
    region  = "<AWS_REGION>"
    encrypt = "true"
  }
}
```

The state bucket name is account-specific:

* Legacy Orchard account: `orcd-terraform-state`
* Orchard dev account: `dev-orcd-terraform-state`
* Application-specific accounts: `<environment>-<application>-terraform-state` (e.g. `prod-fansifter-terraform-state`)
* Shared infrastructure accounts: `<environment>-orcd-terraform-state` (e.g. `shared-orcd-terraform-state`)

Derive state key from the path to the Terraform directory from the repository root, appended with `terraform.tfstate`.
Strip the top-level folder from the key unless the top-level folder is `prod`, `qa`, or `dev`.

AWS region for the state bucket is account-specific. Use the `lookup-aws-account-metadata` skill 
to look up the `default_region` field for the target account.

#### AWS Provider Configuration

The AWS provider must be configured if the Terraform code includes any AWS resources.

Always configure `default_tags` on the provider using the `theorchard/terraform-default-tags` module,
which should be defined immediately before the `provider` block. This ensures all AWS resources are
automatically tagged with the required tags.

```hcl
module "default_tags" {
  source             = "git@github.com:theorchard/terraform-default-tags.git//?ref=x.x.x" # replace ref with latest release
  environment        = var.environment
  application_family = var.application_family
  service_name       = var.service_name
  team_name          = var.team_name
}

provider "aws" {
  region = var.aws_region

  default_tags {
    tags = module.default_tags.tags
  }
}
```

### `versions.tf` (Required)
Specifies the exact Terraform version. Must be present in every configuration —
use an exact pin, never a range. 

**Always use the latest version** - use the `get-latest-terraform-version` skill to determine this.
Always update the version when changing existing code.

```hcl
terraform {
  required_version = "1.15.5"
}
```

Only add a `required_providers` section if managing resources using non-Hashicorp providers, 
or provider version constraints are required for compatibility reasons. 

When using modules which use non-Hashicorp providers (e.g. `terraform-datadog`), 
these will typically define their own provider requirements, so do not duplicate them in the root module.

### `variables.tf` (Required)
The following values should be defined as variables in `variables.tf`:

- `aws_region` - for AWS provider configuration, default to the account's default region.
- Common module inputs (e.g. `environment`, `application_family`, `service_name`, `team_name`).
- Any values which differ between environments.

All variables must have a type and description. Values are specified via variable defaults. **Do not use `.tfvars` files to specify values**.

### `imports.tf`/`moved.tf`/`removed.tf` (When needed)
Specify all `import`, `moved` and `removed` blocks in these files respectively.

When updating existing configurations, remove all pre-existing `import`, `moved` and `removed` blocks, removing the files entirely if no longer needed.

**Always define state manipulation operations as code. Never manipulate state manually**.

## Naming Conventions
- IAM policies: `AWSSERVICE-${var.environment}-${var.service_name}-access_type`
  - Examples: `MSK-qa-cdc-destination-RO`, `S3-prod-orcd-raw-assets-RW`
- Other resources: `${var.environment}-${var.service_name}-${optional_suffix}`
- Lambda names: Must begin with `lambda-`

## Common Commands

- `tfswitch` - **always run before running any `terraform` commands** to ensure correct Terraform version is being used
- `terraform fmt` - **always run against any modified Terraform directories**

Plan and apply commands can be run locally in the dev account, but changes should still be pushed.
For other accounts, only the DevOps team are allowed to run local plans and applies.

## Best Practices

- Keep configurations small to prevent slow plan/apply times and reduce blast radius of changes.
- **Always use data sources** for external resource references
- **Never hard-code** resource IDs or ARNs
- Use resource references for dependencies within same configuration
- Use `for_each` instead of `count` for multiple similar resources
- Reference resource outputs directly: `aws_instance.example.id`

## Module Usage

Most resources should be created using internal modules. These modules enforce organizational standards and best practices, and promote consistency across configurations.

### Best Practices

- **Always check if a module exists for a given usecase, and use it if so**. See the [Available Modules](#available-modules) section for a list of available modules.
- When creating new modules, **always use the latest version of the module**. Use the `get-latest-terraform-module-version` skill to determine this.
- When updating existing configurations, don't update module versions unless explicitly instructed to do so, or it is required for specific functionality or Checkov compliance.
- Prefer existing modules over direct resource implementation. If a module is missing functionality, instruct the user to raise a DevOps ticket rather than circumventing it.

### Module Documentation

Whenever working with modules, use the `get-terraform-module-documentation` skill to obtain usage instructions, examples and available inputs.

### Module References
Reference external modules using exact version tags:
```hcl
module "example" {
  source = "git@github.com:theorchard/terraform-example.git//path/to/module?ref=1.2.3"
}
```

### Available Modules

Most modules create all the resources for a complete service or component, including Route 53 records, security groups and IAM policies.

- `terraform-airflow` - manages Airflow environments
- `terraform-aws-waf` - manages custom WAFs - only required if custom WAF rules are needed
- `terraform-datadog` - module monorepo for Datadog monitors and dashboards
- `terraform-dev-box` - manages "dev box" environments, used for PHP monolith app development
- `terraform-dynamodb` - manages DynamoDB tables
- `terraform-ecr` - manages ECR repositories
- `terraform-efs` - manages EFS file systems
- `terraform-elasticache` - manages ElastiCache clusters
- `terraform-elasticsearch` - manages Elasticsearch/Opensearch domains
- `terraform-fargate` - manages Fargate services and associated resources
- `terraform-github` - manages GitHub repositories
- `terraform-iam-policy-templates` - monorepo of data-only modules which encapsulates the creation of standard IAM policies not otherwise covered by other modules
- `terraform-internal-spa` - manages private Single Page Applications (SPAs)
- `terraform-kinesis` - manages Kinesis streams
- `terraform-lambda` - manages Lambda functions and associated resources
- `terraform-managed-kafka` - manages MSK clusters
- `terraform-rds` - manages RDS clusters
- `terraform-s3` - manages S3 buckets
- `terraform-sagemaker-notebook` - manages SageMaker notebook instances
- `terraform-secrets-manager` - manages Secrets Manager secrets
- `terraform-sentry` - manages Sentry projects
- `terraform-service-info` - data-only module for retrieving service security group IDs. Use to configure cross-account access between service without hardcoding security group IDs.
- `terraform-sns` - manages SNS topics and subscriptions
- `terraform-spa` - manages public Single Page Applications (SPAs) using Cloudfront
- `terraform-sqs` - manages SQS queues
- `terraform-vpc-info` - data-only module for context-aware lookups of VPC IDs and subnet IDs. **Always use this module for VPC and subnet references**, never hardcode these values or use data sources directly.

### Upgrading Modules

Use the `upgrade-terraform-module` skill to upgrade modules.

## Security & Compliance

### Critical Security Requirements
- **Never commit sensitive information** (secrets, tokens, keys)
- **Never open resources to public internet** (`0.0.0.0/0`)
- **Encrypt everything** using AWS-managed or CMK KMS keys
- **No plaintext secrets** - use Fargate secrets block or Secrets Manager
- **Follow least privilege principle for IAM policies** - never use wildcards
- **Follow least privilege principle for security groups** - no insecure protocols

### IAM Best Practices
- Create purpose-specific IAM roles
- Use inline policies for service-specific permissions, and managed policies for reusable permissions

### Database & Secret Management
- Database access: Use service users with Secrets Manager, not IAM
- Secret management: Use Fargate secrets binding or direct Secrets Manager access for Lambda
