provider "aws" { region = "us-east-1" assume_role { role_arn = "arn:aws:iam::<account_id>:role/deployment" session_name = "deployment" }
}
provider "aws" { region = "us-east-1" assume_role { role_arn = "arn:aws:iam::<account_id>:role/deployment" session_name = "deployment" }
}
provider "aws" { region = "us-east-1" assume_role { role_arn = "arn:aws:iam::<account_id>:role/deployment" session_name = "deployment" }
}
backend "s3" { bucket = "my-tf-states" region = "us-east-1" key = "core.tfstate" dynamodb_table = "terraform-locks"
}
backend "s3" { bucket = "my-tf-states" region = "us-east-1" key = "core.tfstate" dynamodb_table = "terraform-locks"
}
backend "s3" { bucket = "my-tf-states" region = "us-east-1" key = "core.tfstate" dynamodb_table = "terraform-locks"
}
- name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-1
- name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-1
- name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-1
module "my_module" { source = "s3::/my-tf-modules/1.0.41/my-module.zip"
}
module "my_module" { source = "s3::/my-tf-modules/1.0.41/my-module.zip"
}
module "my_module" { source = "s3::/my-tf-modules/1.0.41/my-module.zip"
}
Local Development: Engineer -> AWS SSO Login -> Temporary Credentials -> Terraform CI/CD (GitHub Actions): GitHub Actions -> OIDC Token -> AWS STS -> Temporary Credentials -> Terraform
Local Development: Engineer -> AWS SSO Login -> Temporary Credentials -> Terraform CI/CD (GitHub Actions): GitHub Actions -> OIDC Token -> AWS STS -> Temporary Credentials -> Terraform
Local Development: Engineer -> AWS SSO Login -> Temporary Credentials -> Terraform CI/CD (GitHub Actions): GitHub Actions -> OIDC Token -> AWS STS -> Temporary Credentials -> Terraform
provider "aws" { region = "us-east-1" assume_role { role_arn = "arn:aws:iam::<account_id>:role/deployment" session_name = "deployment" }
}
provider "aws" { region = "us-east-1" assume_role { role_arn = "arn:aws:iam::<account_id>:role/deployment" session_name = "deployment" }
}
provider "aws" { region = "us-east-1" assume_role { role_arn = "arn:aws:iam::<account_id>:role/deployment" session_name = "deployment" }
}
provider "aws" { region = "us-east-1"
}
provider "aws" { region = "us-east-1"
}
provider "aws" { region = "us-east-1"
}
backend "s3" { bucket = "my-tf-states" region = "us-east-1" key = "core.tfstate" dynamodb_table = "terraform-locks" profile = "shared-account"
}
backend "s3" { bucket = "my-tf-states" region = "us-east-1" key = "core.tfstate" dynamodb_table = "terraform-locks" profile = "shared-account"
}
backend "s3" { bucket = "my-tf-states" region = "us-east-1" key = "core.tfstate" dynamodb_table = "terraform-locks" profile = "shared-account"
}
{ "Version": "2012-10-17", "Statement": [ { "Sid": "TerraformStateAccess", "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::<dev_account_id>:root", "arn:aws:iam::<live_account_id>:root" ] }, "Action": [ "s3:ListBucket", "s3:GetObject", "s3:PutObject", "s3:DeleteObject" ], "Resource": [ "arn:aws:s3:::my-tf-states", "arn:aws:s3:::my-tf-states/*" ] } ]
}
{ "Version": "2012-10-17", "Statement": [ { "Sid": "TerraformStateAccess", "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::<dev_account_id>:root", "arn:aws:iam::<live_account_id>:root" ] }, "Action": [ "s3:ListBucket", "s3:GetObject", "s3:PutObject", "s3:DeleteObject" ], "Resource": [ "arn:aws:s3:::my-tf-states", "arn:aws:s3:::my-tf-states/*" ] } ]
}
{ "Version": "2012-10-17", "Statement": [ { "Sid": "TerraformStateAccess", "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::<dev_account_id>:root", "arn:aws:iam::<live_account_id>:root" ] }, "Action": [ "s3:ListBucket", "s3:GetObject", "s3:PutObject", "s3:DeleteObject" ], "Resource": [ "arn:aws:s3:::my-tf-states", "arn:aws:s3:::my-tf-states/*" ] } ]
}
terraform init -reconfigure
terraform init -reconfigure
terraform init -reconfigure
# Dev account
[profile dev]
sso_start_url = https://your-org.awsapps.com/start/#/
sso_region = us-east-1
sso_account_id = <dev_account_id>
sso_role_name = SuperAdmin
region = us-east-1 # Production account
[profile prod]
sso_start_url = https://your-org.awsapps.com/start/#/
sso_region = us-east-1
sso_account_id = <live_account_id>
sso_role_name = SuperAdmin
region = us-east-1 # Shared account (for Terraform state backend)
[profile shared-account]
sso_start_url = https://your-org.awsapps.com/start/#/
sso_region = us-east-1
sso_account_id = <shared_account_id>
sso_role_name = SuperAdmin
region = us-east-1
# Dev account
[profile dev]
sso_start_url = https://your-org.awsapps.com/start/#/
sso_region = us-east-1
sso_account_id = <dev_account_id>
sso_role_name = SuperAdmin
region = us-east-1 # Production account
[profile prod]
sso_start_url = https://your-org.awsapps.com/start/#/
sso_region = us-east-1
sso_account_id = <live_account_id>
sso_role_name = SuperAdmin
region = us-east-1 # Shared account (for Terraform state backend)
[profile shared-account]
sso_start_url = https://your-org.awsapps.com/start/#/
sso_region = us-east-1
sso_account_id = <shared_account_id>
sso_role_name = SuperAdmin
region = us-east-1
# Dev account
[profile dev]
sso_start_url = https://your-org.awsapps.com/start/#/
sso_region = us-east-1
sso_account_id = <dev_account_id>
sso_role_name = SuperAdmin
region = us-east-1 # Production account
[profile prod]
sso_start_url = https://your-org.awsapps.com/start/#/
sso_region = us-east-1
sso_account_id = <live_account_id>
sso_role_name = SuperAdmin
region = us-east-1 # Shared account (for Terraform state backend)
[profile shared-account]
sso_start_url = https://your-org.awsapps.com/start/#/
sso_region = us-east-1
sso_account_id = <shared_account_id>
sso_role_name = SuperAdmin
region = us-east-1
# Login to SSO (opens browser for authentication)
aws sso login --profile dev
aws sso login --profile shared-account # IMPORTANT: Clear any old static credentials first
unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
unset AWS_SESSION_TOKEN # Set the profile for the target account
export AWS_PROFILE=dev # Verify you're using the SSO role (not the old IAM user)
aws sts get-caller-identity
# Should show: arn:aws:sts::<dev_account_id>:assumed-role/AWSReservedSSO_SuperAdmin_.../[email protected] # Run Terraform
terraform init
terraform workspace select dev
terraform plan
terraform apply
# Login to SSO (opens browser for authentication)
aws sso login --profile dev
aws sso login --profile shared-account # IMPORTANT: Clear any old static credentials first
unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
unset AWS_SESSION_TOKEN # Set the profile for the target account
export AWS_PROFILE=dev # Verify you're using the SSO role (not the old IAM user)
aws sts get-caller-identity
# Should show: arn:aws:sts::<dev_account_id>:assumed-role/AWSReservedSSO_SuperAdmin_.../[email protected] # Run Terraform
terraform init
terraform workspace select dev
terraform plan
terraform apply
# Login to SSO (opens browser for authentication)
aws sso login --profile dev
aws sso login --profile shared-account # IMPORTANT: Clear any old static credentials first
unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
unset AWS_SESSION_TOKEN # Set the profile for the target account
export AWS_PROFILE=dev # Verify you're using the SSO role (not the old IAM user)
aws sts get-caller-identity
# Should show: arn:aws:sts::<dev_account_id>:assumed-role/AWSReservedSSO_SuperAdmin_.../[email protected] # Run Terraform
terraform init
terraform workspace select dev
terraform plan
terraform apply
module "oidc_github" { source = "unfunco/oidc-github/aws" version = "1.8.0" github_repositories = [ "your-org/your-terraform-repo" ] attach_admin_policy = true
} output "oidc_role_arn" { value = module.oidc_github.iam_role_arn
}
module "oidc_github" { source = "unfunco/oidc-github/aws" version = "1.8.0" github_repositories = [ "your-org/your-terraform-repo" ] attach_admin_policy = true
} output "oidc_role_arn" { value = module.oidc_github.iam_role_arn
}
module "oidc_github" { source = "unfunco/oidc-github/aws" version = "1.8.0" github_repositories = [ "your-org/your-terraform-repo" ] attach_admin_policy = true
} output "oidc_role_arn" { value = module.oidc_github.iam_role_arn
}
permissions: contents: read steps: - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-1
permissions: contents: read steps: - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-1
permissions: contents: read steps: - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-1
permissions: id-token: write # Required for OIDC contents: read steps: - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }} aws-region: us-east-1
permissions: id-token: write # Required for OIDC contents: read steps: - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }} aws-region: us-east-1
permissions: id-token: write # Required for OIDC contents: read steps: - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }} aws-region: us-east-1
An argument named "enable_classiclink" is not expected here.
An argument named "enable_classiclink" is not expected here.
An argument named "enable_classiclink" is not expected here.
# Provider: 4.67 -> 5.x
required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" }
} # VPC module: 3.18.1 -> 5.16.0
module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "5.16.0"
}
# Provider: 4.67 -> 5.x
required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" }
} # VPC module: 3.18.1 -> 5.16.0
module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "5.16.0"
}
# Provider: 4.67 -> 5.x
required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" }
} # VPC module: 3.18.1 -> 5.16.0
module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "5.16.0"
}
terraform init -upgrade
terraform init -upgrade
terraform init -upgrade
module "my_module" { source = "s3::/my-tf-modules/1.0.41/my-module.zip"
}
module "my_module" { source = "s3::/my-tf-modules/1.0.41/my-module.zip"
}
module "my_module" { source = "s3::/my-tf-modules/1.0.41/my-module.zip"
}
NoCredentialProviders: no valid providers in chain
NoCredentialProviders: no valid providers in chain
NoCredentialProviders: no valid providers in chain
module "my_module" { source = "github.com/your-org/your-tf-modules//my-module?ref=v1.0.93"
}
module "my_module" { source = "github.com/your-org/your-tf-modules//my-module?ref=v1.0.93"
}
module "my_module" { source = "github.com/your-org/your-tf-modules//my-module?ref=v1.0.93"
}
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
AccessDeniedException: User is not authorized to perform: dynamodb:PutItem
AccessDeniedException: User is not authorized to perform: dynamodb:PutItem
AccessDeniedException: User is not authorized to perform: dynamodb:PutItem
eval "$(aws configure export-credentials --profile dev --format env)"
terraform init
eval "$(aws configure export-credentials --profile dev --format env)"
terraform init
eval "$(aws configure export-credentials --profile dev --format env)"
terraform init
Error: Backend configuration changed
Error: Backend configuration changed
Error: Backend configuration changed - Shared/management account - Hosted the S3 state bucket, DynamoDB lock table, and custom Terraform modules in S3
- Dev account - Development environment
- Live/prod account - Production environment (with additional live-eu and live-dr workspaces) - No individual accountability - CloudTrail logs showed deployment user for every change, making it impossible to trace who did what
- Security risk - Static keys can leak, get committed to git, or be shared insecurely
- Key rotation pain - Rotating one shared key means updating it everywhere
- No MFA enforcement - Long-lived access keys bypass MFA requirements - Added id-token: write permission (required for GitHub to issue OIDC tokens)
- Replaced aws-access-key-id / aws-secret-access-key with role-to-assume - development environment: OIDC role ARN from your dev account
- production environment: OIDC role ARN from your prod account