Provisioning e configurazione di sottogruppi e progetti con impostazioni
standard (e variabili CI/CD opzionali) su GitLab (SaaS / gitlab.com) tramite il
provider ufficiale
gitlabhq/gitlab.
Membri e permessi NON sono gestiti da Terraform: l'accesso e' gestito tramite SAML Group Links. Tenerli fuori da Terraform evita conflitti (drift) tra i due sistemi.
L'infrastruttura e' descritta come codice (IaC): modifichi i file .tf, lanci
terraform plan per vedere cosa cambierebbe e terraform apply per applicarlo.
GitLab/
├── versions.tf # Versioni richieste di Terraform e del provider
├── providers.tf # Configurazione del provider GitLab (auth via env)
├── backend.tf # Stato: locale ora, pronto a migrare su GitLab
├── variables.tf # Variabili di input della root (es. gruppo radice)
├── main.tf # Lettura del gruppo radice esistente (data source)
├── inventory.tf # CATALOGO (committato): elenco di sottogruppi/progetti
├── subgroups.tf # Crea i sottogruppi ciclando sull'inventario (for_each)
├── projects.tf # Crea i progetti ciclando sull'inventario (for_each)
├── outputs.tf # Output utili dopo l'apply (URL, id, ...)
├── terraform.tfvars.example # Esempio di valori -> copialo in terraform.tfvars
├── .gitignore # Esclude stato, segreti, .terraform/, ...
│
└── modules/ # Moduli riutilizzabili (gli "stampi" standard)
├── subgroup/ # crea un sottogruppo (+ variabili di gruppo opzionali)
│ ├── versions.tf
│ ├── variables.tf
│ ├── main.tf
│ └── outputs.tf
└── project/ # crea un progetto (+ variabili di progetto opzionali)
├── versions.tf
├── variables.tf
├── main.tf
└── outputs.tf
Perche' questa struttura?
- Inventario (
inventory.tf): l'elenco di cosa creare, espresso come dati (le mappesubgroupseprojects). E' qui che aggiungi una voce per creare un nuovo sottogruppo/progetto. E' versionato in git: ogni modifica passa da una MR. - Root (gli altri
.tf): il "motore".subgroups.tf/projects.tfciclano confor_eachsull'inventario e richiamano i moduli; non li tocchi quasi mai. Terraform unisce automaticamente tutti i.tfdella stessa cartella. - Moduli (
modules/): "stampi" riutilizzabili che descrivono come si crea un sottogruppo o un progetto, con i default di sicurezza (es. visibilita' privata). Lo standard si scrive una volta e vale per tutti.
Apri la shell della tua WSL (OracleLinux_9_5) ed esegui:
sudo dnf install -y dnf-plugins-core
sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
sudo dnf -y install terraform
terraform -versionIn alternativa puoi usare OpenTofu (fork open source, compatibile): i comandi diventano
tofu init,tofu plan, ecc. Il codice di questo progetto funziona con entrambi.
- Su GitLab: User Settings → Access Tokens (Personal Access Token) — oppure usa un Service Account dedicato (consigliato per l'automazione).
- Scope:
api. - Il token deve appartenere a un utente con ruolo Owner o Maintainer sul gruppo radice (serve per creare sottogruppi/progetti).
- Copia il token (
glpat-...): lo vedrai una sola volta.
Nota: i Project/Group Access Token hanno permessi piu' limitati e possono dare errori su alcune risorse. Per iniziare, un Personal Access Token e' la scelta piu' semplice.
# 1) Entra nella cartella del progetto
cd ~/PlatformTeam/Terraform/GitLab
# 2) Crea il tuo file di variabili a partire dall'esempio
cp terraform.tfvars.example terraform.tfvars
# poi modifica parent_group_path con il full path del tuo gruppo radice
# 3) Esporta il token (NON va nei file .tf!)
export GITLAB_TOKEN="glpat-xxxxxxxxxxxxxxxxxxxx"Suggerimento: l'
exportvale solo per la sessione corrente del terminale. Per non riscriverlo ogni volta puoi usare uno strumento comedirenvcon un file.envrc(da NON committare), oppure rilanciarlo ad ogni sessione.
terraform fmt # formatta i file .tf in modo canonico
terraform init # scarica il provider e inizializza la cartella (la prima volta)
terraform validate # controlla che la configurazione sia sintatticamente valida
terraform plan # mostra COSA verrebbe creato/modificato/distrutto (nessuna modifica)
terraform apply # applica le modifiche (chiede conferma: scrivi "yes")
terraform destroy # distrugge tutto cio' che e' gestito da questo stato (attenzione!)Flusso tipico: fmt → init (la prima volta) → validate → plan → apply.
Parti sempre da plan: e' una "anteprima" sicura, non tocca nulla su GitLab.
Tutto ruota attorno a inventory.tf: e' l'unico file che modifichi per creare o
rimuovere sottogruppi e progetti. subgroups.tf e projects.tf ci ciclano sopra e
non vanno toccati.
In inventory.tf, aggiungi una voce alla mappa subgroups (la chiave e' il path):
locals {
subgroups = {
"terraform-sandbox" = { name = "Terraform Sandbox" }
"team-backend" = { name = "Team Backend" } # <- nuovo
}
}Aggiungi una voce alla mappa projects. Il campo subgroup indica in quale
sottogruppo crearlo (= una chiave di subgroups):
locals {
projects = {
"hello-terraform" = { name = "Hello Terraform", subgroup = "terraform-sandbox" }
"billing" = { name = "Billing", subgroup = "team-backend" } # <- nuovo
}
}Poi terraform plan (anteprima) e terraform apply. Nessun nuovo file, nessun nuovo
blocco: solo una riga di dati.
Non si gestiscono qui: l'accesso e' gestito tramite SAML Group Links, fuori da Terraform (vedi la nota all'inizio del README).
I moduli accettano una mappa ci_variables (default: vuota). Quando vorrai gestirle
dai dati, si aggiunge il campo nell'inventario e lo si inoltra al modulo. Forma:
ci_variables = {
"APP_ENV" = { value = "production" }
"DEPLOY_TOKEN" = { value = "...", masked = true, protected = true }
}Ora lo stato e' un file locale (terraform.tfstate). Per passare allo stato gestito
da GitLab (lock automatico, condivisibile in team), senza perdere nulla:
-
Crea (o scegli) un progetto GitLab che faccia da contenitore dello stato e prendi nota del suo Project ID (lo trovi nella home del progetto).
-
Scommenta il blocco
backend "http"inbackend.tf. -
Crea un file
backend.hcl(gia' nel.gitignore) con gli indirizzi:address = "https://gitlab.com/api/v4/projects/<PROJECT_ID>/terraform/state/platform" lock_address = "https://gitlab.com/api/v4/projects/<PROJECT_ID>/terraform/state/platform/lock" unlock_address = "https://gitlab.com/api/v4/projects/<PROJECT_ID>/terraform/state/platform/lock" username = "<il-tuo-username-gitlab>"
(
platforme' il nome dello stato: puoi sceglierlo.) -
Passa il token come password del backend ed esegui la migrazione:
export TF_HTTP_PASSWORD="$GITLAB_TOKEN" terraform init -migrate-state -backend-config=backend.hcl
Terraform copiera' lo stato locale su GitLab. Da quel momento puoi cancellare i file
terraform.tfstate*locali.
- Niente segreti nel codice. Token via
GITLAB_TOKEN, valori sensibili fuori dai.tf. Lo stato e i*.tfvarssono nel.gitignore. - Versioni bloccate.
versions.tffissa Terraform>= 1.5e il provider~> 19.0. Committa.terraform.lock.hcl(NON e' ignorato) per run riproducibili. - Moduli riutilizzabili. Logica di creazione in
modules/, configurazione nella root: meno duplicazione, piu' coerenza. for_eachinvece dicount. L'inventario e le variabili usano mappe con chiavi stabili: aggiungere/rimuovere un elemento non "sposta" gli altri nello stato.- Validazioni di input. La visibilita' e' validata: errori chiari prima di chiamare le API.
- Separazione dei compiti. Terraform gestisce la struttura; i membri li gestisce SAML Group Links. Mai due sistemi sulla stessa cosa.
- Workflow
planprima diapply. Rivedi sempre il piano prima di applicare.
| Problema | Causa probabile / Soluzione |
|---|---|
401 Unauthorized |
GITLAB_TOKEN mancante/scaduto o senza scope api. |
403 Forbidden su creazione |
L'utente del token non e' Owner/Maintainer sul gruppo radice. |
404 sul data "gitlab_group" |
parent_group_path errato: usa il full path, non il nome. |
Errore sul default_branch |
L'istanza usa un branch iniziale diverso da main. Imposta default_branch di conseguenza o lascia initialize_with_readme = true. |
409 Conflict / risorsa gia' esistente |
Il sottogruppo/progetto esiste gia': importalo con terraform import invece di ricrearlo. |
- Provider GitLab: https://registry.terraform.io/providers/gitlabhq/gitlab/latest/docs
- Stato remoto su GitLab: https://docs.gitlab.com/ee/user/infrastructure/iac/terraform_state.html