Écosystème
Wiz + Terramate : checker les configs cloud avant le apply, pas après l'incident
Analyse DevSecOps : orchestrer les stacks Terramate changées, scanner le plan Terraform avec wizcli, bloquer les misconfigs critiques en CI, puis corréler au runtime Wiz.
Wiz scanne très bien ce qui tourne déjà dans le cloud. Terramate orchestre très bien des dizaines de stacks Terraform / OpenTofu. Le trou classique : la misconfig arrive au apply, et le CSPM la découvre heures ou jours plus tard. Security group 0.0.0.0/0, bucket sans block public access, rôle IAM Action: "*" — le review de PR ne les voit pas toujours, surtout quand le plan touche trois modules et deux workspaces.
Il n’existe pas d’intégration native « bouton Wiz » dans Terramate Cloud. C’est une bonne nouvelle : le pattern robuste est client-side. Terramate sélectionne les stacks changées ; terraform plan produit un plan résolu ; wizcli scanne ce plan (ou le HCL) avec la même policy engine que le reste de Wiz ; la CI bloque avant l’apply. Ensuite le connecteur cloud Wiz reprend pour le runtime et le code-to-cloud via le state.
Cet article décrit le pipeline que je mets en place quand une équipe a déjà Terramate et Wiz (ou envisage les deux) : menace → contrôle CI → artefact GitHub Actions → preuve.
La menace : config cloud validée trop tard
Trois échecs de process, pas de CVE exotique.
Le plan « trop grand pour être relu ». Terramate aide précisément parce qu’une monorepo Terraform grossit vite. Sans terramate list --changed, la CI planifie tout, ou rien. Les reviewers regardent le diff HCL, pas l’effet réel des variables et modules. Un count ou un for_each conditionnel crée une ressource publique que personne n’a lue dans le HCL brut.
Le scan HCL sans plan. Un scanner qui ne lit que les .tf rate ce qui n’apparaît qu’après résolution : modules distants, valeurs de variables d’environnement, data sources. Wiz CLI peut scanner un répertoire Terraform et un plan JSON (terraform show -json) — c’est le second qui se rapproche de ce qui sera appliqué.
Le CSPM runtime comme seul filet. Utile, indispensable même (on en parle dans le comparatif Wiz vs natif). Mais une fois le SG ouvert, la fenêtre d’exposition existe. Le shift-left n’élimine pas le CSPM : il réduit le flux de findings « on vient de se tirer une balle dans le pied ».
Rôles : qui fait quoi
| Couche | Outil | Job |
|---|---|---|
| Orchestration monorepo | Terramate | stacks, run --changed, order, tags, code generation |
| Provisioning | Terraform / OpenTofu | plan / apply par stack |
| Policy IaC (avant apply) | Wiz CLI (wizcli) | misconfigs, secrets dans le code/plan, seuils de blocage |
| Posture runtime | Wiz CSPM / Security Graph | findings live, chemins d’attaque, code-to-cloud via state |
Terramate ne remplace pas Wiz ; Wiz ne sélectionne pas vos stacks changées. Le glue code est un workflow CI — et c’est ce glue code qui manque dans 80 % des démos « on a Wiz ».
Prérequis côté Wiz
- Compte service Wiz dédié CI (pas le compte d’un humain) :
WIZ_CLIENT_ID/WIZ_CLIENT_SECRETen secrets GitHub (OIDC vers un secrets manager si vous le pouvez). - Une IaC policy dans Wiz avec enforcement CLI en
BLOCKsur Critical (et idéalement High pour les contrôles CIS que vous avez déjà remédiés). - Connecteur cloud (AWS/Azure/GCP) déjà branché — sinon vous n’aurez que le scan préventif, pas la boucle code-to-cloud.
- Optionnel mais utile : connecteur VCS pour mapper fichiers / auteurs ; le state Terraform reste le pont le plus fiable entre ressource live et déclaration.
Artefact : GitHub Actions — stacks changées → plan JSON → wizcli
Workflow minimal, OIDC-ready pour AWS, calqué sur le preview Terramate avec une étape Wiz après le plan. Ajustez versions et rôle IAM.
name: terramate-wiz-preview
on:
pull_request:
paths:
- "stacks/**"
- "**.tf"
- "**.tm.hcl"
permissions:
id-token: write
contents: read
pull-requests: write
security-events: write
jobs:
preview-and-scan:
runs-on: ubuntu-latest
env:
WIZ_IAC_POLICY: "Default IaC policy"
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Terramate
uses: terramate-io/terramate-action@v3
with:
version: "0.14.0"
- name: Install Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: "1.12.2"
terraform_wrapper: false
- name: List changed stacks
id: list
run: |
CHANGED=$(terramate list --changed)
echo "changed<<EOF" >> "$GITHUB_OUTPUT"
echo "$CHANGED" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
if [ -z "$CHANGED" ]; then
echo "No changed stacks — skip."
else
echo "Changed stacks:"
echo "$CHANGED"
fi
- name: Configure AWS credentials (OIDC)
if: steps.list.outputs.changed != ''
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN_PLAN }}
aws-region: eu-west-3
- name: Terraform init (changed stacks)
if: steps.list.outputs.changed != ''
run: |
terramate run --changed --parallel 1 -- \
terraform init -lock-timeout=5m
- name: Terraform plan (changed stacks)
if: steps.list.outputs.changed != ''
run: |
terramate run --changed --parallel 5 -- \
terraform plan -out out.tfplan -lock=false
- name: Export plan JSON per stack
if: steps.list.outputs.changed != ''
run: |
terramate run --changed -- \
bash -c 'terraform show -json out.tfplan > out.tfplan.json'
- name: Install wizcli
if: steps.list.outputs.changed != ''
run: |
curl -fsSL -o wizcli \
https://downloads.wiz.io/wizcli/latest/wizcli-linux-amd64
chmod +x wizcli
sudo mv wizcli /usr/local/bin/wizcli
wizcli version
- name: Authenticate wizcli
if: steps.list.outputs.changed != ''
env:
WIZ_CLIENT_ID: ${{ secrets.WIZ_CLIENT_ID }}
WIZ_CLIENT_SECRET: ${{ secrets.WIZ_CLIENT_SECRET }}
run: wizcli auth --id "$WIZ_CLIENT_ID" --secret "$WIZ_CLIENT_SECRET"
- name: Wiz IaC scan on plan JSON (fail on policy)
if: steps.list.outputs.changed != ''
run: |
set -euo pipefail
FAIL=0
while IFS= read -r stack; do
[ -z "$stack" ] && continue
echo "::group::Wiz scan $stack"
if ! wizcli iac scan \
--path "${stack}/out.tfplan.json" \
--name "pr-${{ github.event.pull_request.number }}-${stack}" \
--policy "$WIZ_IAC_POLICY" \
--format sarif \
--output "${stack}/wiz-iac.sarif"; then
FAIL=1
fi
echo "::endgroup::"
done <<< "${{ steps.list.outputs.changed }}"
exit $FAIL
- name: Upload SARIF (first stack with report)
if: always() && steps.list.outputs.changed != ''
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: stacks
# Si upload-sarif exige un fichier unique, fusionnez les SARIF
# ou uploadez le premier : $(terramate list --changed | head -1)/wiz-iac.sarif
Notes de terrain :
--changedest non négociable : scanner toute la monorepo à chaque PR brûle le quota Wiz et noie le signal.- Scannez le plan JSON, pas seulement le dossier
.tf, dès que vous avez des modules externes ou des variables injectées en CI. - La commande exacte (
wizcli iac scanvs variantes selon version) évolue : validez avecwizcli iac scan --helpsur l’image CI épinglée ; épingler le binaire par hash est préférable àlatesten prod. - Le job d’apply (branche protégée / environment GitHub) ne démarre que si ce workflow est vert — pas de
continue-on-errorsur le scan.
Script Terramate pour standardiser le scan
Pour éviter de dupliquer la logique dans cinq workflows, un script Terramate dans le dépôt :
# stacks/networking/prod/stack.tm.hcl (extrait)
stack {
name = "networking-prod"
description = "VPC, SG, endpoints — prod"
tags = ["aws", "prod", "networking"]
}
# terramate.tm.hcl à la racine
scripts {
wiz-scan-plan {
description = "plan → JSON → wizcli iac scan"
jobs = [
{
name = "plan"
commands = [
["terraform", "plan", "-out", "out.tfplan", "-lock=false"],
["terraform", "show", "-json", "out.tfplan", ">", "out.tfplan.json"],
]
},
{
name = "wiz"
commands = [
[
"wizcli", "iac", "scan",
"--path", "out.tfplan.json",
"--policy", "${env.WIZ_IAC_POLICY}",
"--name", "${terramate.stack.path.absolute}",
],
]
},
]
}
}
Puis en CI : terramate script run --changed wiz-scan-plan (syntaxe selon votre version Terramate Scripts — vérifiez la doc de la version épinglée). L’idée compte plus que le DSL : une commande repo = plan + scan, réutilisable en local par les devs avec le même service account de préprod.
Ce que Wiz doit bloquer en priorité
Ne partez pas d’une policy à 400 contrôles. Activez d’abord le blocage sur :
- Exposition publique involontaire (SG/NACL
0.0.0.0/0sur ports sensibles, buckets/objets publics, load balancers sans restriction). - Identités dangereuses (
Action/Resource*, trust policies trop larges, absence de conditionoidcsur les rôles CI). - Secrets en clair dans le plan (variables non marked sensitive qui fuient dans le plan JSON — attention : le plan JSON peut contenir des secrets ; traitez les artefacts CI comme sensibles, rétention courte).
- Stockage non chiffré / sans CMK là où votre politique l’exige.
Le reste (conformité CIS complète) peut rester en WARN jusqu’à ce que le backlog de remédiation suive.
Boucle runtime : fermer le code-to-cloud
Après apply, Wiz côté cloud rattache souvent le finding live au fichier Terraform via le state. Conditions :
- Le state est accessible au connecteur / à l’intégration prévue (S3 backend + droits lecture dédiés, ou sync via plateforme type HCP/Scalr si vous y êtes).
- Les ressources sont taguées de façon stable (
Owner,Stack,ManagedBy=terramate) pour le triage humain quand le mapping automatique échoue. - Un finding Critical runtime sans ticket lié au stack Terramate = dette de process, pas seulement de scanner.
Sans cette boucle, vous avez un joli gate CI et un CSPM qui ne se parlent pas — deux silos.
Preuve pour l’audit / le client
# Stacks touchées par la PR
terramate list --changed
# Dernier rapport local (exemple)
jq '.runs[0].results | length' out/wiz-summary.json
# Dans GitHub : check "Wiz IaC scan" required sur main
# Dans Wiz : filtre reports name=pr-* + policy enforcement CLI=BLOCK
Livrables typiques d’une mission : workflow ci-dessus, policy Wiz documentée (quels contrôles bloquent), matrice tags Terramate ↔ comptes cloud, et 5 findings historiques montrant « aurait été bloqué en CI ».
Quand ne pas forcer Wiz dans Terramate
- Vous n’avez pas encore Wiz : commencez par Checkov / Trivy / OPA sur le plan — moins cher, déjà utile. Wiz vient quand le graphe runtime et le code-to-cloud justifient le ticket (grille de décision).
- Votre monorepo a moins de 5 stacks : Terramate est optionnel ; un
terraform plan+ wizcli sur le dossier suffit. - Personne ne regarde les SARIF : un gate qui échoue sans propriétaire de remédiation devient du bruit. Nommez un owner par tag Terramate.
Conclusion
Wiz et Terramate se complètent dès qu’on accepte qu’il n’y a pas de connecteur magique : Terramate choisit quoi planifier, wizcli décide si le plan a le droit d’être appliqué, Wiz CSPM vérifie ce qui tourne. Si vous voulez cadrer ce pipeline (OIDC, policy de blocage, découpage des stacks, lien avec Security Hub natif), un Audit Sécurité Cloud ou une mission supply chain selon que le goulot est l’IaC ou le runtime.
Sources : Terramate — GitHub Actions preview workflow, Terramate — security & data access, Wiz × HashiCorp / Terraform run tasks, Wiz + HCP Terraform (code-to-cloud).
Cet article vous a été utile ? Partagez-le.
Aller plus loin
Un sujet, une mission, une question ?
Distribuée accompagne des PME exigeantes sur la sécurité cloud, la supply chain CI/CD et la sécurité IA.
Réserver 15 min