Terraform i Microsoft Fabric a la pràctica: un medallion en un sol apply
Demo pas a pas amb vídeo: una capacity, 9 workspaces, 36 grups d'Entra ID i els seus rols a Microsoft Fabric amb Terraform. Plan, apply, drift i destroy.
A l’article anterior vam veure la teoria: què és Infrastructure as Code, com s’escriu un recurs, què és l’estat i per què cal llegir el plan abans de cada apply.
Avui anem per la demo. L’encàrrec: el medallion d’un projecte que es diu «ventas» (vendes). Tres capes (bronze, silver i gold) per tres entorns (dev, test i prod), cada workspace amb els seus quatre grups de seguretat i els seus quatre rols. I la capacity a sota, que tampoc no existeix encara.
A mà serien nou workspaces, 36 grups i 36 assignacions de rols. Amb Terraform és un apply.
Aquest és el vídeo de la demo que vam fer l’Alfonso Ming i jo a la NetCoreConf.
El vídeo no té so: t’explico cada pas aquí sota.
El codi és a github.com/marlizana/FabricAsCode.
Pas 0: no hi ha res
Abans de començar, comprovem que el punt de partida és buit:
- Fabric: filtres la llista de workspaces per «ventas». No en surt cap.
- Azure: a Fabric capacities no hi ha cap capacity del projecte.
- Entra ID: busques els grups
sg-fbc-ventas. Cap.
Pas 1: el codi de Terraform, de dalt a baix
El repo té aquesta pinta:
FabricAsCode/
├── versions.tf # providers i versions
├── main.tf # connecta les peces
├── variables.tf
├── outputs.tf
├── environments/ # un .tfvars per escenari
├── modules/
│ ├── capacity/
│ ├── workspace-with-roles/
│ └── templates/medallion-cicd/
└── scripts/demo.ps1 # dreceres per a la demo
versions.tf: els providers
terraform {
required_providers {
azurerm = { source = "hashicorp/azurerm", version = "~> 4.0" }
azuread = { source = "hashicorp/azuread", version = "~> 3.0" }
fabric = { source = "microsoft/fabric", version = "~> 1.14" }
}
}
provider "azurerm" { features {} }
provider "azuread" {}
provider "fabric" {}
azurerm crea la capacity, azuread els grups i fabric els workspaces i els rols. Els blocs provider van buits expressament: les credencials del service principal arriben per variables d’entorn.
environments/ventas.tfvars: l’encàrrec
capacity_name = "fbcventas"
capacity_sku = "F2"
region = "spaincentral"
template = "medallion-cicd"
project_name = "ventas"
layers = ["bronze", "silver", "gold"]
environments = ["dev", "test", "prod"]
admin_group_members = [
"mar@contoso.onmicrosoft.com",
"alfonso@contoso.onmicrosoft.com",
]
Aquí hi ha tot el que canvia d’un projecte a un altre: la mida de la capacity, la regió, les capes, els entorns i qui entra als grups Admin. Per a un projecte nou es copia aquest fitxer i el codi no es toca.
main.tf: uneix les peces
Crida el mòdul de la capacity i el template medallion-cicd, i li passa al template l’ID de la capacity.
modules/capacity/main.tf: la capacity de Fabric
Un resource group i un azurerm_fabric_capacity amb SKU F2. I un detall que em va costar un plan fallit:
# L'ID d'Azure no és l'ID de Fabric: aquesta data source fa de pont.
data "fabric_capacity" "this" {
display_name = azurerm_fabric_capacity.this.name
}
azurerm retorna l’ID de recurs d’Azure, però un workspace espera el GUID de Fabric. La data source busca la capacity pel nom i retorna l’ID que necessita el workspace.
modules/templates/medallion-cicd/main.tf: les combinacions
locals {
workspace_specs = {
for pair in setproduct(var.layers, var.environments) :
"${pair[0]}-${pair[1]}" => { layer = pair[0], env = pair[1] }
}
}
module "workspace" {
source = "../../workspace-with-roles"
for_each = local.workspace_specs
workspace_name = "${var.project_name}-${each.value.layer}-${each.value.env}" # ventas-bronze-dev
ad_group_prefix = "sg-fbc-${var.project_name}-${each.value.layer}-${each.value.env}" # sg-fbc-ventas-bronze-dev
capacity_id = var.capacity_id
admin_members = var.admin_members
}
setproduct combina les dues llistes i en surten nou parelles capa-entorn. Per a cadascuna es crida el mateix mòdul amb el seu nom i el seu prefix de grups.
modules/workspace-with-roles/main.tf: workspaces, grups i rols
resource "fabric_workspace" "this" {
display_name = var.workspace_name
capacity_id = var.capacity_id
}
resource "azuread_group" "roles" {
for_each = var.roles # Admin, Contributor, Member, Viewer
display_name = "${var.ad_group_prefix}-${lower(each.value)}"
security_enabled = true
members = each.value == "Admin" ? var.admin_members : null
}
resource "fabric_workspace_role_assignment" "this" {
for_each = azuread_group.roles
workspace_id = fabric_workspace.this.id
role = each.key
principal = { id = each.value.object_id, type = "Group" }
}
Un workspace, un grup per rol i una assignació que lliga cada grup al seu rol. Aquest mòdul, cridat nou vegades, és el que genera els 36 grups i les 36 assignacions.
Pas 2: terraform plan
Amb les credencials en variables d’entorn:
terraform init
terraform plan "-var-file=environments/ventas.tfvars" "-out=ventas.tfplan"
Compte: a Windows PowerShell 5.1, posa entre cometes els arguments que porten un punt. Sense cometes,
-var-file=environments/ventas.tfvarses parteix en dos i Terraform es queixa de «Too many command line arguments».
Al plan val la pena aturar-se en quatre llocs: la capacity, un workspace, un grup i una assignació de rol. I després a l’última línia:
Plan: 84 to add, 0 to change, 0 to destroy.
Les 84 són nou workspaces, 36 grups, 36 assignacions, la capacity, el seu resource group i el sufix aleatori del seu nom. Cap canvi i cap esborrat, que és el que esperes la primera vegada.
Pas 3: terraform apply
terraform apply ventas.tfplan
Primer es crea la capacity, que triga un parell de minuts. Després, en paral·lel, els grups i els workspaces. Al final:
Apply complete! Resources: 84 added, 0 changed, 0 destroyed.
Mentre corre, hi ha temps per a un cafè.
Pas 4: el que ha creat
- Azure: la capacity, al seu propi resource group.
- Entra ID: busques
sg-fbc-ventasi surten 36 grups. Asg-fbc-ventas-silver-prod-admin, pestanya Members, hi som l’Alfonso i jo. - Fabric: filtres per «ventas» i surten els nou workspaces. A
ventas-silver-prod, Manage access mostra els quatre grups, cadascun amb el seu rol. I a Workspace settings → License, la capacity.
Els nou workspaces segueixen el mateix patró de noms i tenen exactament els mateixos permisos.
Pas 5: tornar a llançar el plan
Què passa si torno a fer plan sense canviar res?
No changes. Your infrastructure matches the configuration.
Res. Com explicava a la teoria, apply es comporta com un MERGE: el pots llançar deu vegades i continues tenint nou workspaces.
Pas 6: drift, algú ha tocat el portal
Ara faig de companya amb pressa: entro a ventas-silver-prod, Manage access, i trec el grup sg-fbc-ventas-silver-prod-viewer. «Un momentet, per provar».
Plan una altra vegada:
# module.medallion_cicd[0].module.workspace["silver-prod"].fabric_workspace_role_assignment.this["Viewer"] will be created
+ resource "fabric_workspace_role_assignment" "this" { ... }
Plan: 1 to add, 0 to change, 0 to destroy.
Terraform ha consultat Fabric, ha vist que aquesta assignació ja no existeix i proposa tornar-la a crear. Un apply i el grup torna a tenir el seu rol.
Pas 7: un entorn més en una línia
I si demà cal QA? S’afegeix al .tfvars:
environments = ["dev", "test", "qa", "prod"]
I el plan:
Plan: 27 to add, 0 to change, 0 to destroy.
Tres workspaces (bronze, silver i gold de QA), dotze grups i dotze assignacions, amb el mateix patró que la resta. Els entorns que ja existien no es toquen.
Pas 8: terraform destroy
Quan el projecte, la demo o la prova de concepte s’acaba:
terraform plan -destroy "-var-file=environments/ventas.tfvars" "-out=borrar.tfplan"
terraform apply borrar.tfplan
Llegeix el plan abans: ha de dir 84 to destroy i res més. S’esborren els workspaces, els grups i la capacity, i sense la capacity es deixa de facturar.
Compte: amb la capacity en pausa, el provider de Fabric no deixa fer ni plan, ni apply, ni destroy. Reprèn-la abans i espera que estigui Active.
L’script de la demo
Al repo veuràs scripts/demo.ps1. Embolcalla aquestes mateixes ordres per no haver de teclejar-les davant de 200 persones.
| Drecera | Què fa |
|---|---|
. .\scripts\demo.ps1 env | Carrega les credencials a la sessió (amb el punt davant) |
.\scripts\demo.ps1 init | terraform init i el workspace d’estat |
.\scripts\demo.ps1 plan | Plan amb el .tfvars de l’escenari |
.\scripts\demo.ps1 apply | Plan a fitxer, confirmació i apply d’aquest fitxer |
.\scripts\demo.ps1 drift | Plan per veure què ha canviat fora de Terraform |
.\scripts\demo.ps1 qa / sin-qa | Afegeix o treu l’entorn de QA |
.\scripts\demo.ps1 pausa / reanuda | Posa en pausa o reprèn la capacity |
.\scripts\demo.ps1 destroy | Plan d’esborrat, confirmació i destroy |
Abans i després
| Abans | Després |
|---|---|
| Una tarda de clics per projecte | Un apply d’uns minuts |
| Un Word desactualitzat com a documentació | El codi descriu com està la plataforma |
| QA i pro semblants, però no iguals | Tots els entorns surten del mateix mòdul |
| «Qui ha tret aquest rol?» sense resposta | El drift apareix al següent plan |
El següent projecte comença copiant un .tfvars en lloc d’obrir el portal. Si ho proves amb el repo, explica’m als comentaris com t’ha anat.
La Pris, la meva gata, ha vist el vídeo sencer i la seva única aportació ha estat asseure’s al teclat durant l’apply. Per sort ja estava aplicant el plan desat.
Enllaços d’interès
T’ha servit? Aplaudeix, sense compte. Pots fer-ho diverses vegades.
Comentaris