Terraform path · The model · Topic 2 of 14

Providers, resources and data sources

Not started

Terraform itself is a small engine: it parses configuration, builds a dependency graph, stores state and computes plans. Everything that touches a real system lives in **providers**, plugins that terraform init downloads from a registry and runs as separate processes. The AWS provider knows what an aws_s3_bucket is and which API calls create one; the random provider generates values; the local provider manages files. A **resource** block asks a provider to create and manage an object; a **data** block asks it to read one.

Why it matters: Terraform core knows nothing about AWS, GitHub or DNS. Providers do, and which version you run, how it is configured and how it is pinned decide whether tomorrow's plan matches today's.

  1. 1A provider is a plugin

    Each provider is a separate binary downloaded by terraform init that maps resource types to an API.

  2. 2Resources are managed, data sources are read

    A resource block creates and owns an object. A data block only looks something up that exists already.

  3. 3Pin versions, commit the lock file

    Constraints in required_providers say which versions are acceptable; .terraform.lock.hcl records exactly which one was chosen.

  4. 4Aliases for several configurations

    Two Regions or two accounts means two provider configurations, selected per resource with provider = aws.west.

1

How it works

The explanation, with examples you can run.

Requiring and configuring

terraform {  required_version = ">= 1.5"  required_providers {    aws    = { source = "hashicorp/aws", version = "~> 6.0" }    random = { source = "hashicorp/random", version = "~> 3.6" }  }} provider "aws" {  region = "eu-west-1"} provider "aws" {  alias  = "us"  region = "us-east-1"}

source is namespace/type on the registry (registry.terraform.io by default). The provider block holds settings such as the Region; credentials should come from the environment (a role, SSO session or CI identity), never from the file. A second configuration gets an alias, and resources choose it with provider = aws.us.

The lock file

terraform init picks the newest version matching the constraints and writes .terraform.lock.hcl:

# This file is maintained automatically by "terraform init".# Manual edits may be lost in future updates. provider "registry.terraform.io/hashicorp/local" {  version     = "2.9.1"  constraints = "~> 2.5"  hashes = [    "h1:OZGJN0LSSat5QIxZPxDXtVr4XpHb2oG7cVKJhSqBFIE=",    "zh:25606c7a5e308144fb627f6e31611bb52ff72bb9ae2d27af39673ab1a6b3c1bf",

Commit it. Later runs use exactly this version and verify the checksums, so a laptop, CI and a colleague all run the same provider. Upgrading is explicit: terraform init -upgrade, then review the plan. If CI runs on Linux and people on macOS, record hashes for both with terraform providers lock -platform=linux_amd64 -platform=darwin_arm64.

Resources and data sources

data "aws_vpc" "shared" {  tags = { Name = "shared-network" }} resource "aws_security_group" "api" {  name   = "api"  vpc_id = data.aws_vpc.shared.id}

The VPC is looked up on every plan and never modified; the security group is created, tracked in state, updated and, if you remove the block, destroyed. Every resource has an address (aws_security_group.api) that Terraform uses in plans and state, and each argument's behaviour (in-place update or forced replacement) is defined by the provider's schema, which is why the provider documentation is the reference you will read most.

Things that go wrong

  • Unpinned providers: a major version changes defaults or removes arguments, and the next plan wants to replace things.
  • Provider configuration inside reusable modules: modules should declare required_providers but receive provider configurations from the caller, so they can be used with any Region or account.
  • Data sources that find nothing or several results fail the plan. That is usually a feature: better than guessing.
2

Check yourself

3 questions. Get them all right to finish this topic.

  1. 1.Your colleague runs terraform init and gets a newer provider version than you, and the plan differs. What prevents this?
  2. 2.You need the ID of an existing VPC that another team manages. Which block do you use?
  3. 3.What does version = "~> 3.6" allow?

0 of 3 right so far. Get all three to finish this topic.