3.2 How Terraform Uses Providers
Key Takeaways
- Providers are plugins that translate HCL into vendor API calls; Terraform core does not implement AWS or Azure APIs
- Resource types belong to a provider — aws_instance is implemented by hashicorp/aws, not by Terraform core
- Configure a provider with a provider block and pass credentials through environment variables, a shared credentials file, or an IAM role — never hardcode secrets in .tf files
- During plan the provider reads and refreshes remote objects; during apply it creates, updates, or deletes them
- A resource that omits the provider meta-argument uses the default unaliased configuration whose local name matches the resource type prefix
3.2 How Terraform Uses Providers
Quick Answer: A provider is a plugin. Terraform core does not know how to call AWS or Azure. The plugin translates your HCL (
aws_instance,azurerm_resource_group) into that vendor's API. You configure the plugin with aproviderblock and feed it credentials from the environment, a shared credentials file, or a cloud role — not from secrets pasted into Git.
Objective 2b on Terraform Associate (004) is conceptual: describe how Terraform uses providers. The exam is not asking you to memorize every AWS argument. It is asking whether you can separate Terraform core from the plugin, name what the plugin contributes (resource types, data sources, provider-defined functions, API calls), and configure that plugin without leaking credentials.
Providers are plugins, not Terraform core
HashiCorp's language docs open with the same sentence you should be able to repeat: Terraform relies on plugins called providers to interact with remote systems. Each provider adds resource types and data sources. Without providers, Terraform cannot manage any infrastructure.
That split is the highest-yield 004 trap in this objective:
- Terraform core parses HCL, builds the dependency graph, reads and writes state, computes a plan, and calls plugin RPCs.
- The provider plugin implements
aws_instance, talks HTTPS toec2.amazonaws.com, and returns attributes such asidandprivate_ip.
Terraform 1.12 does not contain an AWS SDK. Upgrading the terraform binary does not give you new aws_* resource arguments. New AWS features arrive when you upgrade the hashicorp/aws provider (see 3.1).
Most providers target a cloud or SaaS API. Some, such as hashicorp/random and hashicorp/archive, are local utilities: they still install as plugins, but they do not call a public cloud. A root module that mixes aws_instance with random_id is using two plugins, not a special core feature.
Resource types belong to providers
Every managed resource type is implemented by exactly one provider. The first segment of the type is the preferred local name:
| Resource or data type | Provider local name | Typical source |
|---|---|---|
aws_instance, aws_vpc | aws | hashicorp/aws |
random_id, random_password | random | hashicorp/random |
kubernetes_deployment | kubernetes | hashicorp/kubernetes |
datadog_monitor | datadog | datadog/datadog |
There is no core resource type that spans two clouds. Multi-cloud on Terraform means multiple providers in one configuration, each managing its own types. That idea starts in objective 1c and becomes configuration in 2c (section 3.3).
If a resource omits the provider meta-argument, Terraform takes the first word of the type (aws from aws_instance) and looks for a provider configuration with that local name. That is why using the preferred local name in required_providers lets you skip provider = aws on almost every resource.
The provider block configures the plugin
required_providers answers "which plugin, which version." The provider block answers "how should that plugin authenticate and which account or region should it target."
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
The body of provider "aws" is defined by the AWS provider, not by Terraform core. region is an AWS argument. Azure uses features and subscription_id. Kubernetes uses config_path or in-cluster settings. Read the provider's own registry docs for the argument list.
If you omit the provider block entirely, Terraform still creates an empty default configuration for that local name. Providers that can discover everything from the environment (region plus credentials) often work with that empty block. Providers that require arguments fail at plan time because the empty default is incomplete.
You may use expressions in a provider block, but only values Terraform knows before apply: input variables, locals, and other literal configuration. You cannot set region = aws_instance.web.availability_zone — that attribute is computed during apply, so the provider would not be configurable when the run starts.
Default versus explicit provider
- The
providerblock withoutaliasis the default configuration for that local name. - Resources, data sources, and modules that do not set a provider meta-argument use that default.
- An explicit
provider = aws.west(covered in 3.3) overrides the default for one block.
On 004, "default provider" means the unaliased configuration, not "the provider HashiCorp likes best."
Authentication — do not hardcode secrets
Providers need credentials. HashiCorp's provider-block reference states that many providers accept shell environment variables or other alternate sources so credentials stay out of version-controlled configuration. That sentence is exam-complete.
For hashicorp/aws, the usual assignment methods are:
- Environment variables —
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN,AWS_PROFILE,AWS_REGION/AWS_DEFAULT_REGION. - Shared credentials and config files —
~/.aws/credentialsand~/.aws/config, optionally selected withprofilein the provider block orAWS_PROFILE. - Runtime roles — an EC2 instance profile, ECS task role, or Kubernetes IRSA role. The provider obtains a temporary session from the metadata service.
- Provider arguments —
access_key,secret_key,token,shared_credentials_files,assume_role. These work, but secrets written as string literals in.tffiles get committed.
Do not do this in a shared repository:
provider "aws" {
region = "us-east-1"
access_key = "AKIA..." # never commit this
secret_key = "wJalr..." # never commit this
}
The 004 answer is: pass credentials through the environment, a shared credentials file, or a cloud role. The provider block should carry non-secret settings such as region. Sensitive-data handling goes deeper in objective 4h; here you only need the rule that provider secrets do not belong in Git.
HCP Terraform typically injects credentials as environment variables or through a run's variable set, then still uses the same provider "aws" { region = ... } block.
Data sources and provider-defined functions
At a high level, a provider contributes more than managed resources:
- Managed resources (
resource "aws_instance" "web") — Terraform creates, updates, and destroys the remote object and records a binding in state. - Data sources (
data "aws_ami" "al2023") — the provider reads an existing object during plan and exposes attributes for the rest of the configuration. Terraform does not create that AMI. - Provider-defined functions (Terraform 1.8+, so they are in scope for 1.12) — offline helper functions the plugin exposes. You call them with
provider::<local-name>::<function-name>(...), for exampleprovider::aws::arn_parse(var.arn)or the built-inprovider::terraform::encode_tfvars({ example = "Hello!" }).
You cannot define custom functions in HCL itself. If the exam mentions a provider:: call, that is a provider-defined function, not a built-in such as length() or lookup().
Plan versus apply: which API calls happen when
Both phases load the provider plugin. They do not do the same work.
| Phase | What Terraform core asks the provider to do | Typical vendor API traffic |
|---|---|---|
terraform plan (and the refresh that precedes it) | Read current remote objects and data sources so the plan compares configuration with reality | Describe* / GET-style reads |
terraform apply | Execute the planned create, update, or delete, then read the result back into state | Create* / Modify* / Delete* plus follow-up reads |
Prior to an operation, Terraform refreshes state by asking each provider to read the objects already bound in state. That is why a console-deleted EC2 instance shows up as a create on the next plan: the AWS provider's read returned "not found," core updated the planned action, and apply will call create. Apply is not the first time the plugin runs.
A plan that only interpolates locals and never touches a resource still needs initialized providers if the configuration references those providers. terraform init must have succeeded first.
004 traps for objective 2b
- Terraform core does not implement AWS or Azure APIs.
- Upgrading Terraform CLI does not add new
aws_*arguments; upgrading the AWS provider does. - Providers run during plan and apply, not only during apply.
- Data sources are provider features that read existing objects; they are not a second Terraform core language.
- Empty default provider configurations work only when the plugin can discover required settings from the environment.
Which statement correctly describes Terraform core and cloud APIs on Terraform 1.12?
What is the safest way to give the AWS provider credentials in a Git-tracked Terraform 1.12 configuration?
During terraform plan versus terraform apply, what does a provider typically do?