Skip to content

Commit b2338cf

Browse files
committed
chore(infra/website): add tf-apply pipeline + clean variable comments
Two changes: 1. Strip stale `# Leave false until Phase 4 cutover…` comments from manage_apex_records / manage_www_records — the variable description field already says what they do, and the cutover narrative they preserved is no longer load-bearing. 2. Add a tf-apply pipeline (`.github/workflows/tf-apply.yml`) so changes under `infra/terraform/website/` actually deploy on merge to main. Previously only `tf-plan.yml` ran on PRs and applies were manual, which is how the cleanUrls fix sat unapplied for hours after #1576 merged. - New IAM role `iii-website-prod-github-tf-apply` (AdministratorAccess, trust narrowly scoped to a new `iii-website-prod-tf-apply` env so repo settings can require reviewers without gating routine S3 deploys). - Workflow runs on push to main + workflow_dispatch, uses concurrency `tf-apply-website` to serialize applies, captures output to the job summary. Bootstrap (one-time, manual): AWS_PROFILE=motia-prod terraform apply → grab `github_tf_apply_role_arn` from outputs → set repo secret `AWS_TF_APPLY_ROLE_ARN` → create `iii-website-prod-tf-apply` GitHub environment with required reviewers
1 parent bd6cf33 commit b2338cf

4 files changed

Lines changed: 138 additions & 8 deletions

File tree

.github/workflows/tf-apply.yml

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
name: Terraform Apply
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- 'infra/terraform/website/**'
8+
- '.github/workflows/tf-apply.yml'
9+
workflow_dispatch:
10+
inputs:
11+
ref:
12+
description: 'Git ref to apply (default: current default branch)'
13+
required: false
14+
type: string
15+
16+
concurrency:
17+
group: tf-apply-website
18+
cancel-in-progress: false
19+
20+
permissions:
21+
contents: read
22+
id-token: write
23+
24+
jobs:
25+
apply:
26+
name: terraform apply (infra/terraform/website)
27+
runs-on: ubuntu-latest
28+
environment: iii-website-prod-tf-apply
29+
timeout-minutes: 15
30+
31+
env:
32+
AWS_REGION: us-east-1
33+
TF_IN_AUTOMATION: 'true'
34+
35+
steps:
36+
- uses: actions/checkout@v4
37+
with:
38+
ref: ${{ inputs.ref || github.ref }}
39+
40+
- uses: hashicorp/setup-terraform@v3
41+
with:
42+
terraform_version: '1.9.8'
43+
terraform_wrapper: false
44+
45+
- name: Configure AWS credentials (GitHub OIDC)
46+
uses: aws-actions/configure-aws-credentials@v4
47+
with:
48+
role-to-assume: ${{ secrets.AWS_TF_APPLY_ROLE_ARN }}
49+
aws-region: ${{ env.AWS_REGION }}
50+
51+
- name: Terraform init
52+
working-directory: infra/terraform/website
53+
run: terraform init -input=false
54+
55+
- name: Terraform apply
56+
id: apply
57+
working-directory: infra/terraform/website
58+
env:
59+
TF_VAR_alarm_email: ${{ secrets.ALARM_EMAIL }}
60+
run: |
61+
set -o pipefail
62+
terraform apply -input=false -auto-approve -no-color 2>&1 | tee apply.txt
63+
{
64+
echo 'apply<<TF_APPLY_EOF'
65+
tail -c 60000 apply.txt
66+
echo 'TF_APPLY_EOF'
67+
} >> "$GITHUB_OUTPUT"
68+
69+
- name: Job summary
70+
if: always()
71+
env:
72+
APPLY: ${{ steps.apply.outputs.apply }}
73+
run: |
74+
{
75+
echo "## terraform apply — \`infra/terraform/website\`"
76+
echo
77+
echo "- Commit: \`${{ github.sha }}\`"
78+
echo "- Ref: \`${{ inputs.ref || github.ref }}\`"
79+
echo
80+
echo '<details><summary>Apply output</summary>'
81+
echo
82+
echo '```'
83+
echo "${APPLY:-(no apply output captured)}"
84+
echo '```'
85+
echo
86+
echo '</details>'
87+
} >> "$GITHUB_STEP_SUMMARY"

infra/terraform/website/iam_github_oidc.tf

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,44 @@ resource "aws_iam_role_policy_attachment" "github_deploy_website" {
8787
policy_arn = aws_iam_policy.github_deploy_website.arn
8888
}
8989

90-
# Read-only role for `tf-plan.yml`. Separate from the deploy role so a malicious
91-
# PR can't accidentally run `terraform apply` with write permissions.
90+
data "aws_iam_policy_document" "github_tf_apply_trust" {
91+
statement {
92+
effect = "Allow"
93+
actions = ["sts:AssumeRoleWithWebIdentity"]
94+
95+
principals {
96+
type = "Federated"
97+
identifiers = [aws_iam_openid_connect_provider.github.arn]
98+
}
99+
100+
condition {
101+
test = "StringEquals"
102+
variable = "token.actions.githubusercontent.com:aud"
103+
values = ["sts.amazonaws.com"]
104+
}
105+
106+
condition {
107+
test = "StringEquals"
108+
variable = "token.actions.githubusercontent.com:sub"
109+
values = [
110+
"repo:${var.github_repo}:environment:${var.github_tf_apply_environment}",
111+
]
112+
}
113+
}
114+
}
115+
116+
resource "aws_iam_role" "github_tf_apply" {
117+
name = "iii-website-prod-github-tf-apply"
118+
description = "Assumed by GitHub Actions from the ${var.github_tf_apply_environment} environment to run `terraform apply` against infra/terraform/website. Configure that environment with required reviewers in repo settings to gate applies."
119+
assume_role_policy = data.aws_iam_policy_document.github_tf_apply_trust.json
120+
max_session_duration = 3600
121+
}
122+
123+
resource "aws_iam_role_policy_attachment" "github_tf_apply_admin" {
124+
role = aws_iam_role.github_tf_apply.name
125+
policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
126+
}
127+
92128
data "aws_iam_policy_document" "github_tf_plan_trust" {
93129
statement {
94130
effect = "Allow"

infra/terraform/website/outputs.tf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ output "github_tf_plan_role_arn" {
3838
value = aws_iam_role.github_tf_plan.arn
3939
}
4040

41+
output "github_tf_apply_role_arn" {
42+
description = "Admin IAM role ARN assumed by the tf-apply GitHub Actions workflow on push to main — set as repo-level GitHub secret AWS_TF_APPLY_ROLE_ARN"
43+
value = aws_iam_role.github_tf_apply.arn
44+
}
45+
4146
output "sns_alarms_topic_arn" {
4247
description = "SNS topic ARN that receives production alarms"
4348
value = aws_sns_topic.alarms.arn

infra/terraform/website/variables.tf

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,25 +58,27 @@ variable "github_environment" {
5858
default = "iii-website-prod"
5959
}
6060

61+
variable "github_tf_apply_environment" {
62+
# Distinct from github_environment so the apply env can be configured with
63+
# required reviewers without gating routine S3 deploys.
64+
description = "GitHub environment scoping the tf-apply role. Configure required reviewers on this env in repo settings to gate applies."
65+
type = string
66+
default = "iii-website-prod-tf-apply"
67+
}
68+
6169
variable "csp_report_only" {
6270
description = "Send CSP as report-only instead of enforcing."
6371
type = bool
6472
default = true
6573
}
6674

6775
variable "manage_apex_records" {
68-
# Phase 4 cutover is complete (see #1470). Records were imported into state
69-
# and Terraform now owns them. Flag retained as an escape hatch for emergency
70-
# rollback — set to false to release ownership without destroying the records
71-
# (use `terraform state rm` after flipping).
7276
description = "Whether Terraform manages the iii.dev apex A/AAAA Route53 records."
7377
type = bool
7478
default = true
7579
}
7680

7781
variable "manage_www_records" {
78-
# Phase 4 cutover complete; same situation as manage_apex_records. Decoupled
79-
# from apex so the two can be released independently if ever needed.
8082
description = "Whether Terraform manages the www.iii.dev A/AAAA Route53 records."
8183
type = bool
8284
default = true

0 commit comments

Comments
 (0)