Before moving Terraform delivery into GitHub Actions, agree how a proposed change becomes an authorized AWS change. The useful design connects the source revision, cloud identity, reviewed plan, approval, execution result, and operating owner.
OpenID Connect, or OIDC, lets a GitHub workflow authenticate to AWS without keeping long-lived AWS credentials in GitHub secrets. That addresses credential delivery. The workflow still needs decisions about which code receives access, what the role can do, and who can approve a change. GitHub OIDC for AWS
For a team scoping a new workflow or improving an existing one, start with a bounded pilot and the following decisions.
Define the pilot and its owner
Choose a Terraform root module, a target AWS account and environment, and an agreed class of change. Record the state location, required tools and dependencies, existing deployment path, and people who can authorize work. Separate any state migration or resource import from the workflow change unless it is explicitly included in the scope.
Ask the operating team to define what it must be able to do after handoff: review a plan, release an approved change, investigate a failure, maintain dependencies, and remove delivery access. Those tasks give the pilot concrete acceptance criteria. A successful demonstration should leave the team able to operate the agreed path itself.
Separate role trust from resource permissions
AWS Identity and Access Management, or IAM, separates the role's trust policy from its permissions policy. Trust determines who can assume the role; permissions define allowed or denied AWS actions and resources. Review both. Restrict GitHub access to the intended repository and execution context rather than granting trust across an organization by default. AWS OIDC role guidance
In the workflow, id-token: write permits an OIDC token request; it does not itself grant permission to change AWS resources. Check the audience expected by the integration as well: GitHub documents sts.amazonaws.com for the official AWS credentials action. GitHub OIDC for AWS
Match the subject claim, sub, to the repository's actual configuration. GitHub documents both older subjects based on repository names and subjects that also include immutable owner and repository IDs. On GitHub.com (not GitHub Enterprise Server), repositories created, renamed, or transferred after July 15, 2026 use the immutable format by default; other repositories keep the name-based format unless the organization or repository opts in. By default, a job that references an environment receives an environment-based subject, so a branch-based trust condition will not match it. Custom subject templates add another reason to verify the exact format. GitHub OIDC subject reference
For the pilot, define separate permission needs for planning and applying. Include backend access in that review. A role called “plan” deserves the same scrutiny as any other automation identity; its name establishes no boundary. Document which source revisions may reach each role and test that unintended contexts are rejected.
Protect the code that receives access
Keep untrusted pull request code out of jobs that receive privileged access. GitHub specifically warns about checking out untrusted code in workflows triggered by pull_request_target or workflow_run. A workflow that only produces a plan still needs a clear decision about whose code it will execute and what that execution can access. GitHub secure use guidance
Record runner ownership and isolation requirements alongside the workflow design. Review third-party actions and reusable workflows, and assign someone to review updates. GitHub now describes a workflow dependency lockfile, currently in public preview, as its strongest protection: it pins each action dependency, including most indirect ones, to a specific commit. Where a repository cannot adopt one, pin each action to a full commit SHA taken from the action's own repository. GitHub action dependency guidance GitHub workflow dependency lockfiles
These choices belong in scope before access is granted. Include who may edit the workflow, who reviews those edits, and how an emergency change is authorized and recorded.
Make approval refer to the plan being applied
Terraform distinguishes a speculative plan from a saved plan produced with -out. A saved plan can later be supplied to terraform apply. An earlier pull request preview should not stand in for review of the final plan. Terraform plan reference
Choose an explicit handoff between review and execution. For example, generate a saved plan from an approved source revision, provide its review output to the approver, and allow the apply job to consume only that identified artifact. Bind the record to the source commit, workflow run, target account, environment, and Terraform root. If the artifact must be replaced or the change must be replanned, require review of the replacement.
Terraform applies a supplied saved plan without asking for another confirmation. The approval therefore belongs before the apply command executes. Terraform apply reference
Treat the saved plan as sensitive. It can contain values in cleartext even when Terraform hides them in terminal output. Define access, retention, and disposal for the artifact; review what is exposed in logs or pull request comments. Terraform saved plan handling
Verify the approval mechanism is available
A GitHub environment can gate a job with protection rules, including required reviewers and deployment branch restrictions. Availability varies. GitHub currently documents required reviewers on Free, Pro, and Team plans as available only for public repositories. Confirm the actual repository visibility and plan before designing a private-repository workflow around that feature. GitHub deployment protection rules
Where an approval gate is used, specify who approves, whether self-approval is permitted, and how bypass rights are controlled. If the required feature is unavailable, resolve the approved mechanism before production use. A named environment alone is not evidence that an approval requirement is enforced.
Coordinate execution and protect state
Use workflow concurrency to coordinate jobs targeting the same Terraform state within the repository. Review cancellation behavior deliberately: GitHub's cancel-in-progress setting can cancel an active run, and by default a newly queued run cancels any older pending run in the same group; the optional queue: max setting keeps up to 100 pending runs instead. Do not adopt an automatic cancellation rule for infrastructure applies without an agreed interruption and recovery procedure. GitHub concurrency controls
Configure backend locking separately. The Terraform S3 backend documents opt-in state locking through use_lockfile; it is not enabled merely by storing state in S3. HashiCorp now marks the older DynamoDB-based locking as deprecated and scheduled for removal in a future minor version. Confirm support in the Terraform version used by the engagement and the permissions needed for its lock object. Terraform S3 backend
Document every other route that can write the same state, including local operator access and other automation. A GitHub concurrency group is not a lock covering those routes. Agree who investigates an interrupted operation, what evidence they inspect, and how further changes remain paused until the state and infrastructure are understood.
Test the boundaries before expanding
Agree the evidence for the pilot before implementation. A useful acceptance review covers both the intended path and the cases that must stop:
- Approved source and target. Record the expected role, account, environment, and Terraform root for the run.
- Unapproved execution context. Show that attempted access is rejected and cannot reach the apply path.
- Missing approval or changed plan. Show that apply does not proceed and a replacement plan returns to review.
- Competing change. Check that concurrency and backend-lock behavior match the agreed operating procedure.
- Failed or interrupted run. Have the team locate evidence, identify the recovery owner, and follow the pause procedure.
- Handoff and access removal. Have client operators run the workflow and revoke delivery access from client-controlled systems.
Choose low-impact test changes and explicit authorization for each exercise. Retain results and unresolved exceptions in the client's repository or agreed work system. The Ownership Standard describes the wider expectations for inspectable work, dependency disclosure, and handoff.
Define the GitOps boundary
If the engagement also calls for GitOps, identify the system that continuously reconciles the desired state. The OpenGitOps principles include declarative, versioned state that agents pull and continuously reconcile. A workflow that runs Terraform after a Git change does not, by itself, establish all of those behaviors. Scope reconciliation, drift handling, and operating responsibility explicitly. OpenGitOps principles
Scope the engagement around acceptance
Uptempo Cloud is an independent AWS professional services firm specializing in DevOps, Terraform, GitHub Actions, GitOps, landing zones, and platform engineering.
Uptempo's GitHub Actions and CI/CD service and Terraform Consulting service cover workflow design, AWS identity, reviewable plans, controlled applies, and handoff. Through Professional Services, define the repositories, environments, deliverables, exclusions, and acceptance evidence in the statement of work. A bounded pilot and its operating documentation can form an agreed project scope; the checklist above does not create a standard package or guarantee.
For recurring improvements, Managed DevOps provides a monthly engineering-hour pool with a jointly prioritized Jira backlog and visible progress and time reporting. Delivery follows the agreed business-hours boundary; it does not include monitoring or pager coverage. To discuss the work, Book Your Free AWS Assessment: one hour of verbal findings and recommendations, with no written deliverable.