Variables, Outputs, and Dependencies in Terraform
/ 5 min read
Series Navigation
- Part 1: Terraform Fundamentals
- Part 2: Resource Management and State
- Part 3: Essential Terraform Functions
- Part 4: Variables, Outputs, and Dependencies (Current)
- Part 5: Terraform Modules and Workspace Management
- Part 6: Managing Remote State and Backend
- Part 7: Testing and CI/CD Integration
- Part 8: Terraform Security and Best Practices
Understanding Variables in Terraform
Variables make your Terraform configurations flexible and reusable. This post explores different types of variables and how to use them effectively.
Input Variables
Basic Variable Declaration
variable "region" { description = "AWS region to deploy resources" type = string default = "us-west-2"}
variable "instance_count" { description = "Number of EC2 instances to create" type = number default = 1}
variable "environment" { description = "Environment name" type = string validation { condition = contains(["dev", "staging", "prod"], var.environment) error_message = "Environment must be dev, staging, or prod." }}Complex Variable Types
# List variablevariable "availability_zones" { description = "List of availability zones" type = list(string) default = ["us-west-2a", "us-west-2b"]}
# Map variablevariable "instance_types" { description = "Instance types per environment" type = map(string) default = { dev = "t3.micro" staging = "t3.small" prod = "t3.medium" }}
# Object variablevariable "vpc_config" { description = "VPC configuration" type = object({ cidr_block = string enable_dns = bool tags = map(string) }) default = { cidr_block = "10.0.0.0/16" enable_dns = true tags = { Environment = "dev" } }}Variable Validation
variable "database_port" { description = "Database port number" type = number
validation { condition = var.database_port > 1024 && var.database_port < 65535 error_message = "Database port must be between 1024 and 65535." }}
variable "environment_name" { description = "Environment name with specific format" type = string
validation { condition = can(regex("^[a-z]+-[a-z]+$", var.environment_name)) error_message = "Environment name must be lowercase and contain two words separated by a hyphen." }}Local Variables
Local variables are useful for intermediate calculations and reducing repetition.
locals { # Simple local variable common_tags = { Project = var.project_name Environment = var.environment ManagedBy = "terraform" }
# Computed local variable instance_name = "${var.project_name}-${var.environment}-instance"
# Complex transformation subnet_cidrs = [ for index, az in var.availability_zones : cidrsubnet(var.vpc_cidr, 8, index) ]
# Conditional local variable instance_type = var.environment == "prod" ? "t3.medium" : "t3.micro"}Output Values
Outputs expose specific values that can be queried or used by other configurations.
Basic Outputs
output "vpc_id" { description = "ID of the created VPC" value = aws_vpc.main.id}
output "instance_ips" { description = "Private IPs of created instances" value = aws_instance.web[*].private_ip}
output "database_endpoint" { description = "Database connection endpoint" value = aws_db_instance.main.endpoint sensitive = true # Marks output as sensitive}Complex Outputs
output "instance_details" { description = "Details of all created instances" value = { for instance in aws_instance.web : instance.id => { private_ip = instance.private_ip public_ip = instance.public_ip subnet_id = instance.subnet_id tags = instance.tags } }}
output "subnet_info" { description = "Information about created subnets" value = [ for subnet in aws_subnet.main : { id = subnet.id cidr_block = subnet.cidr_block az = subnet.availability_zone } ]}Variable Files
terraform.tfvars
region = "us-west-2"instance_count = 2environment = "prod"
vpc_config = { cidr_block = "172.16.0.0/16" enable_dns = true tags = { Environment = "prod" Team = "infrastructure" }}Environment-specific Variables
environment = "prod"instance_count = 3instance_type = "t3.medium"
vpc_config = { cidr_block = "10.0.0.0/16" enable_dns = true tags = { Environment = "prod" Compliance = "pci" }}
# dev.tfvarsenvironment = "dev"instance_count = 1instance_type = "t3.micro"
vpc_config = { cidr_block = "172.16.0.0/16" enable_dns = true tags = { Environment = "dev" Team = "development" }}Managing Dependencies
Implicit Dependencies
# VPC and subnet with implicit dependencyresource "aws_vpc" "main" { cidr_block = var.vpc_cidr}
resource "aws_subnet" "main" { vpc_id = aws_vpc.main.id # Implicit dependency cidr_block = var.subnet_cidr}Explicit Dependencies
# Using depends_on for explicit dependenciesresource "aws_iam_role_policy" "example" { name = "example" role = aws_iam_role.example.id policy = data.aws_iam_policy_document.example.json
depends_on = [ aws_iam_role.example, aws_s3_bucket.log_bucket ]}Data Source Dependencies
# Using data sourcesdata "aws_vpc" "existing" { id = var.vpc_id}
resource "aws_subnet" "example" { vpc_id = data.aws_vpc.existing.id cidr_block = cidrsubnet(data.aws_vpc.existing.cidr_block, 8, 1)}Variable Best Practices
-
Use Descriptive Names
# Goodvariable "vpc_cidr_block" { }# Avoidvariable "vcb" { } -
Always Include Descriptions
variable "environment" {description = "The deployment environment (dev/staging/prod)"type = string} -
Set Appropriate Defaults
variable "enable_monitoring" {description = "Enable detailed monitoring"type = booldefault = false # Conservative default} -
Use Type Constraints
variable "instance_tags" {description = "Tags to apply to instances"type = map(string)default = {}}
Practical Examples
Complete Infrastructure Configuration
variable "project" { description = "Project name" type = string}
variable "environment" { description = "Environment name" type = string}
# main.tflocals { name_prefix = "${var.project}-${var.environment}"
common_tags = { Project = var.project Environment = var.environment ManagedBy = "terraform" }}
resource "aws_vpc" "main" { cidr_block = var.vpc_cidr
tags = merge(local.common_tags, { Name = "${local.name_prefix}-vpc" })}
resource "aws_subnet" "private" { count = length(var.availability_zones) vpc_id = aws_vpc.main.id cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index) availability_zone = var.availability_zones[count.index]
tags = merge(local.common_tags, { Name = "${local.name_prefix}-private-${count.index + 1}" Type = "private" })}
# outputs.tfoutput "vpc_details" { description = "VPC and subnet details" value = { vpc_id = aws_vpc.main.id vpc_cidr = aws_vpc.main.cidr_block subnet_ids = aws_subnet.private[*].id }}Next Steps
In Part 5: Terraform Modules and Workspace Management, we’ll explore how to organize your Terraform code into reusable modules and manage different workspaces.