Terraform: providers, resources & HCL
The core building blocks.
Terraform is where most people start with IaC, so we build it up properly. You write Terraform in its own language, HCL (HashiCorp Configuration Language) — readable, declarative, purpose-built for describing resources. Three concepts are the foundation: providers (plugins that talk to a platform like AWS or Kubernetes), resources (the things you want to exist — a server, a bucket, a DNS record), and the configuration files (.tf) that declare them. Get these three and you can read most Terraform.
terraform {required_providers {aws = { source = "hashicorp/aws", version = "~> 5.0" } # which provider, pinned}}provider "aws" {region = "us-east-1" # configure the provider}resource "aws_instance" "web" { # a resource: TYPE "name"ami = "ami-0abc123"instance_type = "t3.micro"tags = { Name = "web-server" }}
Providers: plugins for platforms
A provider is a plugin that knows how to talk to a specific platform’s API — there are providers for AWS, Azure, Google Cloud, Kubernetes, GitHub, Cloudflare, and hundreds more. You declare which providers you need (pinned to a version), Terraform downloads them on terraform init, and each provider exposes a set of resource types you can then declare. The provider is what makes Terraform cloud-agnostic: the same tool and workflow manage anything that has a provider.
Resources, arguments & references
A resource block declares one thing that should exist: resource "aws_instance" "web" says "an AWS instance I will call web." Inside, arguments set its properties (the AMI, the instance type, tags). The powerful part is that resources can reference each other’s attributes — aws_instance.web.id — so Terraform learns the dependencies between them and creates them in the right order automatically. You describe the resources and how they relate; Terraform figures out the order of operations.
resource "aws_security_group" "web_sg" {name = "web-sg"ingress { from_port = 443, to_port = 443, protocol = "tcp", cidr_blocks = ["0.0.0.0/0"] }}resource "aws_instance" "web" {ami = "ami-0abc123"instance_type = "t3.micro"vpc_security_group_ids = [aws_security_group.web_sg.id] # reference → creates SG first}