Skip to content

Staging Infrastructure

AlienCommons provisions staging from infra/opentofu/environments/stg. The configuration must run only against the workload member account under Workloads/Stg; the AWS Organizations management account is governance-only.

Ownership boundary

OpenTofu owns the staging VPC and host, origin firewall, ECR repositories, private S3 buckets, CloudFront distributions, AWS-managed ACM certificate, application DNS records, and GitHub OIDC roles. It intentionally does not own the state bucket, Organizations structure, budgets, Cloudflare zone-wide SSL settings, Advanced Edge Certificate, or Origin CA certificate values.

The Origin CA certificate, private key, and application runtime secrets remain in pre-created SSM SecureString parameters. OpenTofu receives only their parameter names so secret values never enter state. The EC2 runtime role can read only the explicitly listed staging parameters.

Bootstrap once from a trusted workstation

The first apply cannot run in GitHub Actions because it creates the GitHub OIDC provider and roles that CI will assume. Follow infra/opentofu/README.md using an AWS Identity Center profile for staging. Keep the real account ID, state bucket name, Cloudflare Zone ID, and token in ignored local configuration or the current shell, never in Git.

Before applying, review the plan and confirm:

  • the AWS caller is the staging member account;
  • inbound access is TCP 443 from Cloudflare IP ranges only;
  • ports 22 and 80 are absent;
  • application records are proxied and ACM validation records are DNS-only;
  • all S3 buckets remain private; and
  • no certificate or private-key value appears in the plan.

If a managed hostname already has a Cloudflare DNS record, import the intended record into state or remove it only after proving it is obsolete. Do not let a first apply overwrite an unknown record.

Hand control to GitHub Actions

After the local apply succeeds, copy the two role ARN outputs directly into the GitHub stg Environment variables expected by the infrastructure and deployment workflows. Keep the account, region, state bucket, and Cloudflare Zone ID as environment variables, and the Cloudflare token as an environment secret. Also store the GitHub organization and repository numeric IDs as environment variables; IAM uses them to bind trust to GitHub's immutable OIDC subject.

The Stg Infrastructure workflow is manual-only. Run plan first and review it. Use apply only for that reviewed revision and enter the required confirmation text. The workflow serializes staging infrastructure runs and never uploads the saved plan as an artifact.

After provisioning

Use Systems Manager Session Manager instead of SSH. The manual Stg Application: Deploy workflow runs only from dev and requires the deploy-stg confirmation. It publishes digest-pinned application images to ECR, uploads a checksum-protected deployment bundle to the private deployment bucket, and invokes the host through SSM Run Command.

On the host, the deployment script reads the six application secrets directly from SSM, creates a root-owned 0600 runtime environment file, installs and validates the Origin CA pair, pulls the immutable images, runs migrations and static collection, and waits for service health checks. Secret values are never placed in the bundle or returned to GitHub Actions. The workflow then verifies Cloudflare Full (strict), checks that direct origin access is blocked, and runs public smoke checks. The current release link changes only after all host-side checks pass; a failed update makes a best-effort return to the previous container set. Database migrations are not reversed, so staging migrations must remain backward compatible with the previous application image.