Complete AWS infrastructure setup for storing Terraform state files remotely using S3 with versioning, encryption, and file-based locking
This directory contains Terraform configuration to provision the AWS infrastructure needed to store Terraform state files remotely. This includes an S3 bucket for state storage with file-based locking.
{prefix}-{account-id}-s3-tfstateuse_lockfile = true in the backend configuration
github-role containing role ARNsConfigure these at: Repository → Settings → Secrets and variables → Actions → Secrets
AWS_STATE_ACCOUNT_ROLE_ARN
arn:aws:iam::ACCOUNT_A_ID:role/github-actions-state-roleGH_TOKEN
repo scopeBACKEND_BUCKET_NAME repository variable after provisioningGITHUB_TOKEN may not have permissions to write repository variablesConfigure these at: Repository → Settings → Secrets and variables → Actions → Variables
AWS_REGION
us-east-1, us-west-2, eu-west-1variables.tfvars fileBACKEND_PREFIX
backend_state/terraform.tfstateBACKEND_BUCKET_NAME (Auto-generated)
Configure these required variables in variables.tfvars before running:
| Variable | Type | Description | Example |
|---|---|---|---|
env |
string | Deployment environment identifier | "prod", "dev", "staging" |
region |
string | AWS region for resource deployment | "us-east-1", "us-west-2" |
prefix |
string | Prefix added to all resource names for identification | "mycompany-tf", "project-name" |
https://token.actions.githubusercontent.comsts.amazonaws.comtoken.actions.githubusercontent.comsts.amazonaws.com from the dropdowntoken.actions.githubusercontent.com:subStringLikerepo:YOUR_ORG/YOUR_REPO:* (replace with your GitHub organization and repository name)github-actions-state-role (or your preferred name)AWS_STATE_ACCOUNT_ROLE_ARN GitHub secretThe bucket policy in main.tf automatically uses the current caller's ARN via data.aws_caller_identity.current.arn. No configuration needed!
For local development or testing, ensure you have:
# Install GitHub CLI (if not already installed)
# macOS: brew install gh
# Linux: See https://cli.github.com/manual/installation
# Authenticate with GitHub
gh auth login
gh)jq (for JSON parsing)github-role must exist in AWS Secrets ManagerAWS_STATE_ACCOUNT_ROLE_ARNsecretsmanager:GetSecretValue permission{
"AWS_STATE_ACCOUNT_ROLE_ARN": "arn:aws:iam::<account-id>:role/<role-name>"
}
This is the recommended approach as it handles state file upload automatically.
⚠️ Note: GitHub Actions workflows retrieve the role ARN directly from GitHub repository secrets (AWS_STATE_ACCOUNT_ROLE_ARN). This differs from local script execution, which uses AWS Secrets Manager.
⚠️ Warning: This permanently deletes the S3 bucket.
For local development or testing, use the provided automation scripts. These scripts handle role assumption, Terraform operations, state file management, and repository variable updates automatically.
⚠️ IMPORTANT:
get-state.sh and set-state.sh) retrieve the role ARN from AWS Secrets Manager (secret named 'github-role' with key 'AWS_STATE_ACCOUNT_ROLE_ARN'), not from GitHub repository secrets.AWS_STATE_ACCOUNT_ROLE_ARN).Use the set-state.sh script to provision infrastructure and upload the state file:
cd tf_backend_state
./set-state.sh
What the script does automatically:
AWS_STATE_ACCOUNT_ROLE_ARN from AWS Secrets Manager (secret 'github-role', key 'AWS_STATE_ACCOUNT_ROLE_ARN')AWS_REGION from GitHub repository variables (defaults to us-east-1)BACKEND_PREFIX from GitHub repository variablesterraform init, validate, plan, and applyBACKEND_BUCKET_NAMEterraform.tfstate to S3 (only if infrastructure was just provisioned)If you need to download the state file from S3 (e.g., after running via GitHub Actions), use the get-state.sh script:
cd tf_backend_state
./get-state.sh
What the script does automatically:
AWS_STATE_ACCOUNT_ROLE_ARN from AWS Secrets Manager (secret 'github-role', key 'AWS_STATE_ACCOUNT_ROLE_ARN')AWS_REGION from GitHub repository variables (defaults to us-east-1)BACKEND_BUCKET_NAME and BACKEND_PREFIX from GitHub repository variablesterraform.tfstate from S3 if it existsTo destroy the infrastructure, use Terraform directly (after downloading the state file if needed):
cd tf_backend_state
# Download state file if needed
./get-state.sh
# Destroy infrastructure
terraform plan -var-file="variables.tfvars" -destroy -out terraform.tfplan
terraform apply -auto-approve terraform.tfplan
⚠️ Warning: This permanently deletes the S3 bucket and all resources.
s3://{bucket-name}/{prefix}BACKEND_BUCKET_NAME repository variable${{ vars.BACKEND_BUCKET_NAME }}{prefix}{prefix}-{account-id}-s3-tfstate
mycompany-tf-123456789012-s3-tfstate.gitignore.gitignore).force_destroy = true, meaning it can be deleted even if it contains files. Use with caution.variables.tfvarsGH_TOKEN doesn't have proper permissions or doesn't existrepo scope and store it as GH_TOKEN secretdata.aws_caller_identity.current.arn. Verify this matches your expectations:
aws sts get-caller-identity to see your current ARNAWS_STATE_ACCOUNT_ROLE_ARN secret matches the assumed roleAWS_STATE_ACCOUNT_ROLE_ARN secret contains the correct role ARN (for GitHub Actions)github-role exists in AWS Secrets Managersecretsmanager:GetSecretValue permission for the github-role secretAWS_STATE_ACCOUNT_ROLE_ARN# Test secret retrieval manually
aws secretsmanager get-secret-value --secret-id github-role --query SecretString --output text | jq .
variables.tfvarsBACKEND_BUCKET_NAME variable exists and contains the correct bucket nameComplete project documentation with setup instructions and usage guide
All notable changes to the project, organized by version
| Action | Command/Workflow |
|---|---|
| Provision infrastructure (GitHub Actions) | Run "TF Backend State Provisioning" workflow |
| Provision infrastructure (Local) | ./set-state.sh |
| Download state file | ./get-state.sh |
| Destroy infrastructure (GitHub Actions) | Run "TF Backend State Destroying" workflow |
| Destroy infrastructure (Local) | terraform plan -destroy then terraform apply |
| View current AWS identity | aws sts get-caller-identity |
GitHub Repository: talorlik/tf_backend_state
This project is licensed under the MIT License.
See the LICENSE file for details.