Terraform Configuration Basics: Providers, Resources, and Variables are the three pillars that turn a blank cloud account into a running infrastructure. If you do not understand them, every Terraform file you write will feel like random symbols. For TF-003, exam objective 2.1 demands that you can explain what each one does, why it exists, and how they fit together — because the test will ask you to spot the difference between a resource and a data source, or identify which block declares a provider.
Jump to a section
A simple way to picture Terraform Configuration Basics: Providers, Resources, and Variables
You order a 100-piece flatpack bookshelf from IKEA. Inside the box is everything you need: a set of instructions, bags of screws and dowels, and some shelves and side panels.
The instruction booklet is like your Terraform provider. It tells the assembly tool (Terraform itself) which specific make and model of furniture you are building — for instance, 'this is an IKEA Billy bookshelf'. The provider knows the exact language and steps for working with that type of furniture.
Each individual piece — a wooden shelf, a metal peg, a glass door — is a resource. In the instructions, you see 'attach shelf A to side panel B using screw C'. That direct order is Terraform declaring: 'I want a virtual server, and it must be a t2.micro running Ubuntu'.
But you do not want to write the same screw size fifty times. So you write a variable. You create a sticky note next to the instructions: 'screw size = 30mm'. Every time the instructions say 'screw', they point to your note. If you later want 40mm screws, you change that one sticky note instead of rewriting every instruction.
That is the whole magic. Providers teach Terraform how to talk to a specific cloud, resources are the exact things you want created, and variables let you change those things without rewriting your entire plan.
Terraform is a tool that lets you describe your entire IT infrastructure — servers, databases, networking rules — in plain text files. Those files use a specific language called HashiCorp Configuration Language (HCL). The language has several main building blocks: providers, resources, data sources, variables, and outputs. For TF-003, you must understand each one precisely.
Let us start with providers. A provider is a plugin that teaches Terraform how to talk to a particular platform. The platform could be a public cloud like Amazon Web Services (AWS), Microsoft Azure, or Google Cloud, or it could be a smaller service like a DNS provider or a database-as-a-service. In technical terms, the provider knows the Application Programming Interface (API) of that platform — the set of rules and commands the platform understands. Without the provider, Terraform would have no idea how to create a virtual machine on AWS because it would not speak 'AWS language'.
You declare a provider in your configuration with a block that looks like this:
provider "aws" { region = "us-west-1" }
The name inside the quotes — 'aws' — tells Terraform to download and use the AWS provider. The configuration inside the curly braces sets any credentials or default settings, such as which geographical region to use. You can have multiple providers in one configuration, for example, one for AWS and one for Azure, each managing its own resources.
Now resources. A resource is a single piece of infrastructure you want Terraform to create, update, or destroy. If the provider is the translator, the resource is the thing you are ordering. Every resource belongs to exactly one provider. For example, an AWS virtual machine is declared like this:
resource "aws_instance" "web_server" { ami = "ami-0c55b159cbfafe1f0" instance_type = "t2.micro" }
Here, 'aws_instance' is the resource type. The label after it, 'web_server', is a local name you choose so you can refer to this resource elsewhere in your code. Inside the block, you set properties such as the Amazon Machine Image (AMI) identifier and the instance size. Terraform reads this, talks to the AWS provider, and says: 'Please create one virtual server with these exact settings.'
The key insight: resources are declarative. You write what you want, not how to do it. Terraform figures out the steps. If the resource already exists and its settings match your code, Terraform does nothing. If settings differ, Terraform changes the resource to match your code. If the resource is missing, Terraform creates it. If you delete the resource block, Terraform destroys the real-world resource.
Data sources are cousins of resources. While a resource creates something new, a data source reads information about something that already exists outside Terraform. For example, you might use a data source to fetch the latest available AMI ID for Ubuntu, rather than hardcoding it:
data "aws_ami" "ubuntu" { most_recent = true owners = ["099720109477"] }
The syntax is 'data' instead of 'resource', followed by the type and a local name. Terraform queries the provider for the information and stores it, so you can use it elsewhere. Data sources do not create, modify, or destroy anything — they just fetch data.
Variables make your configuration reusable. Instead of hardcoding a value like 't2.micro' in multiple places, you define a variable once and use it everywhere. A variable is declared like this:
variable "instance_type" { description = "Type of EC2 instance" type = string default = "t2.micro" }
You then use it by writing var.instance_type. When someone else wants to use your code, they can override the default value by setting the variable in a separate file (terraform.tfvars) or via command line. This means you can write one set of Terraform files for a test environment and reuse the same files for production — just change the variable values.
Outputs are like the report card for your Terraform run. An output block tells Terraform to display a specific piece of information after applying the configuration:
output "instance_ip" { value = aws_instance.web_server.public_ip }
When you run terraform apply, you see the IP address printed in the terminal. Outputs are also useful for passing information between different Terraform configurations (called root modules).
How do all these pieces work together? When you run terraform init, Terraform downloads the provider plugins you declared. When you run terraform plan, it compares your configuration against the real-world state saved in a state file (stored locally or remotely) and shows what will change. When you run terraform apply, it executes those changes. The whole process is called 'Infrastructure as Code' (IaC) — managing infrastructure through code that can be versioned, reviewed, and automated.
Before Terraform, IT teams used to log into cloud consoles, click buttons, and inevitably make mistakes. They might also write scripts (like shell scripts or Python) that were brittle and hard to maintain. Terraform replaced that with a standardised, human-readable language that works across clouds. The exam expects you to know the difference between these blocks and when to use each one.
Declare providers
You start by writing a 'provider' block for each cloud platform you want to use. For example, 'provider "aws" { region = "us-west-2" }'. This step tells Terraform which API to talk to and what default settings (like region) to use. Without it, Terraform has no target platform.
Define variables
Create a 'variable' block for any value you want to change later without editing the main code. For example, 'variable "instance_type" { type = string default = "t2.micro" }'. This keeps your configuration flexible and reusable across different environments (dev, test, production).
Declare resources
Write a 'resource' block for each piece of infrastructure you want created. Each resource block has a type (like 'aws_instance') and a local name (like 'web'). Inside, you set properties such as the AMI and instance type. Terraform will create, update, or destroy these to match your code.
Add data sources (if needed)
If you need information about infrastructure that already exists — like the latest Ubuntu AMI — write a 'data' block. For example: 'data "aws_ami" "ubuntu" { most_recent = true }'. This fetches data without creating anything, and you can reference it inside your resource blocks.
Define outputs
Write 'output' blocks for values you want to see after running apply, such as the public IP of a server. For example: 'output "ip" { value = aws_instance.web.public_ip }'. Outputs also let you share data between different configurations (called root modules).
Run terraform init
Before applying, run 'terraform init' in your project directory. This command downloads the provider plugins you declared. Without this step, Terraform cannot communicate with your chosen cloud platform — you will get an error about missing providers.
Run terraform apply
Finally, run 'terraform apply'. Terraform compares your configuration against the current state file, shows you a plan of what will change, and asks for confirmation. After you approve, it creates (or modifies or destroys) the resources in the correct order based on dependencies.
Imagine you work for a small e-commerce company, 'ShopQuick', that is moving its website from a physical server in a closet to the cloud. Your boss says: 'We need two web servers, one database, and a load balancer to distribute traffic.' A few years ago, you would have clicked around the AWS Management Console for an hour, and if you made a mistake — say, made a server public when it should be private — your company's data could be exposed.
Instead, you write a Terraform configuration. You start by declaring the AWS provider so Terraform knows who to talk to:
provider "aws" { region = "us-west-2" }
Then you declare a variable for the instance type, because you might want to upgrade the servers later without changing the main code:
variable "web_instance_type" { description = "Instance type for web servers" type = string default = "t3.medium" }
Now you define the resources. First, the two web servers. You might use a count or for_each (advanced patterns), but for simplicity, you write two separate resource blocks, or better, one block using count = 2. You also create a security group (a firewall) that allows HTTP traffic. Then you create the database server using a separate resource block for an RDS instance. Finally, you create an Application Load Balancer (ALB) and attach the web servers to it.
To make the configuration reusable, you add outputs for the load balancer's DNS name, so your dev team can update the website's CNAME record in their DNS provider.
At the end, you run terraform plan. It prints a list of everything it will create. You review it with your boss and catch a mistake: the database security group is open to the whole internet. You fix the code, run plan again, and only then run apply. Terraform creates all five resources in the correct order — the load balancer first because the servers need to register with it, the security groups before the servers because the servers need the firewall rules to exist.
A month later, traffic spikes. You need bigger servers. You change the variable default from 't3.medium' to 't3.large' and run apply again. Terraform detects that the instance type has changed, so it replaces each server one at a time (if you configured it properly) without downtime.
This is what an IT professional actually does with Terraform every day: write code, review it, apply it, and iterate. The exam focuses on whether you understand the syntax and purpose of each block, because in real life you will be reading and writing them constantly.
TF-003 exam objective 2.1 is one of the most heavily tested areas. The exam will give you short snippets of Terraform configuration and ask you to identify what each block does. Here is exactly what you need to know.
First, you must be able to recognise the four block types by their keywords: 'provider', 'resource', 'data', 'variable', and 'output'. Traps often involve confusing 'data' with 'resource'. If the block starts with 'data', it fetches existing information. If it starts with 'resource', it creates or manages something. The exam loves to present a data source block and ask: 'Does this create a new resource?' The answer is always no.
Second, the exam tests variable usage. You will see questions like: 'Given the following variable declaration, how do you reference it inside a resource block?' The correct answer is 'var.variable_name'. A common trap is using just 'variable_name' without the 'var.' prefix. Another trap is thinking that a variable declared without a default value is optional — it is not. If there is no default and no value is provided, Terraform will prompt you or fail.
Third, the exam tests outputs. You will be asked: 'What is the purpose of an output block?' The correct answer is to display values after apply and to pass data between modules. A trap is to say outputs are used to create resources — they do not.
Fourth, the exam tests provider requirements. You will need to know that you must declare a provider in the configuration, and that you can have multiple providers. A typical trap question: 'You want to manage both AWS and Azure resources. How do you do this?' The correct answer is to declare two provider blocks, one for 'aws' and one for 'azurerm'. Another trap: thinking you can only have one provider per configuration.
Fifth, the exam tests resource syntax. You must know the structure: resource "type" "local_name" { ... }. The type is the resource type (e.g., 'aws_instance'), and the local name is an identifier you choose. Traps include mixing these up — for example, thinking the first string after 'resource' is the local name.
Here is a list of concepts the exam loves to test:
The difference between a resource and a data source.
How to reference a variable (var.name).
How to reference an attribute from another resource (resource_type.local_name.attribute).
The purpose of the provider block (to configure the target platform).
The requirement to run terraform init before using a provider.
What happens if you delete a resource block (the real resource gets destroyed).
That outputs are displayed after terraform apply and can be used in other modules.
Key definitions you must memorise: 'Provider — a plugin that enables Terraform to interact with a specific API.' 'Resource — a block that declares a specific piece of infrastructure.' 'Variable — a parameter that makes configurations dynamic.' 'Data source — a block that reads existing infrastructure information.' 'Output — a value displayed after apply, used for module communication.'
The exam will also test your ability to spot invalid syntax. For example, forgetting the quotes around a variable type, or using a hyphen in a local name. Know that local names must start with a letter or underscore and can contain only letters, numbers, underscores, and hyphens.
A provider block is a plugin declaration that tells Terraform which platform (AWS, Azure, GCP, etc.) to talk to and how to authenticate.
A resource block always starts with the keyword 'resource' and has exactly two labels after it: the resource type and a local name you choose.
A data source block starts with 'data' and reads existing infrastructure information — it never creates, updates, or destroys anything.
Variables are declared outside resource blocks and referenced as var.variable_name — never use the bare variable name.
An output block displays a value after terraform apply and can pass data between different Terraform modules.
You must run terraform init after adding a new provider to download the required plugin before terraform plan or apply will work.
If you delete a resource block from your configuration and run apply, Terraform will destroy that real-world resource.
You can have multiple provider blocks in one configuration to manage resources across different clouds or different regions.
These come up on the exam all the time. Here's how to tell them apart.
Resource
Creates, modifies, or destroys infrastructure.
Block keyword: 'resource'.
Example: resource "aws_instance" "web" { ... } creates a server.
Data Source
Reads information about existing infrastructure only, never modifies anything.
Block keyword: 'data'.
Example: data "aws_ami" "ubuntu" { ... } fetches an AMI ID.
Variable
Declares a parameter that can be overridden to make configurations reusable.
Referenced as 'var.variable_name' inside resource blocks.
Does not display anything by default — it is an input mechanism.
Output
Declares a value that is displayed after terraform apply.
Referenced as 'output.output_name' when passing between modules.
Exists to share results with humans or other Terraform modules.
Provider
A plugin that teaches Terraform how to talk to a specific API (e.g., AWS).
Declared once, then used by many resources.
Does not create any infrastructure by itself.
Resource
A specific piece of infrastructure you want to manage (e.g., a virtual machine).
Each resource belongs to exactly one provider.
Directly creates or modifies real-world objects.
terraform init
Downloads provider plugins and sets up backend configuration.
Must be run before any other command in a new or modified project.
Does not create or change any infrastructure.
terraform apply
Executes the changes defined in your configuration.
Creates, updates, or destroys resources based on the plan.
Can be safely run multiple times; Terraform only changes what differs.
Mistake
A provider is the same as a resource — both create infrastructure.
Correct
A provider is a plugin that enables Terraform to communicate with a specific platform (e.g., AWS). It does not create anything. A resource is a declaration of a specific piece of infrastructure to create.
Beginners see the provider and resource blocks both inside the same file and assume they are the same kind of thing, plus the word 'provider' sounds like it is providing something, so it feels like it should create something.
Mistake
You can only use one provider in a single Terraform configuration.
Correct
You can declare multiple provider blocks (e.g., one for AWS and one for Azure) in the same configuration, and Terraform will manage resources in both.
Most tutorials start with a single provider, so beginners assume that is the limit. The exam explicitly tests multi-provider configurations.
Mistake
Variables must be declared inside the resource block that uses them.
Correct
Variables are declared at the top level of a module (usually in a variables.tf file) and can be referenced from any resource block in that module.
In many programming languages, you declare a variable right where you use it. HCL groups variable declarations separately, which feels unfamiliar to new learners.
Mistake
If you define a variable with a default value, you cannot override it.
Correct
Defaults are just fallbacks. You can always override a variable by passing a value via a .tfvars file, command-line flag, or environment variable.
The word 'default' implies a fixed setting. Beginners think it locks the value, like a default font in a word processor that you must manually change each time.
Mistake
Data sources create new infrastructure just like resources do.
Correct
A data source reads information about existing infrastructure. It never creates, modifies, or deletes anything.
Both blocks look syntactically similar (both have a type and local name, both appear in the configuration), so beginners assume they operate identically.
Mistake
Outputs are only useful for displaying information to humans.
Correct
Outputs serve two purposes: they display values after apply, and they allow one Terraform module to pass data to another module (module composition).
The name 'output' sounds like something you see on screen. Beginners miss the inter-module communication aspect until they build multi-module projects.
Reveal each answer, then mark whether you got it right. Score 60%+ to unlock the next chapter.
A resource declares something you want to create, update, or destroy (e.g., a virtual server). A data source reads information about something that already exists (e.g., the latest AMI ID). Resource blocks start with 'resource', data blocks start with 'data'.
Yes. Terraform needs a provider block to know which platform (e.g., AWS, Azure) to talk to and how to authenticate. Without it, Terraform has no target to manage.
Yes, you can declare multiple provider blocks, each with a different name (e.g., 'aws' and 'azurerm'). This allows you to manage resources in different clouds within the same configuration.
You use 'var.variable_name'. For example, if you declared variable 'instance_type', you write var.instance_type inside the resource block. Do not use just the variable name without 'var.'.
Terraform will destroy that resource in the real world. It compares your current configuration to the state file, and if a resource is missing from the code but exists in the state, Terraform assumes you want it gone.
An output block displays a value after terraform apply (e.g., a server's IP address) and can pass data from one Terraform module to another. It does not create or modify any infrastructure.
No. You only need to run terraform init when you change providers or their versions. For normal edits to resources, variables, or outputs, you just run terraform plan or terraform apply.
You've finished Terraform Configuration Basics: Providers, Resources, and Variables. Continue through the TF-003 study guide to build a complete picture of the exam.
Done with this chapter?