DevOpsil

Terraform "Error: No Valid Credential Sources Found" On AWS — Fixing Authentication And Provider Configuration Issues

Majid Iqbal NayyarMajid Iqbal Nayyar7 min read

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:

  1. Static credentials in the provider block
  2. Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
  3. The shared credentials file (~/.aws/credentials)
  4. EC2 instance metadata (if running on EC2/ECS/Lambda)
  5. 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-identity work from the same terminal?
  • Are AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY exported?
  • 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 have sts:AssumeRole permission?
  • Is AWS_DEFAULT_REGION or region in 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.

Share:

Was this article helpful?

Majid Iqbal Nayyar
Majid Iqbal Nayyar

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

More in Terraform

View all →
TerraformQuick RefBeginnerNeeds Review

Terraform CLI: Cheat Sheet

Terraform CLI cheat sheet with commands organized by workflow — init, plan, apply, destroy, state manipulation, imports, and workspace management.

Zara Blackwood·
3 min read

Discussion