How to Fix 'No Valid Credential Sources for Terraform AWS Provider' (Complete Debugging Guide)
Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 5–15 mins
TL;DR
- What broke: Terraform's AWS provider credential chain exhausted every lookup path — env vars, shared credentials file, instance profile, ECS task role, OIDC — and found nothing valid.
- How to fix it: Explicitly supply credentials via environment variables, an IAM instance/task role, or a properly configured OIDC federation block in the provider; never hardcode keys.
- Shortcut: Use our Client-Side Sandbox below to auto-refactor this — paste your provider block and get a corrected config without leaking secrets to any server.
The Incident (What Does This Error Mean?)
Error: configuring Terraform AWS Provider:
no valid credential sources for Terraform AWS Provider found.
Please see https://registry.terraform.io/providers/hashicorp/aws/latest/docs#authentication
for valid configurations.
Error: no EC2 IMDS role found, operation error ec2imds: GetMetadata
Terraform's AWS provider walks a strict credential chain at plan/apply time:
- Static keys in the
providerblock AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEYenv varsAWS_PROFILE/~/.aws/credentialsshared file- ECS task role via container metadata endpoint
- EC2 instance profile via IMDSv2
- Web Identity Token (OIDC) via
AWS_WEB_IDENTITY_TOKEN_FILE
When every single one of those returns empty or errors, you get this message. The immediate consequence: terraform plan exits non-zero, your pipeline is dead, and no state changes can occur. In a CD pipeline this means a deployment freeze on whatever environment this provider targets.
The Attack Vector / Blast Radius
This error itself is not a direct exploit vector — but the conditions that cause it often are:
- Hardcoded keys that got rotated or deleted — the old
access_key/secret_keyin the provider block are now dead. If those keys were ever committed to git, they are already in your history and potentially harvested by secret-scanning bots. Rotation fixed the auth, but the leaked key window already existed. - Overly broad keys with no rotation policy — teams that hardcode
AWS_ACCESS_KEY_IDin CI/CD env vars tend to use long-lived, over-privileged keys. A single CI/CD platform breach (e.g., compromised GitHub Actions secret) hands an attacker static credentials with no expiry. - IMDS disabled on hardened AMIs — if your security team disabled IMDSv1 and your Terraform runner is not using IMDSv2 or has
HttpTokens=requiredwithout the SDK configured for it, the instance profile lookup silently fails. - Blast radius of a successful exploit: An attacker with the leaked key inherits whatever IAM policy is attached — commonly
AdministratorAccesson poorly governed accounts. Full account takeover in under 60 seconds viaaws iam create-user+ new access key.
How to Fix It (The Solution)
Basic Fix — Environment Variables (Quickest Unblock)
Set credentials in the shell or CI/CD secret store, keep the provider block clean:
- provider "aws" {
- region = "us-east-1"
- access_key = "AKIAIOSFODNN7EXAMPLE"
- secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
- }
+ provider "aws" {
+ region = "us-east-1"
+ # Credentials resolved from environment:
+ # AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN
+ }
Then in your shell or CI secret injection:
export AWS_ACCESS_KEY_ID="$(vault kv get -field=key secret/aws/terraform)"
export AWS_SECRET_ACCESS_KEY="$(vault kv get -field=secret secret/aws/terraform)"
export AWS_SESSION_TOKEN="$(vault kv get -field=token secret/aws/terraform)"
Enterprise Best Practice — OIDC Federation (GitHub Actions / GitLab CI)
Zero long-lived keys. The CI runner exchanges a short-lived OIDC JWT for an assumed IAM role.
IAM Trust Policy (attach to your Terraform execution role):
- # No trust policy — role not assumable by CI
+ {
+ "Version": "2012-10-17",
+ "Statement": [{
+ "Effect": "Allow",
+ "Principal": { "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com" },
+ "Action": "sts:AssumeRoleWithWebIdentity",
+ "Condition": {
+ "StringEquals": {
+ "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
+ },
+ "StringLike": {
+ "token.actions.githubusercontent.com:sub": "repo:your-org/your-repo:*"
+ }
+ }
+ }]
+ }
GitHub Actions workflow:
- env:
- AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
- AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ permissions:
+ id-token: write
+ contents: read
+ steps:
+ - name: Configure AWS credentials via OIDC
+ uses: aws-actions/configure-aws-credentials@v4
+ with:
+ role-to-assume: arn:aws:iam::123456789012:role/terraform-ci-role
+ aws-region: us-east-1
Terraform provider block (no changes needed — env vars are set by the action):
- provider "aws" {
- region = "us-east-1"
- access_key = var.aws_access_key
- secret_key = var.aws_secret_key
- }
+ provider "aws" {
+ region = "us-east-1"
+ }
For EC2/ECS runners — ensure IMDSv2 is reachable:
- # Instance launched with HttpTokens=optional or IMDS disabled
+ resource "aws_instance" "terraform_runner" {
+ metadata_options {
+ http_endpoint = "enabled"
+ http_tokens = "required" # IMDSv2 enforced
+ http_put_response_hop_limit = 1
+ }
+ }
And ensure the AWS SDK/Terraform version is ≥ 4.x (IMDSv2-aware by default).
💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.
Prevention in CI/CD
1. Checkov — Block Hardcoded Keys at PR Time
Add to your pipeline:
- name: Checkov Scan
uses: bridgecrewio/checkov-action@master
with:
directory: .
check: CKV_AWS_41 # Ensure no hardcoded credentials in provider block
CKV_AWS_41 fails the build if access_key or secret_key are literal strings in any .tf file.
2. tfsec
tfsec . --include-passed --minimum-severity HIGH
# Flags: aws-iam-no-user-attached-policies, general-secrets-sensitive-in-attribute
3. OPA / Conftest Policy
# policy/no_hardcoded_aws_keys.rego
package terraform.aws.provider
deny[msg] {
input.resource_changes[_].type == "provider"
creds := input.resource_changes[_].change.after
creds.access_key != ""
msg := "DENY: Hardcoded AWS access_key in provider block. Use OIDC or instance profile."
}
Run in CI:
terraform show -json tfplan.binary | conftest test --policy policy/ -
4. git-secrets / Gitleaks Pre-commit Hook
gitleaks protect --staged --redact
# Blocks commits containing AKIA* patterns (AWS access key IDs)
5. Enforce required_providers Version Pinning
Older AWS provider versions had buggy IMDS fallback. Pin to a known-good version:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.30.0" # Full IMDSv2 + OIDC support
}
}
}