OpenTofu
Strumento open-source per l’infrastructure as code e fork guidato dalla community di Terraform.
Installazione
Sezione intitolata “Installazione”Installazione per Piattaforma
Sezione intitolata “Installazione per Piattaforma”| Comando | Descrizione |
|---|---|
brew install opentofu | Installa su macOS con Homebrew |
snap install --classic opentofu | Installa su Linux con Snap |
choco install opentofu | Installa su Windows con Chocolatey |
curl -fsSL https://get.opentofu.org/install-opentofu.sh | sh | Installa tramite script shell |
docker run -it ghcr.io/opentofu/opentofu | Esegui via Docker |
tofu --version | Mostra la versione installata |
tofu -help | Mostra i comandi disponibili |
Installazione con Gestore di Versioni
Sezione intitolata “Installazione con Gestore di Versioni”# Usando tofuenv (come tfenv per OpenTofu)
git clone https://github.com/tofuutils/tofuenv.git ~/.tofuenv
echo 'export PATH="$HOME/.tofuenv/bin:$PATH"' >> ~/.bashrc
# Elenca versioni disponibili
tofuenv list-remote
# Installa versione specifica
tofuenv install 1.8.0
tofuenv use 1.8.0
# Fissa la versione nel progetto
echo "1.8.0" > .opentofu-version
Comandi Principali
Sezione intitolata “Comandi Principali”Comandi del Workflow
Sezione intitolata “Comandi del Workflow”| Comando | Descrizione |
|---|---|
tofu init | Inizializza la directory di lavoro e scarica i provider |
tofu init -upgrade | Re-inizializza e aggiorna le versioni di provider/moduli |
tofu init -backend-config=prod.hcl | Inizializza con file di configurazione backend |
tofu plan | Anteprima delle modifiche all’infrastruttura |
tofu plan -out=plan.tfplan | Salva il plan su file per applicazione successiva |
tofu plan -target=aws_instance.web | Pianifica modifiche solo per una risorsa specifica |
tofu plan -var-file=prod.tfvars | Pianifica con file di variabili specifico |
tofu apply | Applica le modifiche all’infrastruttura (chiede conferma) |
tofu apply -auto-approve | Applica le modifiche senza conferma |
tofu apply plan.tfplan | Applica un file di plan salvato |
tofu destroy | Distruggi tutta l’infrastruttura gestita |
tofu destroy -auto-approve | Distruggi senza conferma |
tofu destroy -target=aws_instance.web | Distruggi solo una risorsa specifica |
Comandi di Ispezione
Sezione intitolata “Comandi di Ispezione”| Comando | Descrizione |
|---|---|
tofu validate | Valida la sintassi della configurazione |
tofu fmt | Formatta i file di configurazione |
tofu fmt -check | Controlla se i file sono formattati (adatto per CI) |
tofu fmt -recursive | Formatta tutti i file nelle sottodirectory |
tofu output | Visualizza tutti i valori di output |
tofu output -json | Visualizza gli output in formato JSON |
tofu output db_password | Visualizza un valore di output specifico |
tofu show | Mostra lo stato corrente in formato leggibile |
tofu show -json | Mostra lo stato in formato JSON |
tofu show plan.tfplan | Mostra i dettagli del plan salvato |
tofu graph | Genera il grafico delle dipendenze delle risorse (formato DOT) |
tofu graph | dot -Tpng > graph.png | Renderizza il grafico delle dipendenze come PNG |
tofu refresh | Riconcilia lo stato con l’infrastruttura reale |
tofu providers | Elenca i provider usati nella configurazione |
Configurazione (HCL)
Sezione intitolata “Configurazione (HCL)”Configurazione del Provider
Sezione intitolata “Configurazione del Provider”# Blocco dei provider richiesti
terraform {
required_version = ">= 1.6.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
random = {
source = "hashicorp/random"
version = "~> 3.5"
}
}
# Crittografia dello stato (funzionalita esclusiva di OpenTofu)
encryption {
method "aes_gcm" "default" {
keys = key_provider.pbkdf2.mykey
}
state {
method = method.aes_gcm.default
enforced = true
}
plan {
method = method.aes_gcm.default
enforced = true
}
key_provider "pbkdf2" "mykey" {
passphrase = var.state_passphrase
}
}
}
# Provider con configurazione predefinita
provider "aws" {
region = "us-east-1"
default_tags {
tags = {
ManagedBy = "opentofu"
Environment = var.environment
Project = var.project_name
}
}
}
# Alias del provider per multi-regione
provider "aws" {
alias = "west"
region = "us-west-2"
}
# Usa il provider con alias su una risorsa
resource "aws_instance" "west_server" {
provider = aws.west
ami = "ami-0abcdef1234567890"
instance_type = "t3.micro"
}
Definizioni delle Risorse
Sezione intitolata “Definizioni delle Risorse”# Risorsa base
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
tags = {
Name = "${var.project_name}-web"
}
}
# Data source (leggi risorse esistenti)
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}
# Valori locali
locals {
common_tags = {
Project = var.project_name
Environment = var.environment
ManagedBy = "opentofu"
}
az_count = length(data.aws_availability_zones.available.names)
}
# Count e for_each
resource "aws_subnet" "public" {
count = local.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]
tags = merge(local.common_tags, {
Name = "${var.project_name}-public-${count.index}"
Type = "public"
})
}
resource "aws_security_group_rule" "ingress" {
for_each = {
http = { port = 80, cidrs = ["0.0.0.0/0"] }
https = { port = 443, cidrs = ["0.0.0.0/0"] }
ssh = { port = 22, cidrs = [var.admin_cidr] }
}
type = "ingress"
from_port = each.value.port
to_port = each.value.port
protocol = "tcp"
cidr_blocks = each.value.cidrs
security_group_id = aws_security_group.main.id
}
Variabili e Output
Sezione intitolata “Variabili e Output”Definizioni delle Variabili
Sezione intitolata “Definizioni delle Variabili”| Comando | Descrizione |
|---|---|
variable "name" { type = string } | Definisci una variabile stringa |
variable "port" { type = number default = 8080 } | Variabile con valore predefinito |
variable "tags" { type = map(string) } | Variabile mappa |
variable "cidrs" { type = list(string) } | Variabile lista |
variable "config" { type = object({ ... }) } | Variabile oggetto con struttura |
variable "db_pass" { sensitive = true } | Segna la variabile come sensibile (nascosta nell’output) |
variable "env" { validation { ... } } | Variabile con validazione personalizzata |
Passaggio delle Variabili
Sezione intitolata “Passaggio delle Variabili”| Comando | Descrizione |
|---|---|
tofu plan -var="name=value" | Passa variabile via CLI |
tofu plan -var-file="prod.tfvars" | Passa variabili da file |
TF_VAR_name=value tofu plan | Passa via variabile d’ambiente |
Crea il file terraform.tfvars | Valori delle variabili caricati automaticamente |
Crea il file *.auto.tfvars | File di variabili caricati automaticamente |
Esempi di Variabili e Output
Sezione intitolata “Esempi di Variabili e Output”# variables.tf
variable "environment" {
type = string
description = "Deployment environment"
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}
variable "instance_config" {
type = object({
instance_type = string
volume_size = number
encrypted = bool
})
default = {
instance_type = "t3.micro"
volume_size = 20
encrypted = true
}
}
variable "database_password" {
type = string
sensitive = true
}
# outputs.tf
output "vpc_id" {
description = "The ID of the VPC"
value = aws_vpc.main.id
}
output "public_subnet_ids" {
description = "List of public subnet IDs"
value = aws_subnet.public[*].id
}
output "web_url" {
description = "URL of the web server"
value = "https://${aws_lb.web.dns_name}"
}
output "db_connection_string" {
description = "Database connection string"
value = "postgresql://${var.db_username}:${var.database_password}@${aws_db_instance.main.endpoint}/${var.db_name}"
sensitive = true
}
File di Variabili
Sezione intitolata “File di Variabili”# prod.tfvars
environment = "prod"
instance_config = {
instance_type = "t3.large"
volume_size = 100
encrypted = true
}
# dev.tfvars
environment = "dev"
instance_config = {
instance_type = "t3.micro"
volume_size = 20
encrypted = false
}
Gestione dello Stato
Sezione intitolata “Gestione dello Stato”Comandi per lo Stato
Sezione intitolata “Comandi per lo Stato”| Comando | Descrizione |
|---|---|
tofu state list | Elenca tutte le risorse nello stato |
tofu state show aws_instance.web | Mostra dettagli di una risorsa specifica |
tofu state mv aws_instance.old aws_instance.new | Rinomina una risorsa nello stato |
tofu state rm aws_instance.web | Rimuovi una risorsa dallo stato (mantiene l’infrastruttura reale) |
tofu state pull | Recupera lo stato remoto su stdout |
tofu state push | Invia lo stato locale al backend remoto |
tofu import aws_instance.web i-1234567 | Importa una risorsa esistente nello stato |
tofu state replace-provider hashicorp/aws registry.opentofu.org/hashicorp/aws | Sostituisci provider nello stato |
tofu force-unlock LOCK_ID | Rilascia un lock bloccato sullo stato |
Configurazione del Backend
Sezione intitolata “Configurazione del Backend”# Backend S3 (il piu comune)
terraform {
backend "s3" {
bucket = "my-tofu-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "tofu-locks"
encrypt = true
}
}
# Backend PostgreSQL
terraform {
backend "pg" {
conn_str = "postgres://user:pass@db.example.com/tofu_state?sslmode=require"
}
}
# Backend HTTP (funziona con GitLab, alternative a Terraform Cloud)
terraform {
backend "http" {
address = "https://gitlab.example.com/api/v4/projects/1/terraform/state/prod"
lock_address = "https://gitlab.example.com/api/v4/projects/1/terraform/state/prod/lock"
unlock_address = "https://gitlab.example.com/api/v4/projects/1/terraform/state/prod/lock"
username = "gitlab-ci-token"
password = var.ci_token
}
}
# Backend locale (predefinito, per sviluppo)
terraform {
backend "local" {
path = "terraform.tfstate"
}
}
Crittografia dello Stato (Esclusiva OpenTofu)
Sezione intitolata “Crittografia dello Stato (Esclusiva OpenTofu)”# Crittografia dello stato con AWS KMS
terraform {
encryption {
key_provider "aws_kms" "main" {
kms_key_id = "arn:aws:kms:us-east-1:123456789:key/abcd-1234"
region = "us-east-1"
key_spec = "AES_256"
}
method "aes_gcm" "default" {
keys = key_provider.aws_kms.main
}
state {
method = method.aes_gcm.default
enforced = true
}
}
}
Utilizzo dei Moduli
Sezione intitolata “Utilizzo dei Moduli”| Comando | Descrizione |
|---|---|
module "vpc" { source = "./modules/vpc" } | Usa un modulo locale |
module "vpc" { source = "github.com/org/repo//modules/vpc" } | Usa un modulo da GitHub |
module "vpc" { source = "registry.opentofu.org/..." } | Usa un modulo dal registry OpenTofu |
tofu get | Scarica e aggiorna i moduli |
tofu get -update | Forza l’aggiornamento di tutti i moduli |
Struttura del Modulo
Sezione intitolata “Struttura del Modulo”modules/
└── vpc/
├── main.tf # Risorse
├── variables.tf # Variabili di input
├── outputs.tf # Valori di output
├── versions.tf # Requisiti del provider
└── README.md # Documentazione d'uso
Esempio di Modulo
Sezione intitolata “Esempio di Modulo”# modules/vpc/main.tf
resource "aws_vpc" "this" {
cidr_block = var.cidr
enable_dns_support = true
enable_dns_hostnames = true
tags = merge(var.tags, { Name = var.name })
}
resource "aws_subnet" "public" {
count = length(var.public_subnets)
vpc_id = aws_vpc.this.id
cidr_block = var.public_subnets[count.index]
availability_zone = var.azs[count.index]
map_public_ip_on_launch = true
tags = merge(var.tags, {
Name = "${var.name}-public-${count.index}"
})
}
# modules/vpc/variables.tf
variable "name" { type = string }
variable "cidr" { type = string }
variable "azs" { type = list(string) }
variable "public_subnets" { type = list(string) }
variable "tags" { type = map(string) default = {} }
# modules/vpc/outputs.tf
output "vpc_id" { value = aws_vpc.this.id }
output "public_subnet_ids" { value = aws_subnet.public[*].id }
# Utilizzo del modulo
module "vpc" {
source = "./modules/vpc"
name = "production"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b", "us-east-1c"]
public_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
tags = local.common_tags
}
Migrazione da Terraform
Sezione intitolata “Migrazione da Terraform”Compatibilita Drop-In
Sezione intitolata “Compatibilita Drop-In”| Comando | Descrizione |
|---|---|
Sostituisci terraform con tofu nei comandi | I comandi CLI sono compatibili drop-in |
tofu init -upgrade | Re-inizializza con i provider OpenTofu |
Mantieni i file .tf esistenti invariati | La sintassi HCL e completamente compatibile |
Mantieni i file .tfstate esistenti | Il formato dello stato e compatibile |
Mantieni i file .terraform.lock.hcl esistenti | Il file di lock e compatibile |
Aggiorna CI/CD per usare l’azione opentofu/setup-opentofu | Cambia GitHub Actions |
Checklist di Migrazione
Sezione intitolata “Checklist di Migrazione”Migrazione da Terraform a OpenTofu:
1. Installa OpenTofu accanto a Terraform
2. Nella directory del tuo progetto, esegui: tofu init
3. Esegui: tofu plan
- Confronta l'output con: terraform plan
- Dovrebbe essere identico (nessuna modifica)
4. Sostituisci 'terraform' con 'tofu' nelle pipeline CI/CD
5. Aggiorna GitHub Actions:
- Vecchio: hashicorp/setup-terraform
- Nuovo: opentofu/setup-opentofu
6. Aggiorna le sorgenti dei provider nel file di lock (se necessario):
- tofu providers lock
7. Abilita la crittografia dello stato (esclusiva OpenTofu):
- Aggiungi il blocco encryption al blocco terraform {}
8. Cambia i riferimenti al registry dei moduli:
- registry.terraform.io → registry.opentofu.org
- (Entrambi funzionano ancora, ma il registry OpenTofu e preferito)
GitHub Actions CI/CD
Sezione intitolata “GitHub Actions CI/CD”name: OpenTofu Plan & Apply
on:
push:
branches: [main]
pull_request:
jobs:
plan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: opentofu/setup-opentofu@v1
with:
tofu_version: 1.8.0
- name: Init
run: tofu init
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Plan
run: tofu plan -out=plan.tfplan -no-color
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Apply (main only)
if: github.ref == 'refs/heads/main'
run: tofu apply -auto-approve plan.tfplan
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
Funzionalita Esclusive di OpenTofu
Sezione intitolata “Funzionalita Esclusive di OpenTofu”Crittografia dello Stato
Sezione intitolata “Crittografia dello Stato”Crittografa i file di stato a riposo — non disponibile in Terraform. Supporta provider di chiavi PBKDF2, AWS KMS, GCP KMS e Azure Key Vault.
Crittografia dello Stato Lato Client
Sezione intitolata “Crittografia dello Stato Lato Client”A differenza della crittografia lato server di Terraform Cloud, OpenTofu crittografa lo stato prima che lasci la tua macchina, assicurando che nessuna terza parte possa leggere lo stato della tua infrastruttura.
Indipendenza del Registry
Sezione intitolata “Indipendenza del Registry”OpenTofu mantiene il proprio registry di provider e moduli su registry.opentofu.org, garantendo la disponibilita a lungo termine indipendentemente dalle decisioni di licenza di HashiCorp.
Buone Pratiche
Sezione intitolata “Buone Pratiche”-
Usa stato remoto con locking — configura un backend S3 + DynamoDB (o equivalente) per prevenire modifiche concorrenti e abilitare la collaborazione in team.
-
Abilita la crittografia dello stato — usa la funzionalita esclusiva di crittografia di OpenTofu con KMS o PBKDF2 per proteggere i dati sensibili nei file di stato.
-
Fissa le versioni dei provider e dei moduli — usa i vincoli
~>(es.~> 5.0) per consentire aggiornamenti patch prevenendo modifiche che rompono la compatibilita. -
Usa workspace o separazione per directory per gli ambienti — mantieni lo stato di dev, staging e produzione separato per prevenire modifiche accidentali tra ambienti.
-
Scrivi moduli per pattern riutilizzabili — estrai pattern comuni (VPC, database, cluster Kubernetes) in moduli con input e output chiari.
-
Valida le variabili con vincoli — aggiungi blocchi
validationalle variabili per catturare valori non validi prima che raggiungano il provider cloud. -
Salva i plan prima di applicarli — usa
tofu plan -out=plan.tfplanpoitofu apply plan.tfplanper assicurarti di applicare esattamente cio che hai revisionato. -
Usa
default_tagssui provider — imposta tag a livello organizzativo (ManagedBy, Environment, Project) a livello di provider per garantire un tagging coerente. -
Importa risorse esistenti — usa
tofu importper portare l’infrastruttura non gestita sotto il controllo di OpenTofu piuttosto che ricrearla. -
Esegui
tofu fmt -checkin CI — imponi la formattazione coerente tra tutti i membri del team facendo fallire le build CI su file non formattati. -
Usa i data source per i riferimenti esterni — fai riferimento all’infrastruttura esistente (AMI, VPC, zone DNS) con blocchi
datainvece di codificare gli ID. -
Revisiona i plan con attenzione — leggi sempre l’output del plan prima di applicare. Presta attenzione alle azioni
destroyereplaceche potrebbero causare downtime.