Solving Terraform: “No valid credential sources found for AWS Provider”

My Problem

Using Terraform v0.12 and attempting to use the AWS provider to init an S3 backend, I’m receiving the error:

Initializing the backend…

Error: No valid credential sources found for AWS Provider.
Please see for more information on providing credentials for the AWS Provider

I’m experimenting with providing static credentials in a .tf file (P.S. don’t do this in production) and I’ve verified that the AWS keys are correct.

My Solution

Preamble: The following is terrible, don’t do this. I’m writing this merely as an answer to something that was puzzling me.

Add access_key and secret_key to the Terraform backend block. E.g.:

terraform {
  backend "s3" {
    bucket = "your-bucket"
    region = "your-region"
    key = "yourkey/terraform.tfstate"
    dynamodb_table = "your-lock-table"
    encrypt = true
    access_key = "DONT_PUT_KEYS_IN_YOUR.TF_FILES"
    secret_key = "NO_REALLY_DONT"

This would be in addition to the keys that you’ve placed in your provider block:

provider "aws" {
   region = "us-east-1"
   access_key = "DONT_PUT_KEYS_IN_YOUR.TF_FILES"
   secret_key = "NO_REALLY_DONT"

The backend needs to be initialized before the provider plugin, so any keys in the provider block are not evaluated. The Terraform backend block needs to be provided with its own keys.

A better method for doing that would be using environmental variables, among other more secure methods (including the use of shared_credentials_file and a profile, such as what Martin Hall references in the comments below. You can also provide a partial configuration and then pass variables in via the command line.

The Long Story

There are a number of ways to provide Terraform with AWS credentials. The worst option is to use static credentials provided in your .tf files, so naturally that’s what I’m experimenting with.

One way to provide credentials is through environmental variables, and when I tested that method out, it worked! I’ll make use of environmental variables in the future (promise), but I want to figure out why static credentials aren’t working because… because.

Another way to provide AWS credentials is via the good ol’ shared credentials file located at .aws/credentials. Again, this works in my scenario but I’m stumped as to why static credentials won’t.

(Side note: At this point in the story, this is the universe telling me just how bad it is to use static credentials, but my preferred decision making methodology is to ignore such urgings.)

Let’s debug this sucker by setting the environmental variable TF_LOGS to trace: export TF_LOGS=trace

# terraform init
2020/05/21 06:26:58 [INFO] Terraform version: 0.12.25
2020/05/21 06:26:58 [INFO] Go runtime version: go1.12.13
2020/05/21 06:26:58 [INFO] CLI args: []string{"/usr/bin/terraform", "init"}
2020/05/21 06:26:58 [DEBUG] Attempting to open CLI config file: /root/.terraformrc
2020/05/21 06:26:58 [DEBUG] File doesn't exist, but doesn't need to. Ignoring.
2020/05/21 06:26:58 [INFO] CLI command args: []string{"init"}

Initializing the backend…

2020/05/21 06:26:58 [TRACE] Meta.Backend: built configuration for "s3" backend with hash value 953412181
2020/05/21 06:26:58 [TRACE] Preserving existing state lineage "da125f8e-6c56-d65a-c30b-77978250065c"
2020/05/21 06:26:58 [TRACE] Preserving existing state lineage "da125f8e-6c56-d65a-c30b-77978250065c"
2020/05/21 06:26:58 [TRACE] Meta.Backend: working directory was previously initialized for "s3" backend
2020/05/21 06:26:58 [TRACE] Meta.Backend: using already-initialized, unchanged "s3" backend configuration
2020/05/21 06:26:58 [INFO] Setting AWS metadata API timeout to 100ms
2020/05/21 06:27:00 [INFO] Ignoring AWS metadata API endpoint at default location as it doesn't return any instance-id
2020/05/21 06:27:00 [INFO] Attempting to use session-derived credentials

Error: No valid credential sources found for AWS Provider.
Please see for more information on providing credentials for the AWS Provider

Huh, it’s as if the backend section is totally ignoring my provider credentials.

It was then that I realized that the backend block has its own variables for keys. Well that’s weird. Why would it need its own definition of my provider’s keys when I already have keys placed in the “aws” provider block? Unless… Terraform doesn’t look at that block.

Some further research confirms that when a terraform backend is init’d, it’s executed before just about anything else (naturally), and there’s no sharing of provider credentials from a provider block even if the backend resides in the provider (E.g. a backend that uses Amazon S3 will not look to the AWS provider block for credentials).

Once I placed my AWS keys in the terraform backend block (don’t do that), things worked.