El Llenguatge HCL: Declarant Infraestructura
3. El Llenguatge HCL: Declarant Infraestructura
HCL (HashiCorp Configuration Language) és el llenguatge que utilitza Terraform. És un llenguatge declaratiu dissenyat per ser llegible per humans i fàcil d'escriure. Vegem els seus components principals.
Sintaxi Bàsica: Blocks, Arguments i Expressions
HCL es compon principalment de blocks (blocs). Un bloc té un tipus, zero o més etiquetes, i un cos que conté arguments i altres blocs niats:
block_type "label1" "label2" {
argument1 = value1
argument2 = value2
nested_block {
nested_argument = nested_value
}
}
Els tipus de blocs més comuns que utilitzaràs són:
terraform: Configuració de Terraform mateixprovider: Configuració de providersresource: Definició de recursos a creardata: Consulta d'informació existentvariable: Declaració de variables d'entradaoutput: Definició de valors de sortidamodule: Crida a mòduls reutilitzableslocals: Definició de valors locals (com variables internes)
Variables: Parametritzant la Configuració
Les variables permeten parametritzar la teva configuració, fent-la reutilitzable en diferents entorns. Declares variables així:
variable "environment" {
description = "L'entorn on desplegar (dev, staging, production)"
type = string
default = "dev"
}
variable "instance_count" {
description = "Nombre d'instàncies a crear"
type = number
default = 1
}
variable "enable_monitoring" {
description = "Si s'ha d'activar la monitorització"
type = bool
default = false
}
variable "allowed_ips" {
description = "IPs permeses per SSH"
type = list(string)
default = []
}
variable "tags" {
description = "Tags comuns per a tots els recursos"
type = map(string)
default = {
Managed_by = "Terraform"
}
}
Pots utilitzar les variables en la teva configuració amb la sintaxi var.nom_variable:
resource "aws_instance" "server" {
count = var.instance_count
ami = "ami-0c55b159cbfafe1f0"
instance_type = var.environment == "production" ? "t3.medium" : "t3.micro"
tags = merge(var.tags, {
Environment = var.environment
Name = "server-${var.environment}-${count.index + 1}"
})
}
Pots passar valors a les variables de diverses maneres:
-
Fitxer terraform.tfvars:
-
Línia de comandes:
-
Variables d'entorn:
Data Sources: Consultant Informació Existent
Els data sources et permeten consultar informació que ja existeix, sigui en el teu cloud provider o en altres llocs:
# Obtenir l'AMI més recent d'Ubuntu
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}
# Obtenir la VPC per defecte
data "aws_vpc" "default" {
default = true
}
# Obtenir les zones de disponibilitat
data "aws_availability_zones" "available" {
state = "available"
}
# Utilitzar les dades
resource "aws_instance" "server" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
availability_zone = data.aws_availability_zones.available.names[0]
}
Els data sources són extremadament útils perquè et permeten fer la teva configuració més flexible i adaptable. Per exemple, en lloc d'hardcodejar l'ID d'una AMI (que canvia amb cada regió i amb cada actualització), pots cercar dinàmicament la AMI més recent que compleixi certs criteris.
Outputs: Exportant Informació
Els outputs et permeten exportar informació sobre la infraestructura creada:
output "server_public_ip" {
description = "IP pública del servidor"
value = aws_instance.server.public_ip
}
output "server_private_ip" {
description = "IP privada del servidor"
value = aws_instance.server.private_ip
sensitive = false
}
output "database_endpoint" {
description = "Endpoint de la base de dades"
value = aws_db_instance.main.endpoint
sensitive = true
}
Després d'executar terraform apply, pots veure els outputs:
$ terraform output
server_public_ip = "203.0.113.45"
server_private_ip = "10.0.1.23"
database_endpoint = <sensitive>
$ terraform output database_endpoint
mysql-instance.abc123.eu-west-1.rds.amazonaws.com:3306
Els outputs són útils per: - Veure informació important després de crear recursos - Passar informació entre diferents configuracions de Terraform - Integrar amb altres eines (per exemple, passar IPs a Ansible)
Locals: Variables Intermèdies
Els locals són com variables, però les calcules dins de la configuració en lloc de passar-les des de fora:
locals {
# Combinar nom del projecte i entorn
name_prefix = "${var.project_name}-${var.environment}"
# Tags comuns per a tots els recursos
common_tags = {
Project = var.project_name
Environment = var.environment
ManagedBy = "Terraform"
Owner = var.team_email
}
# Calcular la mida de la base de dades segons l'entorn
db_instance_class = var.environment == "production" ? "db.t3.large" : "db.t3.micro"
# Determinar si s'ha d'activar multi-AZ
db_multi_az = var.environment == "production" ? true : false
}
resource "aws_instance" "server" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
tags = merge(local.common_tags, {
Name = "${local.name_prefix}-server"
})
}
resource "aws_db_instance" "main" {
instance_class = local.db_instance_class
multi_az = local.db_multi_az
tags = merge(local.common_tags, {
Name = "${local.name_prefix}-database"
})
}
Els locals són ideals per evitar repetició en el teu codi i per fer càlculs que no vols exposar com variables d'entrada.
Expressions i Funcions: Lògica en HCL
HCL suporta expressions i funcions que et permeten fer la teva configuració dinàmica i intel·ligent.
Expressions condicionals:
Bucles amb for_each:
variable "users" {
default = {
alice = { role = "admin" }
bob = { role = "developer" }
carol = { role = "developer" }
}
}
resource "aws_iam_user" "users" {
for_each = var.users
name = each.key
tags = {
Role = each.value.role
}
}
Bucles amb count:
resource "aws_instance" "server" {
count = 3
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
tags = {
Name = "server-${count.index + 1}"
}
}
Funcions útils:
HCL inclou desenes de funcions integrades. Algunes de les més útils:
# Funcions de strings
upper("hello") # "HELLO"
lower("WORLD") # "world"
format("server-%02d", 5) # "server-05"
join(", ", ["a", "b", "c"]) # "a, b, c"
split(",", "a,b,c") # ["a", "b", "c"]
# Funcions de col·leccions
length([1, 2, 3]) # 3
concat([1, 2], [3, 4]) # [1, 2, 3, 4]
merge({a=1}, {b=2}) # {a=1, b=2}
keys({a=1, b=2}) # ["a", "b"]
values({a=1, b=2}) # [1, 2]
# Funcions de xarxa
cidrsubnet("10.0.0.0/16", 8, 1) # "10.0.1.0/24"
cidrhost("10.0.1.0/24", 5) # "10.0.1.5"
# Funcions de fitxers
file("path/to/file.txt") # Llegir un fitxer
templatefile("path/to/template", vars) # Processar un template
# Funcions de codificació
base64encode("hello") # "aGVsbG8="
jsonencode({a = 1, b = 2}) # '{"a":1,"b":2}'
yamlencode({a = 1, b = 2}) # "a: 1\nb: 2\n"
Referència oficial: La documentació completa de funcions està disponible a: https://www.terraform.io/language/functions
Dynamic Blocks: Configuració Repetitiva
Quan necessites crear múltiples blocs similars dins d'un recurs, pots utilitzar dynamic blocks:
variable "ingress_rules" {
default = [
{ port = 22, protocol = "tcp", cidr = "203.0.113.0/24" },
{ port = 80, protocol = "tcp", cidr = "0.0.0.0/0" },
{ port = 443, protocol = "tcp", cidr = "0.0.0.0/0" },
]
}
resource "aws_security_group" "web" {
name = "web-sg"
dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.port
to_port = ingress.value.port
protocol = ingress.value.protocol
cidr_blocks = [ingress.value.cidr]
}
}
}
Això és molt més net que haver de repetir el bloc ingress múltiples vegades.