Terraform Fundamentals - Providers
Learn what are Terraform Providers and how they connect infrastructure code to cloud platforms and SaaS services.
So far we’ve been working with resources and variables to define infrastructure, but today let’s step back to understand a critical component that makes everything possible: Terraform Providers.
In simple terms, a provider is the bridge between Terraform and the cloud service. Whether we are deploying servers in AWS, creating virtual machines in Azure, setting up GitHub repositories, or even configuring Slack workspaces — a provider is what translates Terraform code into real-world actions. Without a provider, Terraform would not know how to communicate with any system.
Follow my journey of 100 Days of Red Team on WhatsApp, Telegram or Discord.
Providers are what allow Terraform to be cloud-agnostic and platform-independent. We define what we want, the provider handles how to talk to the required service.
What exactly is a provider?
A provider is simply a plugin that tells Terraform how to interact with specific APIs. It could manage cloud resources (like AWS EC2 instances, Azure VMs, or Google Cloud Storage buckets) or SaaS services (like GitHub repos, Datadog monitors, or Slack channels).
Each provider knows:
The API endpoints it needs to call
How to authenticate
What types of resources it can manage
What arguments are valid for those resources
This makes providers modular, reusable, and extremely powerful.
How do providers work?
When we run terraform init
, Terraform:
Reads configuration files
Downloads the required providers from the Terraform Registry (or any private registries if configured)
Caches them locally in the
.terraform
directoryPrepares them for use during
plan
andapply
This happens automatically, and we rarely need to think about it after the first setup — unless we upgrade provider versions later.
Cloud Providers vs Third-Party Providers
Most people start with the "big" providers like AWS, Azure, or GCP — these are called cloud providers. They expose a massive range of resources like VMs, storage, networking, databases, and more.
But, as you may have already guessed, Terraform providers go way beyond clouds.
There are third-party providers for:
GitHub (to manage repos, teams, permissions)
Slack (to create channels, manage users)
Datadog (to set up monitors and dashboards)
Cloudflare (to manage DNS, security rules)
Okta (for identity management)
and many more services.
For red teams, this means they can automate not just the setup of attack infrastructure, but also their CI/CD pipelines, user access permissions, alerting systems, and much more.
Introducing the required providers block
In every Terraform configuration, we should explicitly declare which providers we intend to use. This is done using the required_providers
block inside a terraform
block, typically at the top of .tf
files.
Here’s the general structure:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
Breaking this down:
aws
: This is the local name we will use to refer to the provider.source
: This tells Terraform where to find the provider.hashicorp/aws
means the provider is published by HashiCorp.version
: This locks which versions are allowed.~> 5.0
means "any version greater than or equal to 5.0.0 but less than 6.0.0." Terraform uses semantic versioning for providers.
Here are some ways we can constrain versions:= 5.6.2
: Exactly version 5.6.2>= 5.0.0
: Any version greater than or equal to 5.0.0~> 5.0
: Compatible with 5.0.x releases (but not 6.0.0+)<= 5.3.0
: Anything up to version 5.3.0
Choosing the right constraint gives you flexibility without risking unexpected failures.
Here’s how to configure Terraform to work with AWS:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.96.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
The
terraform
block defines which provider and what version to use.The
provider
block configures how to connect — in this case, by specifying the AWS region.
If you noticed, in the previous posts, we didn’t manually define a required_providers
block — and yet everything still worked. That’s because Terraform is smart enough to auto-detect provider requirements based on the resources you use.
For example, when Terraform sees we’re trying to deploy an aws_instance
, it knows we must need the AWS provider, and it automatically downloads the latest suitable version when you run terraform init
.
While this automatic behavior is convenient for quick experiments, explicitly defining required providers becomes crucial as a projects grow. It ensures:
Exact versions are locked down (for stability and reproducibility).
There are no surprises from upstream changes.
The infrastructure remains predictable across teams and environments.
💬 Pro Tip: Always specify provider versions. It’s a best practice even for small projects.
Here’s the updated main.tf
for the Terraform Hello World project:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.aws_region
}
# Fetch default VPC
data "aws_vpc" "default" {
default = true
}
# Get your public IP address
data "http" "my_ip" {
url = "https://checkip.amazonaws.com/"
}
# Create a local variable to hold the IP in CIDR format
locals {
my_ip_cidr = "${trim(data.http.my_ip.response_body, "\n")}/32"
}
resource "aws_instance" "hello" {
ami = var.ami_id
instance_type = var.instance_type
user_data = <<-EOF
#!/bin/bash
echo 'ec2-user:YourSecurePassword123' | chpasswd
sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config
systemctl restart sshd
EOF
vpc_security_group_ids = [aws_security_group.allow_ssh.id]
tags = {
Name = "TerraformHelloWorld"
}
}
resource "aws_security_group" "allow_ssh" {
name = "allow_ssh"
description = "Allow SSH from my IP"
vpc_id = data.aws_vpc.default.id
ingress {
description = "SSH"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [local.my_ip_cidr]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
TL;DR
- Terraform Providers are plugins that allow Terraform to manage resources across cloud platforms and third-party services.
- required_providers block are used to explicitly define and version-control providers.
- Although Terraform can auto-detect providers, explicitly declaring them is a best practice for security, stability, and reproducibility.
Follow my journey of 100 Days of Red Team on WhatsApp, Telegram or Discord.