Terraform is one of the most popular Infrastructure as Code (IaC) tools in the world. It allows us to provision infrastructure resources programmatically and declaratively, meaning we define what we want, and Terraform figures out how to build it.
We do not need to worry about the specific API calls required to provision resources in AWS, GCP, Azure, or other platforms because Terraform abstracts that complexity. However, to use it effectively, we must understand two core components: Providers and Modules.
Providers - The translator layer
Imagine you want to deploy an EC2 instance in AWS. To do this, Terraform needs to know how to authenticate with your AWS account and how to translate your configuration code into the specific API calls that AWS understands.
Providers act as these translators. They are plugins that tell Terraform how to interact with external platforms like AWS, GCP, Azure, GitHub, etc. They encapsulate all the platform-specific logic, allowing you to focus purely on defining your resources.
The Terraform Registry
Terraform relies on an extensive plugin system that you can explore in the Terraform Registry (https://registry.terraform.io/)
Using Providers
Using a provider is super simple:
provider "aws" {
region = "us-west-2"
}
As a best practice, providers are usually defined in a dedicated providers.tf file.
Once the provider is set up, Terraform knows exactly how to read an AWS resource block and interact with the AWS API to deploy it:
resource "aws_instance" "web_server" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
}
Modules - Reusability is served
In traditional programming, we follow the DRY principle (Don't Repeat Yourself) to avoid repeating code over and over. So, we use functions or classes to group collections of related logic, variables, and operations into reusable blocks. By passing different parameters to these functions, we can execute the same behavior in various contexts without duplicating the underlying code.
Terraform modules apply this principle to infrastructure. Just as a function prevents us from rewriting the same script, a module prevents us from rewriting the same resource blocks.
They allow us to group a collection of related resources, think of an EC2 instance, its security groups, its volumes...., into a single, standardized package that can be deployed repeatedly just by changing the input variables.
Using modules
Imagine that we need to deploy S3 buckets for different departments within the company. Instead of typing over and over the multiple Terraform resources to define the buckets, we could create a module that simply accepts a name and environment as parameters:
# modules/secure_bucket/main.tf
variable "bucket_name" {
type = string
}
variable "environment" {
type = string
}
resource "aws_s3_bucket" "this" {
bucket = var.bucket_name
tags = {
Environment = var.environment
ManagedBy = "Terraform Module"
}
}
and then, we would call it as:
# main.tf
module "financial_data_bucket" {
source = "./modules/secure_bucket"
bucket_name = "company-financial-data-2026"
environment = "Production"
}
module "app_assets_bucket" {
source = "./modules/secure_bucket"
bucket_name = "company-app-assets-2026"
environment = "Development"
}
Conclusion
As a takeaway, let's remember that:
Providers: provide the connectivity components to external platforms by translating your Terraform code to the right API calls.
Modules: containers of multiple resources used together. Allows reusability.
Top comments (0)