G
GuideDevOps
Lesson 6 of 14

Variables & Outputs

Part of the Terraform tutorial series.

Input Variables

Variables are parameters that allow you to pass values into Terraform configurations without modifying the source code. They make configurations reusable and customizable.

Variable Declaration

variable "instance_type" {
  description = "Size of EC2 instance"
  type        = string
  default     = "t2.micro"
}

Components:

  • description: Documentation explaining the variable
  • type: What kind of value (string, number, bool, list, map, object)
  • default: Default value if not provided
  • sensitive: Hide from logs (for passwords, keys)
  • validation: Custom validation rules

Variable Types

TypeExampleUse Case
string"t2.micro"Single text value
number3Numeric value
booltrueTrue/false flag
list["az1", "az2"]Ordered collection
mapKey-value pairs
objectTyped collection
tuple["string", 1]Fixed-size mixed types
any(any value)Flexible, not recommended

String Variable

variable "environment" {
  description = "Environment name"
  type        = string
  default     = "dev"
}
 
# Usage:
resource "aws_instance" "web" {
  tags = {
    Environment = var.environment
  }
}

List Variable

variable "availability_zones" {
  description = "Availability zones for resources"
  type        = list(string)
  default     = ["us-east-1a", "us-east-1b"]
}
 
# Usage:
resource "aws_subnet" "private" {
  count             = length(var.availability_zones)
  availability_zone = var.availability_zones[count.index]
  # ...
}

Map Variable

variable "tags" {
  description = "Common tags for all resources"
  type        = map(string)
  default = {
    Team        = "Platform"
    Environment = "Production"
    CostCenter  = "Engineering"
  }
}
 
# Usage:
resource "aws_instance" "web" {
  tags = var.tags
}

Object Variable

variable "db_config" {
  description = "Database configuration"
  type = object({
    name          = string
    engine        = string
    instance_type = string
    storage_size  = number
  })
  
  default = {
    name          = "myapp"
    engine        = "mysql"
    instance_type = "db.t3.micro"
    storage_size  = 20
  }
}
 
# Usage:
resource "aws_db_instance" "main" {
  db_name    = var.db_config.name
  engine     = var.db_config.engine
  identifier = var.db_config.name
}

Variable Validation

variable "instance_count" {
  description = "Number of instances"
  type        = number
  
  validation {
    condition     = var.instance_count > 0 && var.instance_count <= 10
    error_message = "Instance count must be between 1 and 10"
  }
}
 
variable "environment" {
  description = "Environment"
  type        = string
  
  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "Environment must be dev, staging, or prod"
  }
}

Sensitive Variables

variable "db_password" {
  description = "Database password"
  type        = string
  sensitive   = true  # Don't show in logs
}
 
variable "api_key" {
  description = "API key"
  type        = string
  sensitive   = true
}
 
# Usage:
resource "aws_db_instance" "main" {
  password = var.db_password
}

Providing Variable Values

Method 1: terraform.tfvars

instance_type = "t2.small"
environment   = "staging"
region        = "us-west-2"

Method 2: Command line

terraform plan -var="instance_type=t2.small" -var="environment=staging"

Method 3: Environment variables

export TF_VAR_instance_type=t2.small
export TF_VAR_environment=staging
terraform plan

Method 4: .auto.tfvars files

# dev.auto.tfvars
instance_type = "t2.micro"
environment   = "dev"

# prod.auto.tfvars
instance_type = "t3.xlarge"
environment   = "prod"

Output Values

Outputs extract and display values from your infrastructure, similar to return values in programming. They're useful for showing important information after apply.

Output Declaration

output "instance_public_ip" {
  description = "Public IP of web server"
  value       = aws_instance.web.public_ip
}

Components:

  • description: Document what the output shows
  • value: The value to output
  • sensitive: Hide from logs (passwords, keys)
  • depends_on: Explicit dependency

Simple Outputs

# Single value
output "website_url" {
  description = "URL to access website"
  value       = "http://${aws_instance.web.public_ip}"
}
 
# List
output "security_group_ids" {
  description = "Security group IDs"
  value       = aws_security_group.web[*].id
}
 
# Map
output "instance_details" {
  description = "Instance information"
  value = {
    id       = aws_instance.web.id
    ip       = aws_instance.web.public_ip
    dns_name = aws_instance.web.public_dns
  }
}

Complex Outputs

output "load_balancer_endpoint" {
  description = "ALB endpoint"
  value = {
    dns_name       = aws_lb.main.dns_name
    zone_id        = aws_lb.main.zone_id
    target_groups = [
      for tg in aws_lb_target_group.main : {
        arn  = tg.arn
        name = tg.name
      }
    ]
  }
}

Sensitive Outputs

output "database_password" {
  description = "Database password"
  value       = random_password.db.result
  sensitive   = true  # Don't display in console
}
 
# To view: terraform output -json database_password

Conditional Outputs

variable "enable_monitoring" {
  type = bool
}
 
output "cloudwatch_dashboard" {
  description = "CloudWatch dashboard URL"
  value       = var.enable_monitoring ? aws_cloudwatch_dashboard.main.dashboard_body : null
}

Output with Dependencies

output "api_endpoint" {
  description = "API endpoint"
  value       = aws_api_gateway_deployment.main.invoke_url
  
  depends_on = [
    aws_api_gateway_integration.lambda
  ]
}

Accessing Outputs

After apply:

terraform output                    # Show all outputs
terraform output instance_public_ip # Show specific output
terraform output -json              # JSON format

In other configurations:

# reference_stack/main.tf
data "terraform_remote_state" "main" {
  backend = "s3"
  config = {
    bucket = "my-terraform-state"
    key    = "prod/terraform.tfstate"
    region = "us-east-1"
  }
}
 
resource "aws_route53_record" "web" {
  zone_id = aws_route53_zone.main.zone_id
  name    = "api.example.com"
  type    = "A"
  alias {
    name                   = data.terraform_remote_state.main.outputs.load_balancer_dns
    zone_id                = data.terraform_remote_state.main.outputs.load_balancer_zone_id
    evaluate_target_health = true
  }
}

Variable Best Practices

PracticeExampleBenefit
Use descriptionsdescription = "..."Self-documenting code
Set types explicitlytype = list(string)Validate inputs
Use validation blocksvalidation { condition = ... }Catch errors early
Default for devdefault = "t2.micro"Quick testing
Mark sensitive datasensitive = trueSecurity
Use terraform.tfvarsCreate per environmentEasy multi-environment
Organize by layerCore, networking, appMaintainability
Use modulesmodule "networking" {}Reusable components

Output Best Practices

PracticeExampleBenefit
Document everythingdescription = "..."Clear what matters
Show computed valuesvalue = aws_lb.main.dns_nameEasy reference
Group related outputsMap or object typeOrganized results
Use depends_on when neededdepends_on = [...]Correct ordering
Hide sensitive datasensitive = trueSecurity
Test outputsterraform outputVerify before use
Export to CI/CDterraform output -jsonAutomation

Common Patterns

Multi-Environment Setup

variable "environment" {
  type = string
}
 
variable "instance_type" {
  type = map(string)
  default = {
    dev     = "t2.micro"
    staging = "t2.small"
    prod    = "t3.large"
  }
}
 
resource "aws_instance" "main" {
  instance_type = var.instance_type[var.environment]
}

Feature Flags

variable "enable_monitoring" {
  type    = bool
  default = true
}
 
variable "enable_backup" {
  type    = bool
  default = true
}
 
resource "aws_cloudwatch_log_group" "main" {
  count             = var.enable_monitoring ? 1 : 0
  name              = "/aws/lambda/main"
  retention_in_days = 7
}

Exposing Computed Values

resource "aws_lb" "main" {
  # ...
}
 
output "load_balancer_dns" {
  description = "DNS name of load balancer"
  value       = aws_lb.main.dns_name
}
 
output "resources_created" {
  description = "Summary of created resources"
  value = {
    lb_dns    = aws_lb.main.dns_name
    lb_zone   = aws_lb.main.zone_id
    timestamp = timestamp()
  }
}