| Tested on | Ubuntu 26.04 LTS (Resolute Raccoon) |
|---|---|
| Package | terraform 1.16.1hashicorp/aws 6.63.0awscli 2.36.38 |
| Applies to | Any host with Terraform and the AWS provider; examples use ap-south-1 |
| Lab environment | Ubuntu workstation with AWS account access — Terraform lab environment on Ubuntu |
| Privilege | AWS identity with sts:GetCallerIdentity; normal user on the workstation |
| Scope | Reproduce No valid credential sources found for the HashiCorp AWS provider, then fix it with shared credentials, environment variables, named profiles, aws configure export-credentials, IAM Identity Center (SSO) profiles, and EC2 instance roles — including precedence when more than one source is present. Does not cover IAM policy design, multi-account organization setup, or non-AWS providers. |
The first time you point Terraform at AWS, this error stops the run cold:
Error: No valid credential sources foundIt usually means the AWS provider plugin could not find keys, a profile, SSO session, or an instance role — not that Terraform itself is broken. The confusing part is that aws sts get-caller-identity may still work in the same terminal while terraform plan fails.
This guide walks through every common credential source beginners use and how to wire each one into Terraform.
What this Terraform AWS error means
When Terraform plans or applies an AWS resource, the provider must sign API requests. If every credential source in the chain is empty or unreachable, you get:
Error: No valid credential sources found— headline from the AWS provider- A second line about EC2 IMDS on laptops and CI runners — Terraform also tried the instance metadata service at
169.254.169.254and failed
That IMDS tail is normal on a workstation. It does not mean you must run Terraform on EC2 — it only means no instance role was found as a last resort.
Common situations that trigger it:
~/.aws/credentialsis missing or empty- You signed in with
aws loginbut never exported session variables for Terraform AWS_PROFILEorprofilein the provider block points at a profile that does not exist- Environment variables were set in one shell tab but Terraform runs in another
- You copied a tutorial that assumes an EC2 instance role you do not have
Reproduce the credential error
From your Terraform project directory, add a minimal AWS data source if you need a quick test target:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
provider "aws" {
region = "ap-south-1"
}
data "aws_caller_identity" "current" {}
output "account_id" {
value = data.aws_caller_identity.current.account_id
}Initialize providers:
terraform initStrip every AWS credential source and run plan — this simulates a clean CI job or a new laptop:
env -i HOME="$HOME" PATH="$PATH" USER="$USER" \
AWS_CONFIG_FILE=/dev/null AWS_SHARED_CREDENTIALS_FILE=/dev/null \
terraform planSample output:
Error: No valid credential sources found
with provider["registry.terraform.io/hashicorp/aws"],
on main.tf line 10, in provider "aws":
10: provider "aws" {
Error: failed to refresh cached credentials, no EC2 IMDS role found,
operation error ec2imds: GetMetadata, exceeded maximum number of attempts, 3,
request send failed, Get
"http://169.254.169.254/latest/meta-data/iam/security-credentials/": dial tcp
169.254.169.254:80: connect: connection refusedYou have reproduced the failure. The fixes below add credentials back one source at a time.
AWS provider credential precedence
The HashiCorp AWS provider uses the AWS SDK for Go v2 credential chain. HashiCorp documents this precedence order:
| Priority | Source | Typical example |
|---|---|---|
| 1 | Provider configuration | Explicit credentials, profile, or assume_role in provider "aws" |
| 2 | Environment / web identity | AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, or OIDC via AWS_WEB_IDENTITY_TOKEN_FILE |
| 3 | Shared credentials | ~/.aws/credentials |
| 4 | Shared config | Profiles, SSO, assume_role, credential_process in ~/.aws/config |
| 5 | Container credentials | ECS task role |
| 6 | Instance metadata | EC2 instance profile (IMDS) |
Precedence gotchas worth remembering:
- If the provider block sets
profile = "my-profile", that profile must exist in the shared files — valid environment variables alone do not override a missing profile name - If
profileis not set, environment variables can satisfy Terraform even when credential files are absent - The AWS CLI and Terraform can use many of the same AWS configuration files and environment variables, but they do not use the same underlying SDK. If the CLI works while Terraform fails, compare
AWS_*variables,AWS_PROFILE, the shared config/credentials files, and anyprofileargument in the provider block
Fix with ~/.aws/credentials
If you already use an IAM access key, the AWS provider can read it from ~/.aws/credentials. Long-lived access keys are not the preferred setup for new local-development environments, but this remains a common source worth checking when troubleshooting older configurations.
After installing the AWS CLI, you can create or update a profile with:
aws configureThat creates entries like:
[default]
aws_access_key_id = AKIAEXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEYAnd region in ~/.aws/config:
[default]
region = ap-south-1
output = jsonWith files in place, run plan from the same directory (no env -i wrapper):
terraform planSample output:
Changes to Outputs:
+ account_id = "342524208658"
Plan: 0 to add, 0 to change, 0 to destroy.The plan succeeds because Terraform reads the default profile from the shared credentials file.
Fix with AWS environment variables
CI pipelines, containers, and GitHub Actions often supply temporary credentials as environment variables instead of writing ~/.aws/credentials.
Export credentials for the current shell:
export AWS_ACCESS_KEY_ID="AKIAEXAMPLE"
export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
export AWS_DEFAULT_REGION="ap-south-1"If you use temporary credentials (SSO or assumed role), also set AWS_SESSION_TOKEN.
Run plan with credential files hidden to prove env vars alone are enough:
AWS_CONFIG_FILE=/dev/null AWS_SHARED_CREDENTIALS_FILE=/dev/null terraform planSample output:
Changes to Outputs:
+ account_id = "342524208658"On a real workstation, the fastest way to populate those variables from an existing CLI session is:
eval "$(aws configure export-credentials --format env)"That one line is the fix when aws sts get-caller-identity works but Terraform still reports no credential sources — common after aws login on CLI v2.32+.
Fix with a named AWS profile
A named profile selects a specific AWS configuration when one workstation uses multiple identities or accounts. The simplest legacy example is an access-key profile in ~/.aws/credentials:
[terraform-lab]
aws_access_key_id = AKIAEXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEYProfiles can also represent IAM Identity Center, assume_role chains, or credential_process entries in ~/.aws/config — the SSO section below covers that pattern.
Point the provider at a credentials-file profile:
provider "aws" {
region = "ap-south-1"
profile = "terraform-lab"
}Run plan with explicit file paths if you keep profiles outside the default location:
export AWS_SHARED_CREDENTIALS_FILE=~/.aws/credentials
export AWS_CONFIG_FILE=~/.aws/config
terraform planTerraform loads terraform-lab from the credentials file. A typo in the profile name fails immediately:
Error: failed to get shared config profile, nonexistent-profileFix the spelling in provider "aws" or create the missing profile — environment variables do not rescue a bad profile argument.
Fix when aws login works but Terraform fails
Modern AWS CLI can sign you in without long-lived access keys — see AWS CLI tutorial. aws login stores credentials in the AWS login cache. The Terraform AWS provider's Go SDK credential chain does not currently consume that login provider directly, so export the active CLI credentials into standard environment variables:
eval "$(aws configure export-credentials --format env)"That export is the common fix when:
aws sts get-caller-identity # works
terraform plan # failsVerify the CLI identity first:
aws sts get-caller-identityRun plan again after export:
terraform planThe same pattern appears in cloud guides such as deploy EKS with Terraform — export credentials in every shell (or CI step) before terraform init, plan, or apply.
For scripts, chain export and Terraform in one line:
eval "$(aws configure export-credentials --format env)" && terraform planFix with IAM Identity Center SSO
Teams using IAM Identity Center (formerly AWS SSO) store SSO settings in ~/.aws/config, not in credentials:
[profile corp-sso]
sso_start_url = https://my-org.awsapps.com/start
sso_region = ap-south-1
sso_account_id = 123456789012
sso_role_name = AdministratorAccess
region = ap-south-1Workflow:
- Log in:
aws sso login --profile corp-sso - Point Terraform at the profile:
provider "aws" {
region = "ap-south-1"
profile = "corp-sso"
}- Run
terraform planin the same shell session before the SSO token expires
Alternatively, after aws sso login, use eval "$(aws configure export-credentials --profile corp-sso --format env)" so Terraform reads standard AWS_* variables.
Fix with an EC2 instance role
On an EC2 instance with an instance profile attached, Terraform needs no access keys in files or environment variables. The provider obtains temporary credentials through the instance metadata service (IMDS) automatically.
Minimal provider block on EC2:
provider "aws" {
region = "ap-south-1"
}Confirm the instance is using a role — with no file or environment credentials configured, run:
aws sts get-caller-identityIf that succeeds on EC2, the CLI and provider may both be using the attached instance role.
On a laptop, the IMDS connection refused line inside the original error is normal — there is no instance role to fall back to. Run Terraform from EC2 (or supply credentials another way) when you need AWS API access from your workstation.
Compare AWS credential methods
| Method | Best for | Terraform setup |
|---|---|---|
~/.aws/credentials |
Legacy access-key setups, older labs | Default provider block; optional profile |
| Environment variables | CI/CD, containers, OIDC-fed pipelines | export or eval "$(aws configure export-credentials …)" |
| Named profile | Multiple identities on one machine | profile = "name" in provider block |
aws login / SSO |
Temporary console or SSO sessions | aws login or aws sso login, then export env vars |
| EC2 instance role | Runs on EC2 / some CI agents | Provider block only; attach IAM instance profile |
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
aws sts works, Terraform fails |
Session lives in CLI cache only | eval "$(aws configure export-credentials --format env)" |
failed to get shared config profile |
profile in provider block is wrong |
Fix profile name or add section to ~/.aws/config |
| Worked yesterday, fails today | Expired SSO or assumed-role session | Re-run aws sso login or refresh credentials |
| IMDS error on a laptop | Normal — no EC2 role present | Use credentials file, env vars, or SSO — not IMDS |
| Worked in terminal A, not B | Variables not exported in second shell | Export AWS_* in the same shell that runs Terraform |
AccessDenied instead of no credentials |
Auth succeeded; IAM policy denies API | Attach permissions — different problem than missing keys |
Summary
No valid credential sources found means the AWS provider exhausted its credential chain — shared files, environment variables, SSO profiles, and finally EC2 metadata. Reproduce it by hiding ~/.aws/* and unsetting AWS_* variables, then fix it by supplying one valid source.
On a developer laptop, eval "$(aws configure export-credentials --format env)" after aws login covers many modern setups; older ~/.aws/credentials access-key profiles still appear in the wild. In CI, prefer temporary credentials obtained through workload federation/OIDC or an assigned role. When the CI platform supplies temporary AWS credentials as environment variables, the provider reads AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN. On EC2, attach an instance profile and keep keys out of code entirely.
When the CLI and Terraform disagree, compare the provider profile argument, active environment variables, and whether the shell running Terraform is the same one where you signed in. For backend and state issues after auth works, continue with Configure S3 bucket as Terraform backend.
References
- Terraform AWS provider — authentication
- AWS CLI configuration files
- AWS CLI
configure export-credentials - IAM Identity Center credential flows
- EC2 instance metadata IAM roles
Frequently Asked Questions
Why does Terraform try EC2 metadata on my laptop?
The AWS provider's credential chain checks every source in order, including the instance metadata service at 169.254.169.254. On non-EC2 hosts that request fails quickly. It is a fallback message, not a requirement to use EC2.
Is it safe to put access keys in the provider block?
Terraform accepts access_key and secret_key in provider "aws", but you should avoid it. Hard-coded credentials can leak through source control, copied configuration files, logs, or other tooling. Prefer environment variables, profiles, temporary credentials, or workload roles.
Do I need different credentials for the S3 backend and the AWS provider?
Usually no. The S3 backend and AWS provider can both use the standard AWS credential chain, so the same identity often authenticates both. They are configured separately, however, so you can give the backend a different profile, assume_role configuration, or other credential settings when needed.
Does terraform validate catch missing AWS credentials?
No. validate checks configuration syntax without calling AWS APIs. The credential error appears on plan or apply when the provider contacts AWS.

