Skip to content

Providers Resources and The Terraform Registry


Terraform itself does not know how to create every server, DNS record, database, or SaaS object in existence. Instead, it relies on providers. Once you understand providers, resources, and how the Registry connects them, Terraform configurations stop looking like mysterious blocks and start looking like a structured interface to external systems.

What Is a Provider?

A provider is Terraform’s interface to an external system.

Terraform can manage many different kinds of systems because providers implement the logic required to communicate with those systems.

Conceptually:

    flowchart LR
    A[Terraform configuration] --> B[Terraform]
    B --> C[Provider]
    C --> D[External API or platform]
    D --> E[Infrastructure or service]
  

Terraform handles the configuration language and planning workflow. The provider knows how to communicate with the target platform.

Providers are separate from Terraform itself and have their own versions.

That means this:

Terraform version

and this:

Provider version

are independent pieces of your environment.

Declaring Provider Requirements

Provider requirements are normally declared in a terraform block.

For example:

terraform {
  required_providers {
    random = {
      source  = "hashicorp/random"
      version = "~> 3.6"
    }
  }
}

There are three important pieces here.

The local provider name is:

random

The source address is:

hashicorp/random

The version constraint is:

~> 3.6

The source address tells Terraform where the provider comes from.

The version constraint tells Terraform which provider versions are acceptable.

Terraform uses this information during initialization.

Provider Configuration

After declaring a provider requirement, you can configure the provider itself.

For the random provider, no configuration is necessary:

provider "random" {}

A provider for an external platform like AWS may require settings such as a region or other configuration.

The important distinction is:

required_providers
    β”‚
    └── Which provider dependency do I need?

provider block
    β”‚
    └── How should that provider be configured?

These are related but not the same thing.

What Is a Resource?

A resource represents an object that Terraform manages.

For example:

resource "random_pet" "name" {
  length = 2
}

The resource has two labels:

random_pet
name

The first is the resource type.

The second is the Terraform-local resource name.

Together they form the resource address:

random_pet.name

The provider supplies the resource type’s implementation.

So conceptually:

Provider
   β”‚
   └── random
         β”‚
         └── random_pet resource type
                    β”‚
                    └── name

The resource type tells Terraform what kind of object is being managed.

The local name lets your configuration refer to that particular instance.

Provider, Resource, Argument, and Attribute

These four terms are easy to mix up.

Consider:

resource "random_pet" "name" {
  length = 2
}

Provider

The provider supplies Terraform’s interface to an external system.

random

Resource type

The resource type identifies the kind of resource.

random_pet

Resource argument

An argument is a value you provide to configure the resource.

length = 2

Resource attribute

An attribute is a value exposed by a resource that you can reference.

For example:

output "generated_name" {
  value = random_pet.name.id
}

Here:

random_pet.name

identifies the resource, and:

.id

accesses an attribute exposed by that resource.

The relationship is:

provider
    β”‚
    β–Ό
resource type
    β”‚
    β”œβ”€β”€ arguments β†’ values you configure
    β”‚
    └── attributes β†’ values the resource exposes

Not every provider resource has the same arguments or attributes. You learn those details from the provider documentation.

Finding Provider Documentation

The Terraform Registry is the main directory for publicly available Terraform providers.

Provider documentation describes:

  • available resource types
  • provider configuration
  • resource arguments
  • resource attributes
  • supported versions
  • examples
  • provider-specific behavior

When working with a resource you have not used before, do not guess its arguments.

Look up the provider’s documentation and check the version you are actually using.

Provider documentation is versioned, so documentation for one provider release may not exactly match another release.

Version Constraints

A provider requirement can use a version constraint.

For example:

terraform {
  required_providers {
    random = {
      source  = "hashicorp/random"
      version = "~> 3.6"
    }
  }
}

A version constraint describes the range of versions Terraform may select.

The exact syntax supports several operators, including:

>=
<=
>
<
=
~

You do not need to memorize every possible combination immediately. The important principle is that provider versions should be constrained deliberately rather than allowing an unexpected provider upgrade to change behavior without review.

Terraform’s dependency lock file then records the selected provider version and package checksums.

The Dependency Lock File

After:

terraform init

you will usually have:

.terraform.lock.hcl

The configuration might say:

version = "~> 3.6"

while the lock file records a specific selected version within that constraint.

Think of it this way:

Version constraint
    β”‚
    └── "These versions are acceptable."

Lock file
    β”‚
    └── "This is the version we selected."

This helps different environments make consistent provider selections.

The lock file should normally be committed to version control.

If you intentionally want to reconsider provider selections, you can use:

terraform init -upgrade

That tells Terraform to reconsider available provider versions within the declared constraints.

Provider Aliases

A provider can have more than one configuration.

For example, imagine you need to work with two regions of the same platform. You can create an additional provider configuration using an alias.

A simplified example looks like:

provider "example" {
  region = "region-a"
}

provider "example" {
  alias  = "secondary"
  region = "region-b"
}

A resource can then explicitly select the aliased configuration:

resource "example_resource" "my_resource" {
  provider = example.secondary

  # other arguments...
}

The important idea is simply:

default provider configuration
        β”‚
        └── region-a

aliased provider configuration
        β”‚
        └── region-b

Provider aliases become more important as configurations work across multiple regions or accounts. For now, understand the concept rather than trying to design complex multi-provider architectures.

A Complete Small Example

Use the random provider again so that the exercise requires no external cloud account.

Create:

terraform-provider-example/
└── main.tf

Put this exact content in main.tf:

terraform {
  required_providers {
    random = {
      source  = "hashicorp/random"
      version = "~> 3.6"
    }
  }
}

provider "random" {}

resource "random_pet" "application" {
  length    = 3
  separator = "-"
}

output "application_name" {
  description = "Generated application name."
  value       = random_pet.application.id
}

Initialize:

terraform init

Review the plan:

terraform plan

Apply it:

terraform apply

Confirm:

yes

Display the output:

terraform output application_name

When finished:

terraform destroy

This example demonstrates the complete relationship:

required_providers
        β”‚
        β–Ό
     provider
        β”‚
        β–Ό
 resource type
        β”‚
        β–Ό
resource instance
        β”‚
        β–Ό
resource attribute
        β”‚
        β–Ό
     output

Reading Registry Documentation Effectively

When you encounter an unfamiliar resource, start with its documentation rather than copying an example blindly.

Look for:

  1. The provider version you are using.
  2. The resource type.
  3. Required arguments.
  4. Optional arguments.
  5. Computed attributes.
  6. Example configurations.
  7. Notes about replacement or special behavior.

A useful question to ask while reading is:

Which values do I provide, and which values will Terraform/provider give back to me?

That question naturally separates arguments from attributes.

For example:

resource "random_pet" "application" {
  length = 3
}

You provide:

length

Terraform/provider exposes:

id

which you can then reference:

random_pet.application.id

That pattern appears constantly throughout Terraform.

The Bigger Picture

The provider model is what makes Terraform extensible.

Terraform does not need to contain specialized code for every infrastructure platform directly. Providers act as the bridge:

                  Terraform
                      β”‚
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β–Ό             β–Ό             β–Ό
    Provider A    Provider B    Provider C
        β”‚             β”‚             β”‚
        β–Ό             β–Ό             β–Ό
     Platform A   Platform B    Platform C

The Terraform language stays consistent while each provider exposes the resources and data relevant to its target system.

Once you understand this model, provider documentation becomes much easier to navigate.

The next chapter moves from fixed values inside a configuration to configurable values: variables, outputs, and local values.

Last updated on