Skip to content
6 min read·Lesson 4 of 10

Providers and Resources

Learn how to configure Terraform providers, declare resources, and use data sources to read existing infrastructure.

Providers and resources are the foundation of every Terraform configuration. Providers give Terraform the ability to interact with a cloud API; resources are the actual infrastructure objects you create and manage.

Configuring Providers

terraform {
  required_version = ">= 1.6.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"     # allow 5.x but not 6.x
    }
    random = {
      source  = "hashicorp/random"
      version = ">= 3.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"

  # Credentials: use environment variables or IAM role — never hardcode!
  # AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY env vars are read automatically
}

# Multiple provider configurations (aliases)
provider "aws" {
  alias  = "eu"
  region = "eu-west-1"
}
Security: Never put AWS credentials directly in provider blocks or commit them to Git. Use environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY), IAM roles, or AWS profiles instead.

Resources

A resource block declares an infrastructure object that Terraform will create and manage. The block label is resource_type.local_name:

resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true

  tags = {
    Name = "main-vpc"
  }
}

resource "aws_subnet" "public" {
  vpc_id                  = aws_vpc.main.id     # implicit dependency
  cidr_block              = "10.0.1.0/24"
  availability_zone       = "us-east-1a"
  map_public_ip_on_launch = true
}

resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.main.id
}

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw.id
  }
}

Resource Arguments

  • Required arguments: Must be specified (Terraform validates before plan)
  • Optional arguments: Have defaults if omitted
  • Computed attributes: Set by the provider after creation (e.g., id, arn)

Data Sources

Data sources let you query existing infrastructure that Terraform doesn't manage. Use them to reference shared resources like existing VPCs, the latest AMI ID, or a Route53 zone:

# Look up the latest Amazon Linux 2023 AMI
data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["al2023-ami-*-x86_64"]
  }
}

# Reference in a resource
resource "aws_instance" "web" {
  ami           = data.aws_ami.amazon_linux.id
  instance_type = "t3.micro"
}

# Look up an existing VPC by tag
data "aws_vpc" "shared" {
  tags = {
    Name = "shared-vpc"
  }
}

resource "aws_subnet" "app" {
  vpc_id     = data.aws_vpc.shared.id
  cidr_block = "10.1.10.0/24"
}

Resource Dependencies

Terraform automatically determines the order to create resources based on attribute references. If resource A references an attribute of resource B, Terraform creates B first:

# Terraform knows: create aws_vpc.main before aws_subnet.public
resource "aws_subnet" "public" {
  vpc_id = aws_vpc.main.id   # this reference creates the dependency
}

For dependencies that are not captured by attribute references (e.g., IAM policy must exist before an instance launches), use depends_on:

resource "aws_instance" "app" {
  ami           = data.aws_ami.amazon_linux.id
  instance_type = "t3.micro"

  depends_on = [
    aws_iam_role_policy_attachment.app_policy
  ]
}

Lifecycle Meta-Arguments

resource "aws_db_instance" "main" {
  # ...

  lifecycle {
    prevent_destroy       = true   # fail plan if this resource would be destroyed
    create_before_destroy = true   # create replacement before destroying original
    ignore_changes        = [      # don't update if these change outside Terraform
      password,
      engine_version,
    ]
  }
}

Next: Variables and Outputs — making your Terraform configurations reusable and parameterisable.

Key Takeaways

  • The required_providers block specifies which providers to use and their version constraints.
  • Providers are configured with credentials and region; avoid hardcoding secrets in provider blocks.
  • Resources are the core unit — each resource maps to one cloud infrastructure object.
  • Data sources read existing resources without managing them — useful for VPCs, AMIs, and shared resources.
  • Resource dependencies are inferred from attribute references; use depends_on for implicit dependencies.

Test your knowledge

Try exam-style practice questions to reinforce what you've learned.

Practice Questions →