What the AWS provider does
The AWS provider is a translator plugin. You write resource "aws_s3_bucket" ... in HCL, and the provider turns that into an HTTP request to the AWS API, PUT /buckets/.... The plugin knows all 300+ AWS resource types: S3, EC2, IAM, Lambda, RDS, and so on.
The plugin is downloaded by terraform init (see tf-init) from the Terraform Registry.
Minimal configuration
terraform { required_providers { aws = {source = "hashicorp/aws"
version = "~> 5.60"
}
}
}
provider "aws" {region = "us-east-1"
}
No credentials are listed here. The provider finds them itself through the chain (see below).
Credentials chain: where the provider looks for keys
The AWS provider tries these places in order. The first one that works wins:
- Parameters in the provider block (
access_key,secret_key). Do not do this in production: the credentials end up in your code. - Environment variables:
AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYAWS_SESSION_TOKEN(for temporary credentials)AWS_DEFAULT_REGIONorAWS_REGION
- Shared credentials file
~/.aws/credentials(created byaws configure). - EC2 Instance Metadata Service (IMDS), when Terraform runs on an EC2 instance with an IAM role.
- ECS / EKS task role, for containerized environments.
The right approach for a developer:
aws configure # once: you enter the keys, and the provider finds them itself
The right approach for a production server: an IAM role on EC2, an ECS task role, or IRSA in EKS. The keys live nowhere; they are issued as short-lived credentials through IMDS.
Region: a required parameter
AWS services are regional: an S3 bucket in us-east-1 is not visible from eu-central-1. You can set the region three ways:
provider "aws" {region = "us-east-1"
}
or with an env var:
export AWS_DEFAULT_REGION=us-east-1
terraform apply
or in ~/.aws/config:
[default]
region = us-east-1
If the region is set nowhere, terraform plan fails with the error "no valid credential sources found". The wording is confusing, but the real cause is the region or the keys.
default_tags: tags on everything
This is one of the most useful provider parameters:
provider "aws" {region = "us-east-1"
default_tags { tags = {Project = "linuxlab-courses"
Environment = "dev"
ManagedBy = "terraform"
Owner = "platform-team"
}
}
}
Now every resource gets these tags automatically. This:
- Solves the "who created this?" problem in your cloud bills.
- Makes resources easier to find and delete:
aws ec2 describe-instances --filters Name=tag:Project,Values=linuxlab-courses. - Gives you a basis for FinOps reports.
If a resource has its own tags, they merge with default_tags. On a collision, the specific tags override the global ones.
assume_role: working with other accounts
Large companies often give each team its own AWS account, and cross-account access goes through an IAM role:
provider "aws" {region = "us-east-1"
assume_role {role_arn = "arn:aws:iam::123456789012:role/TerraformDeployerRole"
session_name = "terraform-from-local"
external_id = "secret-shared-with-the-target-account"
}
}
Here is what happens:
- The provider first walks the credentials chain and finds keys in an env variable or a file.
- With those keys it calls AWS STS
AssumeRoleand gets temporary credentials, valid for 1 hour. - Every later call to the AWS API goes out with the temporary credentials.
This is much safer than storing the target account's keys.
Several regions at once
Use alias (see tf-provider-block):
provider "aws" {region = "us-east-1" # default
}
provider "aws" {alias = "europe"
region = "eu-central-1"
}
resource "aws_s3_bucket" "us" {bucket = "my-us-bucket"
}
resource "aws_s3_bucket" "eu" {provider = aws.europe
bucket = "my-eu-bucket"
}
Pitfalls
-
Do not write
access_keyandsecret_keyin HCL. It goes into git, then into a public repository, then into a GitGuardian scan, and then the whole world sees your keys. Use env vars oraws configure. -
regionis not the same thing as an Availability Zone. A region isus-east-1, an AZ isus-east-1a. The AZ is usually set at the level of a specific resource. -
It is easy to get lost between AWS profiles. If you have several profiles in
~/.aws/credentials, andAWS_PROFILEpoints to one while the env variableAWS_ACCESS_KEY_IDpoints to another, the env variable wins (per the credentials chain). Always checkaws sts get-caller-identitybefore dangerous operations. -
default_tags can conflict with provider-level limits. For example, an EBS volume does not support every tag type, and tags like
aws:created-byare reserved by AWS. -
assume_rolecosts time. Each STS call is one more HTTP request. It should not hurt, but on large applies it is noticeable.