Terraform nel 2026: Novita e Best Practices

Terraform di HashiCorp rimane lo strumento di Infrastructure as Code (IaC) piu utilizzato al mondo. Con oltre il 70% di market share nel settore, e diventato uno standard de facto per gestire infrastrutture cloud. Nel 2026, Terraform ha raggiunto una maturita notevole con nuove funzionalita che semplificano la gestione di infrastrutture complesse.

Novita Terraform 1.7/1.8

Removed Block (1.7)

Finalmente un modo pulito per rimuovere risorse dallo state senza distruggerle:

# Rimuove la risorsa dallo state senza eliminarla nel cloud
removed {
  from = aws_instance.legacy_server

  lifecycle {
    destroy = false
  }
}

Prima dovevi usare terraform state rm, ora e dichiarativo e versionabile.

Import Block Migliorato (1.7+)

Import risorse esistenti direttamente nel codice:

import {
  to = aws_instance.web_server
  id = "i-0123456789abcdef0"
}

resource "aws_instance" "web_server" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"

  tags = {
    Name = "WebServer"
  }
}
# Genera configurazione automatica
terraform plan -generate-config-out=generated.tf

Test Framework Nativo (1.6+)

Testing integrato nel linguaggio:

# tests/vpc.tftest.hcl
run "create_vpc" {
  command = apply

  assert {
    condition     = aws_vpc.main.cidr_block == "10.0.0.0/16"
    error_message = "VPC CIDR block is incorrect"
  }
}

run "check_subnets" {
  command = plan

  assert {
    condition     = length(aws_subnet.private) == 3
    error_message = "Expected 3 private subnets"
  }
}
terraform test

Provider-Defined Functions (1.8)

I provider possono ora definire funzioni custom:

# Funzione definita dal provider AWS
locals {
  arn_parts = provider::aws::arn_parse(aws_s3_bucket.main.arn)
  account_id = local.arn_parts.account
}

Struttura Progetti Enterprise

Layout Consigliato

terraform/
├── modules/                    # Moduli riutilizzabili
│   ├── networking/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   ├── outputs.tf
│   │   └── README.md
│   ├── compute/
│   ├── database/
│   └── monitoring/
├── environments/              # Configurazioni per ambiente
│   ├── dev/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   ├── terraform.tfvars
│   │   └── backend.tf
│   ├── staging/
│   └── production/
├── tests/                     # Test Terraform
│   ├── networking.tftest.hcl
│   └── compute.tftest.hcl
└── .github/
    └── workflows/
        └── terraform.yml

Moduli Riutilizzabili

modules/networking/main.tf:

resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = merge(var.tags, {
    Name = "${var.environment}-vpc"
  })
}

resource "aws_subnet" "private" {
  count = length(var.private_subnet_cidrs)

  vpc_id            = aws_vpc.main.id
  cidr_block        = var.private_subnet_cidrs[count.index]
  availability_zone = var.availability_zones[count.index]

  tags = merge(var.tags, {
    Name = "${var.environment}-private-${count.index + 1}"
    Type = "private"
  })
}

resource "aws_subnet" "public" {
  count = length(var.public_subnet_cidrs)

  vpc_id                  = aws_vpc.main.id
  cidr_block              = var.public_subnet_cidrs[count.index]
  availability_zone       = var.availability_zones[count.index]
  map_public_ip_on_launch = true

  tags = merge(var.tags, {
    Name = "${var.environment}-public-${count.index + 1}"
    Type = "public"
  })
}

modules/networking/variables.tf:

variable "environment" {
  description = "Environment name"
  type        = string
}

variable "vpc_cidr" {
  description = "CIDR block for VPC"
  type        = string
  default     = "10.0.0.0/16"
}

variable "private_subnet_cidrs" {
  description = "CIDR blocks for private subnets"
  type        = list(string)
  default     = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
}

variable "public_subnet_cidrs" {
  description = "CIDR blocks for public subnets"
  type        = list(string)
  default     = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
}

variable "availability_zones" {
  description = "Availability zones"
  type        = list(string)
}

variable "tags" {
  description = "Tags to apply to all resources"
  type        = map(string)
  default     = {}
}

Uso dei Moduli

environments/production/main.tf:

module "networking" {
  source = "../../modules/networking"

  environment          = "production"
  vpc_cidr            = "10.0.0.0/16"
  availability_zones  = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]

  private_subnet_cidrs = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
  public_subnet_cidrs  = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]

  tags = {
    Environment = "production"
    ManagedBy   = "terraform"
    Project     = "my-project"
  }
}

module "compute" {
  source = "../../modules/compute"

  environment        = "production"
  vpc_id            = module.networking.vpc_id
  private_subnet_ids = module.networking.private_subnet_ids

  instance_type = "t3.large"
  min_size      = 3
  max_size      = 10
}

State Management Best Practices

Remote State con Locking

backend.tf:

terraform {
  backend "s3" {
    bucket         = "my-company-terraform-state"
    key            = "production/terraform.tfstate"
    region         = "eu-west-1"
    encrypt        = true
    dynamodb_table = "terraform-locks"
  }
}

Setup DynamoDB per locking:

resource "aws_dynamodb_table" "terraform_locks" {
  name         = "terraform-locks"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"

  attribute {
    name = "LockID"
    type = "S"
  }

  tags = {
    Name = "Terraform Lock Table"
  }
}

State Isolation

Separa lo state per ambiente e componente:

s3://terraform-state/
├── networking/
│   ├── dev/terraform.tfstate
│   ├── staging/terraform.tfstate
│   └── production/terraform.tfstate
├── compute/
│   ├── dev/terraform.tfstate
│   └── production/terraform.tfstate
└── database/
    └── production/terraform.tfstate

Data Sources per Cross-State

# Leggi output da altro state
data "terraform_remote_state" "networking" {
  backend = "s3"
  config = {
    bucket = "my-company-terraform-state"
    key    = "networking/production/terraform.tfstate"
    region = "eu-west-1"
  }
}

# Usa i valori
resource "aws_instance" "web" {
  subnet_id = data.terraform_remote_state.networking.outputs.private_subnet_ids[0]
}

Terraform Cloud/Enterprise

Quando Usarlo

ScenarioTerraform OSSTerraform Cloud
Team piccolo (1-3)SiOpzionale
Team medio (4-10)DifficileConsigliato
Enterprise (10+)NoNecessario
Compliance/AuditLimitatoSi
Policy as CodeNoSi (Sentinel)

Setup Terraform Cloud

terraform {
  cloud {
    organization = "my-organization"

    workspaces {
      name = "my-app-production"
    }
  }
}

Sentinel Policies

Policy as Code per governance:

# policies/enforce-tags.sentinel
import "tfplan/v2" as tfplan

mandatory_tags = ["Environment", "Project", "Owner"]

aws_resources = filter tfplan.resource_changes as _, rc {
  rc.provider_name matches "(.*)aws$" and
  rc.mode is "managed" and
  (rc.change.actions contains "create" or rc.change.actions contains "update")
}

tags_contain_mandatory = rule {
  all aws_resources as _, resource {
    all mandatory_tags as tag {
      resource.change.after.tags contains tag
    }
  }
}

main = rule {
  tags_contain_mandatory
}

CI/CD con Terraform

GitHub Actions

.github/workflows/terraform.yml:

name: Terraform

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  TF_VERSION: 1.8.0
  AWS_REGION: eu-west-1

jobs:
  terraform:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: environments/production

    steps:
      - uses: actions/checkout@v4

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: $

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: $
          aws-secret-access-key: $
          aws-region: $

      - name: Terraform Format Check
        run: terraform fmt -check -recursive

      - name: Terraform Init
        run: terraform init

      - name: Terraform Validate
        run: terraform validate

      - name: Terraform Plan
        id: plan
        run: terraform plan -no-color -out=tfplan
        continue-on-error: true

      - name: Terraform Apply
        if: github.ref == 'refs/heads/main' && github.event_name == 'push'
        run: terraform apply -auto-approve tfplan

Security Best Practices

Secrets Management

MAI committare segreti. Usa:

# AWS Secrets Manager
data "aws_secretsmanager_secret_version" "db_password" {
  secret_id = "production/database/password"
}

resource "aws_db_instance" "main" {
  password = data.aws_secretsmanager_secret_version.db_password.secret_string
}

# HashiCorp Vault
data "vault_generic_secret" "db" {
  path = "secret/database"
}

resource "aws_db_instance" "main" {
  password = data.vault_generic_secret.db.data["password"]
}

Least Privilege IAM

# Policy minima per Terraform
data "aws_iam_policy_document" "terraform" {
  statement {
    effect = "Allow"
    actions = [
      "ec2:*",
      "rds:*",
      "s3:*"
    ]
    resources = ["*"]
    condition {
      test     = "StringEquals"
      variable = "aws:RequestedRegion"
      values   = ["eu-west-1"]
    }
  }
}

State Encryption

# S3 backend con encryption
terraform {
  backend "s3" {
    bucket         = "terraform-state"
    key            = "production/terraform.tfstate"
    region         = "eu-west-1"
    encrypt        = true
    kms_key_id     = "alias/terraform-state-key"
    dynamodb_table = "terraform-locks"
  }
}

Performance e Ottimizzazione

Parallelismo

# Aumenta parallelismo (default: 10)
terraform apply -parallelism=20

# Riduci per rate limiting
terraform apply -parallelism=5

Target Specifici

# Apply solo risorse specifiche
terraform apply -target=module.networking
terraform apply -target=aws_instance.web[0]

Refresh Selettivo

# Skip refresh (piu veloce, meno sicuro)
terraform plan -refresh=false

# Refresh solo specifiche risorse
terraform apply -refresh-only -target=aws_instance.web

Checklist Best Practices

Codice

  • Usa moduli per codice riutilizzabile
  • Variabili con tipi e validazione
  • Output documentati
  • README per ogni modulo
  • Formatting consistente (terraform fmt)

State

  • Remote state con locking
  • State isolation per ambiente
  • Encryption at rest
  • Backup automatici

CI/CD

  • Plan su ogni PR
  • Apply solo da main
  • Review obbligatoria
  • Test automatizzati

Security

  • No secrets in code
  • Least privilege IAM
  • State encryption
  • Audit logging

Conclusioni

Terraform nel 2026 e piu maturo e potente che mai. Le novita come il test framework nativo, l'import dichiarativo e le provider-defined functions rendono lo sviluppo IaC piu produttivo.

Raccomandazioni chiave:

  1. Struttura modulare fin dall'inizio
  2. Remote state con locking sempre
  3. CI/CD per ogni ambiente
  4. Test per moduli critici
  5. Terraform Cloud per team > 3 persone

Risorse