Terraform Core Concepts
Understanding Terraform's core concepts is essential for working effectively with Infrastructure as Code. This guide covers the fundamental building blocks of Terraform.
Configuration Files
What are Configuration Files?
Terraform uses text files with the .tf extension to describe infrastructure.
These files use HCL (HashiCorp Configuration Language) syntax.
File Types:
*.tf- Configuration files (main.tf, variables.tf, outputs.tf)*.tfvars- Variable values (terraform.tfvars, prod.tfvars)*.tfstate- State files (terraform.tfstate).terraform.lock.hcl- Dependency lock file
Providers
What are Providers?
Providers are plugins that Terraform uses to interact with cloud providers, SaaS providers, and other APIs. Each provider adds a set of resource types and data sources that Terraform can manage.
Common Providers:
- AWS -
hashicorp/aws - Azure -
hashicorp/azurerm - GCP -
hashicorp/google - Docker -
kreuzwerker/docker - Kubernetes -
hashicorp/kubernetes
Provider Block Example:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-east-1"
# Optional: Configure via environment variables
# AWS_ACCESS_KEY_ID
# AWS_SECRET_ACCESS_KEY
# AWS_REGION
}
Resources
What are Resources?
Resources are the most important element in Terraform. Each resource block describes one or more infrastructure objects, such as virtual networks, compute instances, or higher-level components.
Resource Block Syntax:
resource "resource_type" "resource_name" {
# Resource configuration
argument1 = value1
argument2 = value2
# Nested blocks
nested_block {
nested_argument = value
}
}
Example: AWS EC2 Instance
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = {
Name = "WebServer"
Environment = "Production"
}
}
Accessing Resource Attributes:
# Reference resource
resource "aws_instance" "web" {
# ...
}
# Use resource attributes in other resources
resource "aws_eip" "web_ip" {
instance = aws_instance.web.id
vpc = true
}
# Output resource attributes
output "instance_id" {
value = aws_instance.web.id
}
output "public_ip" {
value = aws_instance.web.public_ip
}
Data Sources
What are Data Sources?
Data sources allow Terraform to use information defined outside of Terraform, or defined by another separate Terraform configuration. Data sources fetch data that can be used elsewhere in your configuration.
Data Source Example:
# Fetch latest AMI
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
}
# Use data source in resource
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id
instance_type = "t2.micro"
}
Common Data Sources:
aws_ami- Get AMI informationaws_vpc- Get VPC informationaws_subnet- Get subnet informationaws_availability_zones- Get availability zones
Variables
Input Variables
Variables allow you to customize aspects of Terraform modules without altering the module's source code. They make your configuration more flexible and reusable.
Variable Declaration (variables.tf):
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t2.micro"
validation {
condition = can(regex("^t[23]\\.", var.instance_type))
error_message = "Instance type must be t2 or t3 family."
}
}
variable "tags" {
description = "Tags to apply to resources"
type = map(string)
default = {}
}
variable "enabled" {
description = "Enable or disable resources"
type = bool
default = true
}
variable "subnet_ids" {
description = "List of subnet IDs"
type = list(string)
}
Variable Types:
string- Text valuesnumber- Numeric valuesbool- True/false valueslist(type)- Ordered listmap(type)- Key-value pairsset(type)- Unordered collectionobject({...})- Structured objectstuple([...])- Fixed-length list
Using Variables:
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = var.instance_type # Reference variable
tags = var.tags
}
Providing Variable Values:
# terraform.tfvars
instance_type = "t3.medium"
tags = {
Environment = "production"
Project = "web-app"
}
# Command line
terraform apply -var="instance_type=t3.large"
# Environment variables
export TF_VAR_instance_type="t3.large"
terraform apply
Outputs
Output Values
Output values make information about your infrastructure available on the command line, and can expose information for other Terraform configurations to use.
Output Declaration (outputs.tf):
output "instance_id" {
description = "ID of the EC2 instance"
value = aws_instance.web.id
sensitive = false
}
output "public_ip" {
description = "Public IP address of the instance"
value = aws_instance.web.public_ip
}
output "private_key" {
description = "Private key (sensitive)"
value = tls_private_key.example.private_key_pem
sensitive = true
}
output "all_tags" {
description = "All tags from the instance"
value = aws_instance.web.tags
}
Using Outputs:
# View all outputs
terraform output
# View specific output
terraform output instance_id
# View outputs as JSON
terraform output -json
# Use output in other configurations
instance_id = module.ec2.instance_id
Local Values
What are Local Values?
Local values assign a name to an expression so you can use it multiple times within a module without repeating it. They help reduce duplication and make your code more readable.
Local Values Example:
locals {
common_tags = {
Environment = var.environment
Project = "web-app"
ManagedBy = "Terraform"
}
instance_name = "${var.environment}-${var.project}-instance"
# Computed values
availability_zones = data.aws_availability_zones.available.names
}
# Use local values
resource "aws_instance" "web" {
tags = local.common_tags
tags = merge(local.common_tags, {
Name = local.instance_name
})
}
Expressions
Types of Expressions
Interpolation:
resource "aws_instance" "web" {
ami = "ami-${var.ami_id}"
user_data = <<-EOF
echo "Instance ID: ${aws_instance.web.id}"
EOF
}
Functions:
# String functions
resource "aws_s3_bucket" "example" {
bucket = "${var.project_name}-${random_id.bucket_suffix.hex}"
}
# Numeric functions
resource "aws_instance" "web" {
count = length(var.subnet_ids)
# ...
}
# Collection functions
locals {
merged_tags = merge(var.common_tags, var.additional_tags)
subnet_cidrs = [for s in var.subnets : s.cidr]
# Conditional
instance_type = var.environment == "prod" ? "t3.large" : "t3.micro"
}
Built-in Functions:
upper(),lower()- String manipulationlength()- Get length of collectionsmerge()- Merge mapssplit(),join()- String operationsfile()- Read file contentsjsonencode(),jsondecode()- JSON operations
Modules
What are Modules?
Modules are containers for multiple resources that are used together. They allow you to organize your code, reuse infrastructure, and create abstractions.
Using Modules:
module "vpc" {
source = "./modules/vpc"
vpc_cidr = "10.0.0.0/16"
environment = var.environment
tags = var.tags
}
# Access module outputs
resource "aws_instance" "web" {
subnet_id = module.vpc.private_subnet_id
# ...
}
State
What is State?
Terraform must store state about your managed infrastructure and configuration. This state is used to map real world resources to your configuration, keep track of metadata, and improve performance.
State File Location:
- Local -
terraform.tfstate(default) - Remote - S3, Azure Storage, GCS, Terraform Cloud
Dependencies
Resource Dependencies
Terraform automatically tracks dependencies between resources. When you reference one resource from another, Terraform understands the dependency relationship.
Implicit Dependencies:
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "web" {
vpc_id = aws_vpc.main.id # Implicit dependency
cidr_block = "10.0.0.0/24"
}
Explicit Dependencies:
resource "aws_instance" "web" {
# ...
depends_on = [
aws_iam_role.example,
aws_security_group.example
]
}
Next Steps
- Providers & Resources - Working with providers and resources
- State Management - Understanding and managing Terraform state
- Modules - Creating reusable modules
- Getting Started - Back to basics