TL;DR — Resumen Rápido
OpenTofu es el fork open-source de Terraform de la Linux Foundation. Aprende instalación, migración desde Terraform, cifrado de estado, HCL y CI/CD.
OpenTofu es el fork open-source de Terraform mantenido bajo la Linux Foundation, creado después de que HashiCorp cambiara la licencia de Terraform de Mozilla Public License 2.0 a la Business Source License (BSL) en agosto de 2023. Esta guía cubre todo lo que necesitas para entender los orígenes de OpenTofu, instalarlo, migrar proyectos Terraform existentes sin fricción, dominar los fundamentos de HCL, gestionar el estado de forma segura con cifrado del lado del cliente e integrarlo en pipelines CI/CD modernos.
Por Qué Existe OpenTofu: La Historia del Fork de Licencia
Terraform fue open-source bajo MPL 2.0 durante más de nueve años. El 10 de agosto de 2023, HashiCorp anunció que Terraform 1.6 y todas las versiones futuras se publicarían bajo la Business Source License (BSL 1.1) — una licencia de código fuente disponible que restringe el uso del software para competir comercialmente con HashiCorp.
La respuesta de la comunidad fue rápida. En días, ingenieros de Spacelift, Gruntwork, Env0, Harness, Scalr y otros firmaron una carta abierta y crearon el fork OpenTofu desde la última versión MPL 2.0 (Terraform 1.5.7). La Linux Foundation aceptó OpenTofu como proyecto en septiembre de 2023, y OpenTofu 1.6.0 — la primera versión estable con nuevas funciones — se lanzó en enero de 2024.
Implicaciones clave del cambio de licencia:
- Terraform OSS ahora es BSL — no puedes construir un producto competidor con él
- OpenTofu permanece en MPL 2.0 — completamente open-source, sin restricciones comerciales
- HashiCorp (ahora IBM) controla Terraform Cloud y Terraform Enterprise
- OpenTofu tiene gobernanza comunitaria, con un Comité Técnico de Dirección y proceso RFC abierto
Compatibilidad OpenTofu vs Terraform
OpenTofu 1.6–1.9 apunta a compatibilidad directa con Terraform 1.5.x:
| Característica | OpenTofu | Terraform OSS |
|---|---|---|
| Sintaxis HCL | Idéntica a TF 1.5 | Igual |
| Ecosistema de proveedores | registry.opentofu.org + fallback terraform.io | registry.terraform.io |
| Formato de archivo de estado | .tfstate compatible en binario | Mismo formato |
.terraform.lock.hcl | Funciona sin cambios | Igual |
| Comandos del binario | tofu reemplaza terraform 1:1 | terraform |
| Cifrado de estado | Sí (AES-GCM, KMS) | No |
| Evaluación temprana de variables | Sí (1.8+) | No |
| Funciones definidas por proveedor | Sí (1.8+) | No |
Bloque removed | Sí (1.7+) | Parcial |
| Terraform Cloud | No compatible | Nativo |
El costo práctico de migración para la mayoría de equipos es un buscar-y-reemplazar de terraform → tofu en scripts de CI. Los archivos HCL, proveedores, módulos y archivos de estado no se tocan.
Instalación
apt (Debian / Ubuntu)
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://get.opentofu.org/opentofu.gpg | \
sudo tee /etc/apt/keyrings/opentofu.gpg > /dev/null
curl -fsSL https://packages.opentofu.org/opentofu/tofu/gpgkey | \
sudo gpg --no-tty --batch --dearmor -o /etc/apt/keyrings/opentofu-repo.gpg > /dev/null
echo "deb [signed-by=/etc/apt/keyrings/opentofu.gpg,/etc/apt/keyrings/opentofu-repo.gpg] \
https://packages.opentofu.org/opentofu/tofu/any/ any main" | \
sudo tee /etc/apt/sources.list.d/opentofu.list
sudo apt update && sudo apt install -y opentofu
tofu version
dnf (RHEL / Fedora / Rocky)
sudo tee /etc/yum.repos.d/opentofu.repo <<'EOF'
[opentofu]
name=opentofu
baseurl=https://packages.opentofu.org/opentofu/tofu/rpm_any/rpm_any/$basearch
enabled=1
gpgcheck=1
gpgkey=https://get.opentofu.org/opentofu.gpg
EOF
sudo dnf install -y opentofu
Homebrew
brew install opentofu
asdf
asdf plugin add opentofu
asdf install opentofu 1.9.0
asdf global opentofu 1.9.0
Migración desde Terraform
Paso 1 — Instalar OpenTofu junto a Terraform
Ambos binarios pueden coexistir. tofu y terraform son ejecutables separados.
Paso 2 — Ejecutar tofu init en tu proyecto existente
cd mi-proyecto-terraform/
tofu init
OpenTofu lee el mismo bloque versions.tf / required_providers. Descarga proveedores desde registry.opentofu.org. Tu .terraform.lock.hcl se reutiliza tal cual.
Paso 3 — Reemplazar terraform por tofu en scripts CI
# Antes
terraform fmt -check
terraform init -input=false
terraform plan -out=tfplan
terraform apply -auto-approve tfplan
# Después (sin cambio de lógica)
tofu fmt -check
tofu init -input=false
tofu plan -out=tfplan
tofu apply -auto-approve tfplan
Paso 4 — Verificar compatibilidad de estado
El formato JSON de .tfstate es idéntico. Si gestionas estado remoto (S3, GCS, Azure Blob), no se necesita migración. Ejecuta un plan con OpenTofu: debería mostrar sin cambios si la infraestructura coincide con el estado existente.
tofu plan
# Esperado: No changes. Your infrastructure matches the configuration.
Fundamentos de HCL
Proveedores, Recursos y Fuentes de Datos
terraform {
required_version = ">= 1.6"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.40"
}
}
}
provider "aws" {
region = var.aws_region
}
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "${var.project}-vpc"
Environment = var.environment
}
}
data "aws_ami" "al2023" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-2023.*-x86_64"]
}
}
Variables, Locales y Salidas
variable "environment" {
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "El entorno debe ser dev, staging o prod."
}
}
locals {
common_tags = {
Project = var.project
Environment = var.environment
ManagedBy = "opentofu"
}
}
output "vpc_id" {
description = "ID de la VPC creada"
value = aws_vpc.main.id
}
count y for_each
resource "aws_subnet" "public" {
count = 3
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index)
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = merge(local.common_tags, { Name = "${var.project}-publica-${count.index + 1}" })
}
resource "aws_s3_bucket" "this" {
for_each = var.buckets
bucket = "${var.project}-${each.key}-${data.aws_caller_identity.current.account_id}"
tags = merge(local.common_tags, { Name = each.key })
}
Bloques dinámicos
resource "aws_security_group" "web" {
name = "${var.project}-sg-web"
vpc_id = aws_vpc.main.id
dynamic "ingress" {
for_each = var.reglas_ingreso
content {
from_port = ingress.value.puerto
to_port = ingress.value.puerto
protocol = ingress.value.protocolo
cidr_blocks = ingress.value.cidrs
description = ingress.value.descripcion
}
}
}
Gestión de Estado
Backends remotos
# S3 + DynamoDB
terraform {
backend "s3" {
bucket = "mi-bucket-estado-tofu"
key = "prod/infra/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "tofu-bloqueos-estado"
encrypt = true
}
}
Cifrado de Estado del Lado del Cliente (exclusivo de OpenTofu)
# AWS KMS (recomendado para producción)
terraform {
encryption {
key_provider "aws_kms" "clave_prod" {
kms_key_id = "arn:aws:kms:us-east-1:123456789012:key/mrk-abc123"
region = "us-east-1"
key_spec = "AES_256"
}
method "aes_gcm" "metodo_prod" {
keys = key_provider.aws_kms.clave_prod
}
state {
method = method.aes_gcm.metodo_prod
}
plan {
method = method.aes_gcm.metodo_prod
}
}
}
Comandos útiles de estado
# Listar todos los recursos en el estado
tofu state list
# Mostrar detalles de un recurso específico
tofu state show aws_instance.web[0]
# Importar un recurso cloud existente al estado
tofu import aws_s3_bucket.assets mi-bucket-existente
# Mover un recurso a una nueva dirección
tofu state mv aws_instance.web aws_instance.app
# Desbloquear un estado bloqueado
tofu force-unlock LOCK_ID
Módulos
# Ruta local
module "vpc" {
source = "./modules/vpc"
project = var.project
cidr = "10.0.0.0/16"
}
# Etiqueta Git
module "vpc" {
source = "git::https://github.com/org/infra-modules.git//vpc?ref=v2.3.0"
project = var.project
cidr = "10.1.0.0/16"
}
# Registro de OpenTofu
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 20.0"
cluster_name = "${var.project}-${var.environment}"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.subnet_ids
}
Espacios de Trabajo (Workspaces)
tofu workspace new staging
tofu workspace new prod
tofu workspace list
tofu workspace select staging
locals {
config_entorno = {
dev = { tipo_instancia = "t3.micro", min = 1, max = 2 }
staging = { tipo_instancia = "t3.small", min = 2, max = 4 }
prod = { tipo_instancia = "t3.medium", min = 3, max = 10 }
}
actual = local.config_entorno[terraform.workspace]
}
Integración CI/CD
GitHub Actions con OIDC
name: OpenTofu Plan y Apply
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
id-token: write
contents: read
pull-requests: write
jobs:
tofu:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configurar OpenTofu
uses: opentofu/setup-opentofu@v1
with:
tofu_version: "1.9.0"
- name: Configurar credenciales AWS via OIDC
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsTofu
aws-region: us-east-1
- name: tofu init
run: tofu init -input=false
- name: tofu fmt check
run: tofu fmt -check -recursive
- name: tofu validate
run: tofu validate
- name: tofu plan
run: tofu plan -out=tfplan -input=false
- name: tofu apply
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: tofu apply -auto-approve tfplan
Tabla Comparativa de Herramientas
| Característica | OpenTofu | Terraform OSS | Terraform Cloud | Pulumi | Crossplane | CDK for TF |
|---|---|---|---|---|---|---|
| Licencia | MPL 2.0 | BSL 1.1 | Propietaria | Apache 2.0 | Apache 2.0 | MPL 2.0 |
| Lenguaje | HCL | HCL | HCL | Python/TS/Go | YAML/Go | Python/TS/Java |
| Cifrado de estado | Sí (integrado) | No | Sí (gestionado) | Sí (gestionado) | N/A | No |
| Evaluación temprana de vars | Sí (1.8+) | No | No | N/A | N/A | N/A |
| Ecosistema de proveedores | Proveedores Terraform | Proveedores Terraform | Proveedores Terraform | Proveedores Pulumi + puente TF | CRDs Kubernetes | Proveedores Terraform |
| Curva de aprendizaje | Baja (HCL) | Baja (HCL) | Baja | Media (código real) | Alta (CRDs K8s) | Media |
| Mejor para | IaC open-source | Equipos pequeños aceptando BSL | Equipos que quieren backend gestionado | Desarrolladores que prefieren código | Equipos nativos de Kubernetes | Desarrolladores que no les gusta HCL |
Ejemplo Práctico: Infraestructura AWS
# main.tf
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_support = true
enable_dns_hostnames = true
tags = merge(local.common_tags, { Name = "${var.project}-vpc" })
}
resource "aws_subnet" "public" {
count = var.az_count
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index)
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = merge(local.common_tags, { Name = "${var.project}-publica-${count.index + 1}" })
}
resource "aws_lb" "main" {
name = "${var.project}-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.alb.id]
subnets = aws_subnet.public[*].id
tags = local.common_tags
}
resource "aws_autoscaling_group" "app" {
name = "${var.project}-asg"
vpc_zone_identifier = aws_subnet.private[*].id
target_group_arns = [aws_lb_target_group.app.arn]
min_size = local.actual.min
max_size = local.actual.max
launch_template {
id = aws_launch_template.app.id
version = "$Latest"
}
}
output "dns_alb" {
description = "Nombre DNS del Application Load Balancer"
value = aws_lb.main.dns_name
}
Casos Especiales y Errores Comunes
Fallback del registro de proveedores: OpenTofu verifica registry.opentofu.org primero. Si un proveedor solo está en registry.terraform.io, añade un source explícito en required_providers. La mayoría de los proveedores principales se sincronizan automáticamente.
Migración de cifrado de estado: Habilitar el cifrado en un archivo de estado no cifrado existente es una operación unidireccional por clave. Planifica el despliegue cuidadosamente — documenta el ARN de tu clave y asegúrate de que varios miembros del equipo tengan acceso a KMS antes de habilitarlo.
Evaluación temprana de variables (1.8+): Las variables ahora pueden usarse en los bloques backend y provider. Esto elimina la necesidad de soluciones alternativas de configuración parcial de backend en muchas configuraciones.
Aislamiento de workspaces: Los workspaces CLI comparten el mismo bucket de backend pero usan diferentes claves de estado. NO proporcionan aislamiento de credenciales en la nube, roles IAM o configuraciones de proveedor — usa módulos raíz separados para verdadero aislamiento de entornos.
Resumen
- OpenTofu fue bifurcado de Terraform 1.5.7 en agosto de 2023 tras el cambio de licencia BSL de HashiCorp, y ahora es un proyecto de la Linux Foundation bajo MPL 2.0
- Es un reemplazo directo de Terraform 1.5.x — mismo HCL, mismos proveedores, mismo formato de estado; la migración solo requiere sustituir
tofuporterraformen los comandos - El cifrado de estado del lado del cliente (AES-GCM, AWS KMS, GCP KMS) es la característica nueva más significativa de OpenTofu — no disponible en Terraform OSS
- La evaluación temprana de variables (1.8+) permite variables en los bloques
backendyprovider, eliminando muchas soluciones alternativas de configuración parcial - Instala mediante
apt,dnf,brew,asdf, Docker o la acción de GitHubsetup-opentofu - La integración CI/CD es sencilla: reemplaza
terraformportofu, usa OIDC para autenticación en la nube y sigue el flujo plan → revisión → apply - Usa módulos y
for_eachcon mapas para construir configuraciones de infraestructura componibles y sin repetición
Artículos Relacionados
- Introducción a Terraform: Infraestructura como Código
- Ansible Playbooks y Roles: Guía Completa de Automatización de Infraestructura
- Ansible para Principiantes: Automatiza la Configuración de Servidores Sin Agente
- Docker Compose: Define y Ejecuta Aplicaciones Multi-Contenedor
- GitHub Actions: Guía Completa de Automatización CI/CD