Goal and architecture
GitHub Actions → GitHub OIDC token → AWS STS → scoped IAM role → private S3 artifact. This is a learning deployment, not a public website.
Prerequisites
Terraform 1.5.7 or newer, AWS CLI v2 with an authenticated sandbox profile, and a GitHub repository whose main branch you control. Your local AWS identity needs permission to manage IAM and S3. Initial provisioning runs locally; GitHub receives only artifact permissions.
Download and setup
Download the complete lab. Extract it and open its github-oidc folder.
- Check your account with
aws sts get-caller-identity. - Inspect IAM → Identity providers. If
token.actions.githubusercontent.comexists, setexisting_provider_arnto that ARN; the lab will reuse it without deleting it on cleanup. - Create
terraform.tfvarsusing the example below. Match your repository’s actual subject format: older repositories may userepo:OWNER/REPO:ref:refs/heads/main; repositories with immutable claims userepo:OWNER@OWNER_ID/REPO@REPO_ID:ref:refs/heads/main. Find numeric IDs withgh api repos/OWNER/REPO --jq '{owner_id: .owner.id, repo_id: .id}'and check your repository’s OIDC settings. Do not add a GitHub environment to this workflow; that changes its subject.
region = "us-east-1"
github_subject = "repo:OWNER@OWNER_ID/REPO@REPO_ID:ref:refs/heads/main"
# existing_provider_arn = "arn:aws:iam::ACCOUNT:oidc-provider/token.actions.githubusercontent.com"
terraform init
terraform validate
terraform plan -out=lab.tfplan
terraform apply lab.tfplan
terraform output
- In GitHub repository Settings → Secrets and variables → Actions → Variables, create
LAB_ROLE_ARNandLAB_BUCKETfrom Terraform outputs; setLAB_AWS_REGIONto your chosen region. - Copy
deploy.ymlinto.github/workflows/deploy.ymlin that repository and commit it to main. - Run OIDC deployment lab from the Actions tab, choosing main.
Validate
The workflow prints its assumed-role identity, uploads demo/hello.txt, downloads it, and compares both files. A successful run proves read/write access to the demo prefix. The bucket remains private. Running from another branch skips deployment; other repositories cannot satisfy the exact trust condition.
Troubleshooting
For an STS access-denied response, compare the exact subject, audience, branch, role ARN, and id-token: write permission. If IAM reports that the provider exists, set existing_provider_arn. For S3 access denied, verify the bucket variable and the demo/ prefix. Allow a short time for IAM changes to propagate before retrying.
Costs and cleanup
S3 storage and requests can incur charges. The bucket is disposable: force_destroy deliberately removes all its objects during destruction.
Disable or remove the workflow first, then run:
terraform plan -destroy
terraform destroy
Confirm Terraform reports no managed resources with terraform state list. Remove the three GitHub variables. A reused identity provider remains intact. Keep your Terraform state until cleanup is complete.
References and verification
This lab has not been deployed to AWS as part of the portfolio build. Provider and static checks are separate from end-to-end verification.