Automated
An Automated system needs no human effort for repetitive operations: provisioning, deployment, validation, secret distribution, remediation. In the AWS Baseline and the blueprints the pipeline, not a laptop, is the only writer to shared environments. If deployment requires a hero, it is not automated.
The infrastructure pipeline
Three workflows ship with the landing zone, and the two blueprint infrastructure repositories carry the first two:
| Workflow | Trigger | What it does |
|---|---|---|
plan.yml | Pull request touching environments/, units/, stacks/, modules/, root.hcl, common.hcl, scripts/, the VPC maps or itself | Generates the stacks, runs the guardrail scripts, tofu fmt, tflint, Checkov and Trivy, then terragrunt run --all plan and posts the result. One plan per pull request; a new push supersedes the running one |
apply.yml | Merge to main | Applies every unit in dependency order behind a GitHub Environment. Applies queue per ref and are never cancelled, so two merges cannot apply against the same state at once. Blueprints run a dev, staging, prod matrix, each under the Environment of the same name |
drift-detection.yml | 01:00 UTC Tuesday to Friday for the network, identity and active workload accounts; Monday and on demand for all fourteen | terragrunt run --all plan -detailed-exitcode per account directory, all regions plus global; exit code 2 opens a drift issue with the plan output |
Both plan and apply write a JSON run report that CI uploads as an artifact. The GitHub Actions explainer describes the runners and the OIDC step every workflow starts with.
Guardrails that run before a plan
The same checks run locally in pre-commit and again in CI, so a contributor without hooks is still caught:
| Check | Protects against |
|---|---|
check-stack-layout.py | A region folder without a stack file, a hand-written unit under environments/, an unreferenced unit definition |
check-no-hardcoded-cidrs.py | A private IP range typed anywhere but the VPC map |
check-vpc-maps.py | Invalid, overlapping or mis-nested ranges, or a unit whose address lookup has no map entry |
check-mock-outputs.py | A dependency that cannot be planned because its outputs are not mocked |
check-required-inputs.py | A unit that does not supply every required input of its module |
check-module-versions.py | A module whose provider pins differ from root.hcl |
tofu fmt, tflint, Checkov, Trivy | Malformed, non-idiomatic or insecure configuration |
The policy as code page explains why conventions checked by machine stay true; the Checkov, Trivy and tflint explainer covers the scanners.
Application delivery
The Web App Blueprint builds each image once and never rebuilds it for a later stage:
ci.ymlon a pull request: lint, tests, Docker build, Trivy scan.cd-integration.ymlon merge tomain: build, pushmain-<short sha>to ECR in theartifactsaccount, open a pull request againstenvs/devin the GitOps repository, auto-merged; ArgoCD auto-syncs dev.cd-release.ymlon a GitHub Release: add thevX.Y.Ztag to the same image, open a pull request againstenvs/stg, auto-merged; ArgoCD auto-syncs staging.promote-prod.ymlon demand with a release tag: verify the tag exists in ECR, open theenvs/prdpull request with no auto-merge, behind theprodEnvironment; a person merges it and ArgoCD prod is synced manually.cd-frontend.yml: build the React application, sync to S3, invalidate CloudFront.
No workflow pushes to the GitOps repository directly; each mints a short-lived token for the acme-runner GitHub App and opens a pull request. ECR rejects tag overwrites, so latest does not exist. The Data and ETL Blueprint follows the same build-once rule and records the deployed Lambda image tag in an SSM parameter that the infrastructure reads back, so a plan never rolls the function to an older tag. The GitOps, immutable artifacts and ArgoCD pages cover each part.
Everything else that runs without a person
- Secrets: a merge to the secrets repository decrypts each stage's SOPS file with that stage's KMS key and writes SSM Parameter Store SecureStrings in the target account; the External Secrets Operator refreshes Kubernetes secrets hourly.
- DNS and certificates: ExternalDNS writes public and internal records; ACM certificates are validated through DNS and renewed by AWS; the cross-account Route 53 role lets workload accounts write only the records they need.
- Remediation: EventBridge rules remove public access from an S3 bucket reported public and route high-severity findings to SNS.
- Security services: organization-wide enrollment of GuardDuty, Security Hub, Inspector and Macie is a per-service flag, applied once after delegation.
- Runners: optional self-hosted runners start per job on Spot instances and terminate when the job ends.
Review is part of the automation
CODEOWNERS routes reviews by blast radius: environments/core/, units/, .github/, root.hcl, common.hcl, the VPC maps and scripts/ to the infra admins; stacks/ and modules/ to the infra admins and platform leads; security.hcl to the infra admins and the security team; environments/plat/prod/ to platform leads and the security team. An approval from any one listed owner satisfies the rule for a path. The prod GitHub Environment holds required reviewers everywhere. A change reaches AWS only through pull request, plan, review, merge and apply.
How you verify it
- The Actions tab of your landing-zone repository: a plan run on every pull request, an apply run on every merge, drift runs on the schedule above.
- ECR in the
artifactsaccount: repositories with tag immutability on and nolatesttag. - The GitOps repository:
envs/<stage>/values.yamlchanged only by pull requests from theacme-runnerApp. - Settings of each repository: Environments
dev,stagingandprod, with required reviewers onprod.