Sägetstrasse 18, 3123 Belp, Switzerland +41 79 173 36 84 info@ict.technology

      Terraform @ Scale - Parte 1c: Implementazione pratica dei flussi di dati con Remote State

      Attraverso una combinazione di Remote Backends strutturati con cura, una progettazione attenta degli output e l'uso mirato della Data Source terraform_remote_state, è possibile stabilire un flusso di informazioni controllato tra diversi livelli tenant - il tutto senza compromettere l'isolamento dei singoli mandanti.

      L'uso efficace del Remote State per lo scambio di informazioni tra unità organizzative richiede una configurazione ponderata dell'ambiente Terraform. Elemento centrale è la scelta e la configurazione di uno Storage Backend adeguato per la memorizzazione dei dati di stato nei cosiddetti State Files.

      Configurazione di un Remote Backend

      Terraform offre diversi backends ufficiali per la memorizzazione dei dati di stato. Le opzioni ufficialmente supportate da HashiCorp sono:

       Esempio di configurazione
       HashiCorp Consul
       terraform {
        backend "consul" {
          address = "consul.example.com:8500"
          scheme  = "https"
          path    = "terraform/network/global"
          lock    = true
        }
      }
       HashiCorp Terraform Enterprise
       terraform {
        backend "remote" {
          hostname     = "terraform.example.com"
          organization = "ict-technology"
          
          workspaces {
            name = "network-global"
          }
        }
      }
        HashiCorp Terraform Cloud
       terraform {
        backend "remote" {
          hostname     = "app.terraform.io"
          organization = "ict-technology"
          
          workspaces {
            name = "network-global"
          }
        }
      }

       

       Questi Storage Backends offrono diversi vantaggi rispetto ai file di stato locali:

      • Memorizzazione centralizzata: Tutti i membri del team lavorano con lo stesso stato.
      • Meccanismi di locking: Impedisce modifiche parallele e i conflitti correlati.
      • Versionamento: Memorizza lo storico delle modifiche.
      • Controllo degli accessi: Consente concetti di autorizzazione differenziati.

      Nella scelta di un backend, dovreste considerare i requisiti della vostra organizzazione.

      Consul è un'ottima scelta per le aziende che utilizzano la versione gratuita di Terraform. 

      Terraform Cloud e Terraform Enterprise invece adottano un'architettura client/server e dispongono già di uno Storage Backend integrato lato server. Pertanto, non è necessario preoccuparsi ulteriormente di questo aspetto e si può semplicemente utilizzare lo storage integrato. Terraform Cloud/Enterprise offrono inoltre funzionalità aggiuntive come Policy-as-Code per l'implementazione e l'applicazione di politiche, controlli di governance e funzionalità avanzate di collaborazione.

      Esistono anche altri backends supportati da Terraform. Tra questi figurano backends come Amazon S3, HTTPS, diverse banche dati e altri ancora.
      Tuttavia, c'è un aspetto critico da considerare: HashiCorp fornisce supporto solo per i file di stato locali, Consul e Terraform Cloud/Enterprise.
      Potete utilizzare altri backends, ma in caso di problemi non potete aspettarvi alcun supporto o soluzione da HashiCorp, nemmeno con un contratto di supporto Enterprise esistente.
      In ambienti di produzione critici, dovreste quindi utilizzare esclusivamente Consul o Terraform Cloud/Enterprise come Remote State Backends.

      Se utilizzate un backend non supportato da HashiCorp, dovreste assolutamente assicurarvi che il backend offra un meccanismo di locking!
      Altrimenti rischiate che, in caso di esecuzione parallela di un comando terraform apply, le istanze Terraform eseguite in parallelo sovrascrivano reciprocamente i file di stato, rendendo così l'intera infrastruttura incoerente. Ad esempio, backends come una condivisione NFSv3 o un backend HTTPS sono probabilmente scelte discutibili e rischiate perdite di dati. 

       

      Meccanismi di sicurezza

      La memorizzazione dei file di stato è critica, poiché questi possono contenere informazioni sensibili come credenziali di accesso e dettagli sull'infrastruttura.

      Cifratura 

      In Terraform Cloud/Enterprise i dati di stato vengono automaticamente cifrati sia a riposo che durante la trasmissione. In Consul dovreste abilitare TLS per la connessione e le funzionalità di cifratura integrate di Consul:


      terraform {
        backend "consul" {
          address      = "consul.example.com:8500"
          access_token = var.consul_acl_token  
          scheme       = "https"
          path         = "terraform/network/global"
          ca_file      = "/etc/ssl/certs/ca.pem"
          cert_file    = "/etc/ssl/certs/cert.pem"
          key_file     = "/etc/ssl/private/key.pem"   
        }
      }

       Controllo degli accessi

      In Terraform Cloud/Enterprise la gestione degli utenti offre ruoli e permessi dettagliati. Ad esempio, ruoli differenti possono avere diversi privilegi:

      • Team Network-Admins: Accesso completo al workspace network-global.
      • Team App-Developers: Accesso in sola lettura al workspace network-global.

      In Consul si utilizzano le ACL, che possono essere assegnate a un ruolo. Ecco, ad esempio, l'ACL che concede al team App-Developers il permesso di sola lettura:


      # Consul ACL policy
      key_prefix "terraform/network/global" {
        policy = "read"
      }

      In ambienti produttivi dovreste inoltre utilizzare un token ACL per impedire l'accesso non autorizzato da parte di altri client:


       data "terraform_remote_state" "global_network" {
        backend = "consul"
        config = {
          address = "consul.example.com:8500"
          path    = "terraform/network/global"
          scheme  = "https"
          token   = var.consul_acl_token
        }
      }
      

      Versionamento e ripristino

      Terraform Cloud/Enterprise crea automaticamente snapshot dei vostri state files dopo ogni esecuzione riuscita. Questi snapshot possono essere utilizzati per ripristinare lo stato di un ambiente in caso di errore. Utilizzate l'interfaccia utente o la CLI (terraform state pull) per visualizzare la cronologia delle versioni e ripristinare la versione desiderata.

      In Consul è possibile configurare cluster ad alta disponibilità con repliche in altri data center per prevenire perdite di dati. È inoltre possibile creare snapshot dello storage Raft.

       

      Struttura dei file di stato per una gestione ottimale del Multi-Tenancy

      L'organizzazione degli state files dovrebbe riflettere la vostra struttura organizzativa e al tempo stesso supportare i flussi informativi.

      Datacenter View IT

      In Terraform Cloud/Enterprise le diverse configurazioni sono organizzate in "Workspaces". In Consul la separazione avviene tramite "Paths", che corrispondono a uno spazio dei nomi logico.

      Una struttura consolidata potrebbe apparire così:

      InfrastrutturaWorkspace/Path
       Sistemi e servizi globali necessari  global-infrastructure
      Sedi regionali region-{region_name}
      Infrastruttura specifica per cliente customer-{customer_name}
      Infrastruttura specifica per team team-{team_name}
      Infrastruttura specifica per applicazione app-{app_name}

       

      Una struttura di questo tipo consente una chiara separazione delle responsabilità e facilita il ritrovamento degli state files rilevanti.

      In Terraform Cloud/Enterprise potete inoltre utilizzare il Workspace-Tagging per raggruppare workspaces correlati. Questo vi consente, ad esempio, di stabilire una connessione logica tra l'infrastruttura di un'applicazione e quella del team associato.

       

      Esempi di codice per l'ereditarietà dei dati tra tenant

      L'effettiva ereditarietà dei dati avviene tramite la Data Source terraform_remote_state, che consente l'accesso agli Outputs da altre configurazioni Terraform.

      Esempio: Configurazione di rete in un ambiente Multi-Tenant

      Team di rete globale (nel Workspace global-infrastructure):


      module "global_network" {
        source = "./modules/network"
      
        name            = "global-vcn"
        description     = "Global customer network"
        cidr_block      = "10.0.0.0/16"
        parent_id       = var.root_compartment_id
      }
      
      output "global_vcn_id" {
        value = module.global_network.id
      }
      

      Team regionale (nel Workspace region-dc1):


      data "terraform_remote_state" "global_network" {
        backend = "remote"
        config = {
          organization = "ict-technology"
          workspaces = {
            name = "network-global"
          }
        }
      }
      
      module "regional_network" {
        source = "./modules/network"
      
        name            = "regional-subnet-dc1"
        description     = "Subnet of europe region"
        parent_id       = data.terraform_remote_state.global_network.outputs.global_vcn_id
        cidr_block      = "10.0.1.0/24"
      }
      
      output "regional_subnet_id" {
        value = module.regional_network.id
      }

       Team cliente (nel Workspace customer-acme):


      data "terraform_remote_state" "regional_network" {
        backend = "remote"
        config = {
          organization = "ict-technology"
          workspaces = {
            name = "region-europe"
          }
        }
      }
      
      module "customer_environment" {
        source = "./modules/environment"
      
        name            = "customer-instance"
        description     = "customer instance"
        parent_id       = var.customer_compartment_id
        subnet_id       = data.terraform_remote_state.regional_network.outputs.regional_subnet_id
        instance_shape  = "VM.Standard2.1"
      }
      

       Un modello particolarmente raccomandato è l'uso di Outputs strutturati. Invece di fornire molti valori singoli, un Output strutturato può esportare diverse informazioni raggruppate:


      output "network_config" {
        value = {
          vcn_id     = module.global_network.id
          cidr_block = module.global_network.cidr_block
          dns_label  = module.global_network.dns_label
          subnets    = {
            public  = module.subnet_public.id
            private = module.subnet_private.id
          }
        }
      }
      

      In questo modo una configurazione dipendente può accedere in modo mirato alle informazioni rilevanti:


      module "app_server" {
        source = "./modules/compute"
      
        name            = "app-server"
        description     = "application server in private subnet"
        parent_id       = var.customer_compartment_id
        subnet_id       = data.terraform_remote_state.network.outputs.network_config.subnets.private
        instance_shape  = "VM.Standard2.1"
      }
      
      

        Nel prossimo capitolo esamineremo le insidie, le best practices e alcuni trucchi per la gestione dei Remote States.