What a provider is in Terraform
A provider is the plugin that sits between Terraform and a cloud. The AWS provider knows how to talk to the AWS API. The Google Cloud provider talks to GCP. There are providers for GitHub (to manage repositories), Cloudflare (DNS), even for a home router.
Every provider needs configuration: where to connect, with which keys, in which region. That is what the provider block does.
Minimal example
terraform { required_providers { aws = {source = "hashicorp/aws"
version = "~> 5.60"
}
}
}
provider "aws" {region = "us-east-1"
}
There are two blocks here:
terraform { required_providers { ... } }is a meta-block. It says "I need the AWS provider at version 5.60 or higher, within major 5."terraform initreads this to know what to download. See tf-init.provider "aws"configures the downloaded plugin. Here it sets only the region.
This example does not list any AWS credentials. Terraform takes them from the standard places (env vars, ~/.aws/credentials). See aws-provider.
Why you need an alias
Sometimes one provider is not enough. The classic case: you need resources in two different AWS regions.
provider "aws" {region = "us-east-1"
}
provider "aws" {alias = "europe"
region = "eu-central-1"
}
# Resource in the default region us-east-1
resource "aws_s3_bucket" "us" {bucket = "my-us-bucket"
}
# Resource in Europe, explicit reference to the alias
resource "aws_s3_bucket" "eu" {provider = aws.europe
bucket = "my-eu-bucket"
}
One provider without an alias is the default one. Any resource that does not name a provider = goes to it. Everything else goes through aws.alias_name.
default_tags, write it like this
The AWS provider can attach tags to every resource automatically:
provider "aws" {region = "us-east-1"
default_tags { tags = {Project = "my-project"
Environment = "dev"
ManagedBy = "terraform"
}
}
}
Now every resource you create gets these tags with no effort on your part. This saves money (tags feed billing analytics), makes cleanup easier (aws s3api list-buckets --query ... --tag-key Environment Value dev), and helps with FinOps.
If a specific resource has its own tags = {...}, they merge with default_tags. Your own tags win over default_tags when a key matches.
Version constraints
In required_providers you can pin the version in several ways:
| Notation | What it means |
|---|---|
"5.60.0" | Exactly 5.60.0, no deviation. |
"= 5.60.0" | The same thing, stated explicitly. |
">= 5.60.0" | Not below 5.60.0. Risky, it can pull in an incompatible 6.x. |
"~> 5.60" | 5.60.x, 5.61.x, and so on. But not 6.x. Pessimistic, a safe default. |
"~> 5.60.0" | Only 5.60.x. Very strict. |
">= 5.60, < 6.0" | Equivalent to ~> 5.60, stated explicitly. |
The standard in well-run projects is ~> X.Y. This gives you automatic minor updates (bug fixes, new resources) without the risk of a major bump. See tf-version-constraints.
Pitfalls
-
The same
provider "aws" {}twice without an alias is an error. You can have either one without an alias or several with different aliases. -
required_providersis mandatory. If it is missing, Terraform does not know what to download duringinit. It used to default tohashicorp/aws, but that is now deprecated. -
A
providerblock can be empty:provider "aws" {}is valid if all settings come from env vars or credentials files. -
Set endpoints carefully (see localstack-provider). In production they should disappear, or you might accidentally send a request to the wrong cluster.
-
Do not commit credentials in the provider block. Never write
access_key = "AKIA..."in HCL. Use env vars or IAM roles. The one exception: the hardcoded"test"values for LocalStack in training exercises.