Variables and Outputs
Chapter objectives
- Declare and use variables
- Understand the types of variables
- Pass values to variables
- Export outputs
1 - Input Variables
Basic declaration
# variables.tf
variable "region" {
description = "AWS region"
type = string
default = "eu-west-1"
}
variable "instance_type" {
description = "Type d'instance EC2"
type = string
default = "t2.micro"
}
variable "instance_count" {
description = "Nombre d'instances"
type = number
default = 1
}
variable "enable_monitoring" {
description = "Activer le monitoring"
type = bool
default = false
}
Usage
# main.tf
provider "aws" {
region = var.region
}
resource "aws_instance" "web" {
count = var.instance_count
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
monitoring = var.enable_monitoring
}
2 - Types of variables
Primitive types
variable "string_var" {
type = string
default = "hello"
}
variable "number_var" {
type = number
default = 42
}
variable "bool_var" {
type = bool
default = true
}
Collections
# Liste
variable "availability_zones" {
type = list(string)
default = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]
}
# Map
variable "tags" {
type = map(string)
default = {
Environment = "Production"
Team = "DevOps"
}
}
# Set
variable "allowed_ips" {
type = set(string)
default = ["10.0.0.1", "10.0.0.2"]
}
Structural types
# Object
variable "instance_config" {
type = object({
ami = string
instance_type = string
tags = map(string)
})
default = {
ami = "ami-12345678"
instance_type = "t2.micro"
tags = {
Name = "default"
}
}
}
# Tuple
variable "mixed_config" {
type = tuple([string, number, bool])
default = ["hello", 42, true]
}
# Liste d'objets
variable "servers" {
type = list(object({
name = string
type = string
port = number
}))
default = [
{ name = "web", type = "t2.micro", port = 80 },
{ name = "api", type = "t2.small", port = 8080 }
]
}
3 - Variable validation
Validation rules
variable "instance_type" {
type = string
description = "Type d'instance EC2"
validation {
condition = can(regex("^t[23]\\.", var.instance_type))
error_message = "Instance type must be t2.* or t3.*"
}
}
variable "environment" {
type = string
description = "Environnement de déploiement"
validation {
condition = contains(["dev", "staging", "production"], var.environment)
error_message = "Environment must be dev, staging, or production."
}
}
variable "port" {
type = number
description = "Port de l'application"
validation {
condition = var.port >= 1 && var.port <= 65535
error_message = "Port must be between 1 and 65535."
}
}
Multiple validations
variable "bucket_name" {
type = string
validation {
condition = length(var.bucket_name) >= 3
error_message = "Bucket name must be at least 3 characters."
}
validation {
condition = length(var.bucket_name) <= 63
error_message = "Bucket name must not exceed 63 characters."
}
validation {
condition = can(regex("^[a-z0-9][a-z0-9.-]*[a-z0-9]$", var.bucket_name))
error_message = "Bucket name must start and end with lowercase letter or number."
}
}
4 - Passing values
Methods by order of precedence
The terraform.tfvars file
# terraform.tfvars
region = "eu-west-1"
instance_type = "t3.micro"
instance_count = 3
environment = "production"
tags = {
Project = "MyApp"
Environment = "Production"
Team = "Platform"
}
Custom .tfvars file
# production.tfvars
environment = "production"
instance_type = "t3.large"
instance_count = 5
# staging.tfvars
environment = "staging"
instance_type = "t3.small"
instance_count = 2
# Utilisation
terraform plan -var-file="production.tfvars"
terraform apply -var-file="staging.tfvars"
Environment variables
# Format: TF_VAR_<variable_name>
export TF_VAR_region="eu-west-1"
export TF_VAR_instance_type="t3.micro"
export TF_VAR_environment="production"
# Utilisation
terraform plan
Command line
# Une variable
terraform plan -var="instance_type=t3.large"
# Plusieurs variables
terraform apply \
-var="instance_type=t3.large" \
-var="instance_count=5" \
-var="environment=production"
5 - Sensitive variables
Mark as sensitive
variable "db_password" {
description = "Database password"
type = string
sensitive = true
}
variable "api_key" {
description = "API key for external service"
type = string
sensitive = true
}
Behavior
# Le plan masque les valeurs sensibles
Terraform will perform the following actions:
# aws_db_instance.main will be created
+ resource "aws_db_instance" "main" {
+ password = (sensitive value)
}
Best practices
# ✅ Ne jamais mettre de default pour les secrets
variable "db_password" {
type = string
sensitive = true
# Pas de default!
}
# ✅ Utiliser avec des fichiers tfvars (ignorés par git)
# secrets.tfvars (dans .gitignore)
db_password = "super_secret_password"
6 - Local Values
Declaration
locals {
# Valeur simple
environment = "production"
# Valeur calculée
name_prefix = "${var.project}-${local.environment}"
# Tags communs
common_tags = {
Environment = local.environment
Project = var.project
ManagedBy = "Terraform"
}
# Transformation
instance_ids = [for i in aws_instance.web : i.id]
# Conditionnelle
enable_logging = local.environment == "production" ? true : false
}
Usage
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
tags = merge(local.common_tags, {
Name = "${local.name_prefix}-web"
})
}
resource "aws_s3_bucket" "logs" {
count = local.enable_logging ? 1 : 0
bucket = "${local.name_prefix}-logs"
tags = local.common_tags
}
Difference between variables and locals
7 - Outputs
Basic declaration
# outputs.tf
output "instance_id" {
description = "ID de l'instance EC2"
value = aws_instance.web.id
}
output "public_ip" {
description = "IP publique de l'instance"
value = aws_instance.web.public_ip
}
output "public_dns" {
description = "DNS public de l'instance"
value = aws_instance.web.public_dns
}
Outputs with expressions
# Liste d'IDs
output "instance_ids" {
description = "IDs de toutes les instances"
value = aws_instance.web[*].id
}
# Map d'IPs
output "instance_ips" {
description = "IPs par nom"
value = {
for instance in aws_instance.web :
instance.tags["Name"] => instance.public_ip
}
}
# Valeur conditionnelle
output "load_balancer_dns" {
description = "DNS du load balancer"
value = var.create_lb ? aws_lb.main[0].dns_name : null
}
Sensitive outputs
output "db_password" {
description = "Database password"
value = random_password.db.result
sensitive = true
}
# Affichage
# Outputs:
# db_password = <sensitive>
Output dependencies
output "connection_string" {
description = "Connection string pour la base de données"
value = "postgresql://${var.db_user}:${random_password.db.result}@${aws_db_instance.main.endpoint}"
sensitive = true
# Assurer que la ressource existe avant l'output
depends_on = [aws_db_instance.main]
}
8 - Outputs between modules
Child module
# modules/vpc/outputs.tf
output "vpc_id" {
description = "ID du VPC"
value = aws_vpc.main.id
}
output "public_subnet_ids" {
description = "IDs des subnets publics"
value = aws_subnet.public[*].id
}
output "private_subnet_ids" {
description = "IDs des subnets privés"
value = aws_subnet.private[*].id
}
Parent module
# main.tf
module "vpc" {
source = "./modules/vpc"
cidr_block = "10.0.0.0/16"
}
# Utilisation des outputs du module
resource "aws_instance" "web" {
subnet_id = module.vpc.public_subnet_ids[0]
# ...
}
# Re-export des outputs
output "vpc_id" {
value = module.vpc.vpc_id
}
9 - Viewing outputs
Commands
# Voir tous les outputs après apply
terraform output
# Un output spécifique
terraform output instance_id
# Format JSON
terraform output -json
# Valeur brute (sans quotes)
terraform output -raw public_ip
Example output
$ terraform output
instance_id = "i-0123456789abcdef0"
public_ip = "54.123.45.67"
public_dns = "ec2-54-123-45-67.eu-west-1.compute.amazonaws.com"
$ terraform output -json
{
"instance_id": {
"sensitive": false,
"type": "string",
"value": "i-0123456789abcdef0"
},
"public_ip": {
"sensitive": false,
"type": "string",
"value": "54.123.45.67"
}
}
10 - Best practices
Variables
# ✅ Toujours ajouter une description
variable "instance_type" {
description = "Type d'instance EC2 à utiliser"
type = string
default = "t2.micro"
}
# ✅ Utiliser la validation
variable "environment" {
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Invalid environment."
}
}
# ✅ Marquer les secrets comme sensitive
variable "db_password" {
type = string
sensitive = true
}
Outputs
# ✅ Description claire
output "load_balancer_url" {
description = "URL du load balancer pour accéder à l'application"
value = "https://${aws_lb.main.dns_name}"
}
# ✅ Exporter ce qui est utile
output "deployment_info" {
value = {
vpc_id = module.vpc.vpc_id
instance_ids = aws_instance.web[*].id
lb_dns = aws_lb.main.dns_name
}
}
Summary
Key points
- Variables make the configuration parameterizable
- Use validation for constraints
- Mark secrets as sensitive
- Locals simplify the code
- Outputs expose the important values
Practical exercises
- Create variables for the environment (dev/staging/prod)
- Add validation on the variables
- Use locals for common tags
- Export important information via outputs