-
Notifications
You must be signed in to change notification settings - Fork 1.1k
chore(infra/website): tf-apply pipeline + restore Route53-record management #1580
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| name: Terraform Apply | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main] | ||
| paths: | ||
| - 'infra/terraform/website/**' | ||
| - '.github/workflows/tf-apply.yml' | ||
| workflow_dispatch: | ||
| inputs: | ||
| ref: | ||
| description: 'Git ref to apply (default: current default branch)' | ||
| required: false | ||
| type: string | ||
|
|
||
| concurrency: | ||
| group: tf-apply-website | ||
| cancel-in-progress: false | ||
|
|
||
| permissions: | ||
| contents: read | ||
| id-token: write | ||
|
|
||
| jobs: | ||
| apply: | ||
| name: terraform apply (infra/terraform/website) | ||
| runs-on: ubuntu-latest | ||
| environment: iii-website-prod-tf-apply | ||
| timeout-minutes: 15 | ||
|
|
||
| env: | ||
| AWS_REGION: us-east-1 | ||
| TF_IN_AUTOMATION: 'true' | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| ref: ${{ inputs.ref || github.ref }} | ||
|
|
||
| - uses: hashicorp/setup-terraform@v3 | ||
| with: | ||
| terraform_version: '1.9.8' | ||
| terraform_wrapper: false | ||
|
|
||
| - name: Configure AWS credentials (GitHub OIDC) | ||
| uses: aws-actions/configure-aws-credentials@v4 | ||
| with: | ||
| role-to-assume: ${{ secrets.AWS_TF_APPLY_ROLE_ARN }} | ||
| aws-region: ${{ env.AWS_REGION }} | ||
|
|
||
| - name: Terraform init | ||
| working-directory: infra/terraform/website | ||
| run: terraform init -input=false | ||
|
|
||
| - name: Terraform apply | ||
| id: apply | ||
| working-directory: infra/terraform/website | ||
| env: | ||
| TF_VAR_alarm_email: ${{ secrets.ALARM_EMAIL }} | ||
| run: | | ||
| set -o pipefail | ||
| terraform apply -input=false -auto-approve -no-color 2>&1 | tee apply.txt | ||
| { | ||
| echo 'apply<<TF_APPLY_EOF' | ||
| tail -c 60000 apply.txt | ||
| echo 'TF_APPLY_EOF' | ||
| } >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Job summary | ||
| if: always() | ||
| env: | ||
| APPLY: ${{ steps.apply.outputs.apply }} | ||
| run: | | ||
| { | ||
| echo "## terraform apply — \`infra/terraform/website\`" | ||
| echo | ||
| echo "- Commit: \`${{ github.sha }}\`" | ||
| echo "- Ref: \`${{ inputs.ref || github.ref }}\`" | ||
| echo | ||
| echo '<details><summary>Apply output</summary>' | ||
| echo | ||
| echo '```' | ||
| echo "${APPLY:-(no apply output captured)}" | ||
| echo '```' | ||
| echo | ||
| echo '</details>' | ||
| } >> "$GITHUB_STEP_SUMMARY" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -87,8 +87,44 @@ resource "aws_iam_role_policy_attachment" "github_deploy_website" { | |
| policy_arn = aws_iam_policy.github_deploy_website.arn | ||
| } | ||
|
|
||
| # Read-only role for `tf-plan.yml`. Separate from the deploy role so a malicious | ||
| # PR can't accidentally run `terraform apply` with write permissions. | ||
| data "aws_iam_policy_document" "github_tf_apply_trust" { | ||
| statement { | ||
| effect = "Allow" | ||
| actions = ["sts:AssumeRoleWithWebIdentity"] | ||
|
|
||
| principals { | ||
| type = "Federated" | ||
| identifiers = [aws_iam_openid_connect_provider.github.arn] | ||
| } | ||
|
|
||
| condition { | ||
| test = "StringEquals" | ||
| variable = "token.actions.githubusercontent.com:aud" | ||
| values = ["sts.amazonaws.com"] | ||
| } | ||
|
|
||
| condition { | ||
| test = "StringEquals" | ||
| variable = "token.actions.githubusercontent.com:sub" | ||
| values = [ | ||
| "repo:${var.github_repo}:environment:${var.github_tf_apply_environment}", | ||
| ] | ||
| } | ||
| } | ||
| } | ||
|
|
||
| resource "aws_iam_role" "github_tf_apply" { | ||
| name = "iii-website-prod-github-tf-apply" | ||
| 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." | ||
| assume_role_policy = data.aws_iam_policy_document.github_tf_apply_trust.json | ||
| max_session_duration = 3600 | ||
| } | ||
|
|
||
| resource "aws_iam_role_policy_attachment" "github_tf_apply_admin" { | ||
| role = aws_iam_role.github_tf_apply.name | ||
| policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess" | ||
| } | ||
|
Comment on lines
+123
to
+126
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replace Attaching the AWS managed admin policy gives this GitHub OIDC role full account-wide power, which is far broader than the website module needs and makes a workflow compromise much more damaging. Please scope this to the Terraform resources the module actually manages, or split out a narrower apply role. 🧰 Tools🪛 Checkov (3.2.525)[high] 123-126: Disallow IAM roles, users, and groups from using the AWS AdministratorAccess policy (CKV_AWS_274) 🤖 Prompt for AI Agents |
||
|
|
||
| data "aws_iam_policy_document" "github_tf_plan_trust" { | ||
| statement { | ||
| effect = "Allow" | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trim the raw apply log from the job summary.
Writing the full
terraform applyoutput into$GITHUB_STEP_SUMMARYmakes it more durable and easier to skim than the job log, which increases the chance that sensitive diffs or provider error details get surfaced unnecessarily. Consider replacing it with a short status message and a link back to the run logs, or redacting the captured output first.Suggested change
🤖 Prompt for AI Agents