Terraform "Error: No Valid Credential Sources Found" On AWS — Fixing Authentication And Provider Configuration Issues
Terraform "Error: No valid credential sources found" on AWS — Fixing Authentication and Provider Configuration Issues
If you've spent any time with Terraform and AWS, you've almost certainly run into this gem at some point:
Error: No valid credential sources found
with provider["registry.terraform.io/hashicorp/aws"],
on main.tf line 1, in provider "aws":
1: provider "aws" {}
Please see https://registry.terraform.io/providers/hashicorp/aws
for more information about providing credentials.
It's one of those errors that looks simple but can have a dozen different root causes depending on your environment. Let me walk you through exactly what's happening, why it happens, and how to fix it properly — whether you're running locally, in CI/CD, or on EC2.
What's Actually Happening Here
The AWS Terraform provider uses the same credential resolution chain as the AWS SDK. When you run terraform plan or terraform apply, the provider tries to find valid credentials by checking several sources in order:
- Static credentials in the provider block
- Environment variables (
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY) - The shared credentials file (
~/.aws/credentials) - EC2 instance metadata (if running on EC2/ECS/Lambda)
- Container credentials (ECS task role)
If none of these sources yield valid credentials, you get the error above. The fix depends entirely on which source you intended to use.
Fix 1: Environment Variables (The CI/CD Standard)
For CI/CD pipelines and local development, environment variables are the most straightforward approach. Export them before running Terraform:
export AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"
export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
export AWS_DEFAULT_REGION="us-east-1"
Then your provider block stays clean:
provider "aws" {
region = "us-east-1"
}
One thing people miss: AWS_DEFAULT_REGION or AWS_REGION must be set either as an environment variable or explicitly in the provider block. Forgetting the region doesn't cause the credential error specifically, but it'll cause the next error you hit.
For GitHub Actions specifically, set your secrets in the repository settings and reference them like this:
- 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
This action handles credential injection properly and is far cleaner than manually exporting environment variables in your workflow.
Fix 2: AWS Credentials File
If you're working locally and use aws configure, your credentials live in ~/.aws/credentials. The file looks like this:
[default]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
[dev]
aws_access_key_id = AKIAI44QH8DHBEXAMPLE
aws_secret_access_key = je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEY
If you're using a named profile, tell Terraform about it:
provider "aws" {
region = "us-east-1"
profile = "dev"
}
Or set the environment variable:
export AWS_PROFILE=dev
Common gotcha: If the ~/.aws/credentials file exists but the profile you specified doesn't, Terraform will still throw the credential error. Double-check your profile names with:
aws configure list-profiles
Fix 3: Assumed Roles and Cross-Account Access
This is where things get interesting. A very common pattern in multi-account AWS setups is assuming a role. The error frequently appears here because people configure the assume_role block but forget that you still need base credentials to perform the assumption.
provider "aws" {
region = "us-east-1"
assume_role {
role_arn = "arn:aws:iam::123456789012:role/TerraformDeployRole"
session_name = "TerraformSession"
}
}
This configuration tells Terraform: "Use my current credentials to call sts:AssumeRole and get temporary credentials for this role." You still need valid initial credentials — the assume_role block doesn't replace them.
If your base credentials don't have permission to assume that role, you'll get a different error. But if you have no base credentials at all, you'll get the original "No valid credential sources found" error.
Fix 4: EC2 Instance Profiles and IRSA
If you're running Terraform on an EC2 instance, ECS task, or Lambda, attach an IAM role to the compute resource. The AWS provider will automatically pick up credentials from the instance metadata service.
No credentials configuration needed in the provider block:
provider "aws" {
region = "us-east-1"
}
If this isn't working on EC2, check that the instance has an IAM role attached:
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
If that returns nothing or errors out, the instance either has no role attached or the metadata service is misconfigured.
For Kubernetes workloads using IRSA (IAM Roles for Service Accounts), make sure the service account annotation is correct and the pod has the right service account. The AWS_ROLE_ARN and AWS_WEB_IDENTITY_TOKEN_FILE environment variables should be automatically injected by the EKS mutating webhook.
Fix 5: Static Credentials in the Provider Block (Use Carefully)
You can hardcode credentials directly in the provider block, but I'll say this clearly: don't do this in production or commit it to version control.
# DO NOT commit this to version control
provider "aws" {
region = "us-east-1"
access_key = "AKIAIOSFODNN7EXAMPLE"
secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
If you want to use static credentials without environment variables, use Terraform variables sourced from a secrets manager or .tfvars files that are excluded from git:
variable "aws_access_key" {
sensitive = true
}
variable "aws_secret_key" {
sensitive = true
}
provider "aws" {
region = "us-east-1"
access_key = var.aws_access_key
secret_key = var.aws_secret_key
}
Then pass them at runtime:
terraform apply \
-var="aws_access_key=$AWS_ACCESS_KEY_ID" \
-var="aws_secret_key=$AWS_SECRET_ACCESS_KEY"
Diagnosing the Actual Problem
Before you start shotgunning fixes, run this diagnostic sequence:
1. Check what credentials the AWS CLI sees:
aws sts get-caller-identity
If this fails too, the problem is your AWS credentials setup, not Terraform specifically.
2. Check environment variables:
env | grep AWS
Look for AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_PROFILE, and AWS_DEFAULT_REGION.
3. Enable Terraform debug logging:
export TF_LOG=DEBUG
terraform plan 2>&1 | grep -i "credential\|auth\|sts"
The debug output will show exactly which credential sources the provider is trying and where it's failing.
4. Verify the credentials file:
cat ~/.aws/credentials
cat ~/.aws/config
Make sure the profile you're referencing actually exists.
Provider Version Considerations
Occasionally this error appears after upgrading the AWS provider. The authentication behavior can change between major versions. Lock your provider version to avoid surprise breakages:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
If you recently upgraded and things broke, check the provider changelog. The jump from v3 to v4 and v4 to v5 both had meaningful changes to the credential chain behavior.
The Multi-Region and Multi-Account Pattern
Here's a pattern that catches people off guard. When you define multiple provider aliases for different regions or accounts, each alias needs its own credential source:
provider "aws" {
region = "us-east-1"
alias = "east"
}
provider "aws" {
region = "eu-west-1"
alias = "west"
assume_role {
role_arn = "arn:aws:iam::987654321098:role/TerraformRole"
}
}
If the west provider fails credential validation, you'll get the error even if your east provider is configured correctly. Each provider alias is independently validated.
Quick Reference Checklist
When you hit this error, work through this list:
- Does
aws sts get-caller-identitywork from the same terminal? - Are
AWS_ACCESS_KEY_IDandAWS_SECRET_ACCESS_KEYexported? - If using a profile, does it exist in
~/.aws/credentials? - If on EC2/ECS, does the instance/task have an IAM role attached?
- If using
assume_role, do your base credentials havests:AssumeRolepermission? - Is
AWS_DEFAULT_REGIONorregionin the provider block set? - Are you using a provider alias that might have its own credential requirements?
- Did you recently upgrade the AWS provider version?
Final Thoughts
The "No valid credential sources found" error is almost always an environment issue, not a Terraform code issue. The provider is working exactly as intended — it just can't find what it needs.
My recommendation for most teams: use environment variables in CI/CD (ideally via the aws-actions/configure-aws-credentials action or equivalent), use IAM instance profiles for compute-based runners, and use aws configure with named profiles for local development. Avoid hardcoding credentials anywhere they can leak.
Get the credential chain right once, document it for your team, and this error becomes a distant memory.
Was this article helpful?
Data Infrastructure Engineer
Your data is only as good as the infrastructure it sits on. I specialize in PostgreSQL, Redis, database migrations, backup strategies, and making sure your data survives whatever chaos your application throws at it.
Related Articles
Terraform Error: Invalid For_each Argument Must Be A Map Or Set Of Strings - Complete Fix Guide
If you've been working with Terraform's `for_each` argument, you've probably hit this error at least once. Don't worry – it's one of those frustrating but...
Terraform "Error: Cycle" In Resource Dependencies — How To Detect And Break Circular References
If you've spent any meaningful time with Terraform, you've probably encountered this at the worst possible moment: Everything was working fine, you added o...
Fix Terraform 'Error Acquiring the State Lock' with DynamoDB
Resolve Terraform DynamoDB state lock errors caused by permission issues, missing tables, or misconfigured backends.
Fix Terraform State Lock Stuck After Failed Apply
Safely unlock Terraform state when a lock is stuck after a crashed apply, CI timeout, or interrupted operation.
Terraform Dynamic Blocks For Managing Variable Infrastructure Resources
If you've been working with Terraform for a while, you've probably hit that wall where you need to create resources with configurations that vary based on...
Prometheus Target Down Error: Debugging Failed Scrapes And Network Connectivity Issues
You've deployed Prometheus, configured your targets, and everything looks perfect in your YAML files. Then you check the Prometheus UI and see those dreade...
More in Terraform
View all →Writing Reusable Terraform Modules: A Practical Guide
Learn how to write Terraform modules that are actually reusable — with input validation, sensible defaults, and clean interfaces across environments.
Terraform CLI: Cheat Sheet
Terraform CLI cheat sheet with commands organized by workflow — init, plan, apply, destroy, state manipulation, imports, and workspace management.
Terraform from Zero to Production: Project Structure, Modules, State, and CI/CD
Build production-grade Terraform infrastructure — project structure, module design, state management, testing, and CI/CD pipeline integration.
Testing Terraform with Terratest: A Practical Guide
How to write unit and integration tests for Terraform modules using Terratest — because untested infrastructure is a liability.