Project Oxygen & Ideo-LabIDEO LAB Dashboard 2026

⚙️ Pulumi – Infrastructure as Code programmable

Guide IDEO-Lab pour concevoir, déployer et automatiser ton infrastructure Cloud avec Pulumi (AWS, Azure, GCP, Kubernetes…) en utilisant de “vrais” langages (TypeScript, Python, Go, .NET).

1.1

Vue d’ensemble Pulumi

Concept, différences vs Terraform, projets / stacks / state.

IaC Multi-Cloud Programmable
1.2

Installation & comptes

CLI Pulumi, backends de state, login Pulumi Cloud ou self-hosted.

CLI State
1.3

Premier projet & stack

pulumi new, structure d’un projet, gestion des stacks (dev / prod).

pulumi new Stacks
1.4

Langages & SDK

TypeScript, Python, Go, .NET – avantages, patterns de code.

TS Python Go
2.1

Providers & écosystème

AWS, Azure, GCP, Kubernetes, Cloudflare, GitHub, DBs…

AWS Azure K8s
2.2

State & backends

Pulumi Cloud, S3, Azure Blob, GCS. Organisation des environnements.

State Backends
3.1

Pulumi + Kubernetes

Cluster, namespaces, Helm charts, déploiement applicatif.

K8s Helm

3.2 Stack AWS IDEO-Lab

VPC + Subnets + Security Groups + S3 + EC2 (exemple complet).

Exemple AWS
3.3

Pulumi vs Terraform

Forces/faiblesses, quand choisir l’un ou l’autre, coexistence.

Terraform Comparatif
4.1

CI/CD & pipelines

GitHub Actions, GitLab CI, protections sur les previews.

CI/CD Preview

4.2 Sécurité & Policies

Secrets, encryption, Policy as Code (CrossGuard, OPA).

Security Policies
5.1

Cheat-sheet Pulumi

Commandes & patterns à retenir.

pulumi up pulumi stack
1.1 Vue d’ensemble Pulumi & Architecture IaC
Pulumi en bref

Pulumi est une plateforme d’Infrastructure as Code (IaC) qui permet de décrire ton infrastructure Cloud avec des langages généralistes (TypeScript, Python, Go, C#, F#…).

Au lieu d’un DSL comme HCL, tu utilises ton langage habituel :

  • Conditions, boucles, fonctions, classes, modules, tests unitaires…
  • IDE complet (IntelliSense, refactorings, navigation, LSP).
  • Réutilisation de code entre infra et application.
Principe clé : “Infra = logiciel”

L’infra n’est plus un ensemble de fichiers YAML/HCL figés, mais un vrai projet logiciel :

# Exemple minimal en TypeScript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const bucket = new aws.s3.Bucket("ideolab-bucket", {
    acl: "private",
    versioning: { enabled: true },
});

export const bucketName = bucket.id;
                        

On peut factoriser, créer des libs internes, partager des composants “infrastructure” comme on le fait déjà pour du code applicatif.

Projects & Stacks
Pulumi Project
├── Pulumi.yaml              # Métadonnées du projet (nom, runtime…)
├── Pulumi.dev.yaml          # Config de stack “dev”
├── Pulumi.prod.yaml         # Config de stack “prod”
├── index.ts / __main__.py   # Code infra principal
└── infra/
    ├── network.ts           # VPC, Subnets, Security Groups…
    ├── database.ts          # RDS / Aurora / CosmosDB…
    └── k8s.ts               # Cluster & workloads Kubernetes
                        
  • Project : unité logique de code (repo, module).
  • Stack : instance de ce project (dev, staging, prod, client-A…)
  • State : représentation de l’infra déployée (ressources, outputs).
Cycle de vie d’un pulumi up
dev écrit du code infra (TypeScript / Python / Go / C#)
      │
      ▼
pulumi preview           # Plan d’exécution (diff)
      │
      ▼
pulumi up                # Création / update des ressources
      │
      ▼
State mis à jour         # Pulumi Cloud ou backend S3/Azure/GCS
      │
      ▼
pulumi stack output      # Récupération des outputs (URL, IDs…)
                        

Le process ressemble énormément à Terraform (plan/apply), mais piloté par du code applicatif classique.

Cas d’usage DevOps typiques
  • Plateforme SaaS multi-client : une stack par client, logique de provisioning dynamique.
  • Infra d’équipe data / AI : clusters Kubernetes + buckets + data lakes + jobs batch.
  • Landing zone multi-cloud : standardiser VPC, comptes, IAM, monitoring sur plusieurs providers.
Pourquoi Pulumi plaît aux devs
  • Même langage pour app + infra.
  • Facilité pour intégrer dans les monorepos.
  • Tests unitaires sur l’infra (ex. pytest, jest).
  • Possibilité de générer l’infra à partir de données (boucles, mapping JSON, YAML, API…).
Positionnement vs Terraform (vue macro)
CritèrePulumiTerraform
LangageTypeScript, Python, Go, .NETHCL (DSL maison)
Courbe d’apprentissageTrès simple pour devs applicatifsTrès simple pour profils infra/cloud
Réutilisation de codeExcellente (fonctions, libs, tests…)Modules et variables (plus limité)
ÉcosystèmeModerne, multi-cloud, cross-SaaSUltra-massif, standard de facto
Cas favorisPlateformes “Infra as Software” complexesInfra Cloud “classique” très standardisée
1.2 Installation CLI Pulumi & backends
Linux / macOS
curl -fsSL https://get.pulumi.com | sh

# Ajouter au PATH si besoin
export PATH=$PATH:$HOME/.pulumi/bin

pulumi version
                        
Windows (PowerShell)
choco install pulumi     # via Chocolatey
# ou
scoop install pulumi
                        
SDKs (langages)
# TypeScript / JavaScript
npm install @pulumi/pulumi @pulumi/aws

# Python
pip install pulumi pulumi-aws

# Go
go get github.com/pulumi/pulumi/sdk/v3

# .NET
dotnet add package Pulumi
                        

Le runtime est déterminé par le champ runtime dans Pulumi.yaml (nodejs, python, go, dotnet…).

Pulumi Cloud (par défaut)

Backend managé (SaaS), gère :

  • Stockage du state, historique, audit.
  • Prévisualisations, review de changements.
  • Intégration CI/CD, RBAC, secrets chiffrés.
pulumi login              # ouvre le navigateur
# ou
pulumi login https://app.pulumi.com
                        
Backends self-managed

Utile si tu veux garder le state dans ton infra :

# Backend S3
pulumi login s3://ideolab-pulumi-state

# Backend Azure
pulumi login azblob://pulumi-state

# Backend GCS
pulumi login gs://pulumi-state
                        

Dans ce cas, tu dois gérer toi-même sécurité, backups, lifecycle du bucket de state.

Créer son premier projet
mkdir ideolab-infra
cd ideolab-infra

pulumi new aws-typescript     # ou aws-python, azure-python, kubernetes-go...
# Pulumi pose des questions (nom de project, stack, région…)
                        
Configuration & secrets
# Config non sensible
pulumi config set aws:region eu-west-1

# Secret (mot de passe DB, token API…)
pulumi config set db:password super-secret --secret

# Lister la config de la stack active
pulumi config
                        

Les secrets sont chiffrés côté backend. Pulumi peut aussi utiliser une clé KMS / KeyVault / etc. selon le backend.

1.3 Structure d’un projet & gestion des stacks
Fichiers clés
Pulumi.yaml         # Nom du projet, runtime, description
Pulumi.dev.yaml     # Config propre à la stack “dev”
Pulumi.prod.yaml    # Config propre à la stack “prod”
index.ts            # Code principal (entrypoint) TS
infra/
  network.ts        # VPC, subnetting
  app.ts            # Load balancer, instances, lambdas
  observability.ts  # CloudWatch / Grafana / Logs
                        
Extrait de Pulumi.yaml
name: ideolab-pulumi-aws
runtime: nodejs
description: "Infra IDEO-Lab AWS avec Pulumi (VPC + EC2 + RDS)"
backend:
  url: s3://ideolab-pulumi-state
                        

On peut facilement splitter le code en modules (network, db, k8s…) pour qu’un gros projet reste maintenable.

index.ts (entrypoint)
import "./infra/network";
import "./infra/app";

// ou créer les ressources directement ici :
/*
const vpc = new aws.ec2.Vpc("main-vpc", { ... });
...
*/
                        
Module réutilisable
import * as aws from "@pulumi/aws";

export function createAppBucket(name: string) {
    const bucket = new aws.s3.Bucket(name, {
        versioning: { enabled: true },
        serverSideEncryptionConfiguration: {
            rule: { applyServerSideEncryptionByDefault: { sseAlgorithm: "AES256" } },
        },
    });
    return bucket;
}

// index.ts
const staticBucket = createAppBucket("ideolab-static");
                        
Structure Python
.
├── __main__.py
└── ideolab_infra/
    ├── __init__.py
    ├── network.py
    └── app.py
                        
__main__.py
import pulumi
from ideolab_infra.network import network_id
from ideolab_infra.app import app_url

pulumi.export("network_id", network_id)
pulumi.export("app_url", app_url)
                        
Créer / sélectionner une stack
pulumi stack init dev
pulumi stack init staging
pulumi stack init prod

pulumi stack select dev
pulumi config set env dev
                        
Organisation recommandée
  • 1 project par domaine fonctionnel (ex: core-infra, data-platform…)
  • Stacks alignées sur les environnements (dev / qa / prod / client-X…)
  • State séparé par org / projet / stack pour limiter les blast radius.
1.4 Langages & SDK Pulumi
Choisir son runtime
LangageAvantages
TypeScriptExcellent tooling, typage, écosystème Node, idéal monorepo.
PythonUltra simple pour scripts DevOps / Data / ML.
GoRapide, binaire unique, très apprécié en infra.
.NET (C#, F#)Parfait dans les shops Microsoft / Azure-centrés.
Pattern “component” réutilisable
// TypeScript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

export class WebApp extends pulumi.ComponentResource {
    public readonly url: pulumi.Output<string>;

    constructor(name: string, opts?: pulumi.ComponentResourceOptions) {
        super("ideolab:component:WebApp", name, {}, opts);

        const bucket = new aws.s3.Bucket(`${name}-static`);
        const site = new aws.s3.BucketWebsiteConfigurationV2(`${name}-site`, {
            bucket: bucket.id,
            indexDocument: { suffix: "index.html" },
        });

        this.url = pulumi.interpolate`http://${bucket.websiteEndpoint}`;
        this.registerOutputs({ url: this.url });
    }
}
                    

On peut packager ces composants dans des libs internes (npm, wheel, module Go…) pour les réutiliser sur tout IDEO-Lab.

2.1 Providers & écosystème Pulumi
Clouds principaux
ProviderPackage
AWS@pulumi/aws, pulumi-aws
Azure@pulumi/azure-native
GCP@pulumi/gcp
Kubernetes@pulumi/kubernetes
Au-delà du Cloud “classique”
  • Cloudflare (DNS, WAF, CDN).
  • GitHub / GitLab (repos, teams, permissions).
  • DBaaS (MongoDB Atlas, Neon, etc.).
  • Monitoring & logs (Datadog, New Relic…).

Idéal pour gérer à la fois l’infra “physique” (VPC, K8s) et les couches services / SaaS autour (CI/CD, observabilité).

2.2 State, backends & organisation
Ce que contient le state
  • Liste des ressources créées (type, nom, ID provider).
  • Valeurs d’inputs et d’outputs.
  • Versioning des déploiements, historique.
pulumi stack export > state.json
# snapshot JSON complet de la stack
                    
Bonnes pratiques d’organisation
  • Un org Pulumi par entreprise (ex: ideolab).
  • Projets alignés sur les domaines (infra-core, data-platform…).
  • Stacks par environnement / client.
  • Backups réguliers des buckets de state si backend S3/Azure/GCS.
3.1 Pulumi + Kubernetes
Déployer un manifest K8s
import * as k8s from "@pulumi/kubernetes";

const appLabels = { app: "ideolab-api" };

const deployment = new k8s.apps.v1.Deployment("ideolab-api", {
    spec: {
        selector: { matchLabels: appLabels },
        replicas: 3,
        template: {
            metadata: { labels: appLabels },
            spec: {
                containers: [{
                    name: "api",
                    image: "ghcr.io/ideolab/api:latest",
                    ports: [{ containerPort: 8000 }],
                }],
            },
        },
    },
});
                    
Helm charts avec Pulumi
const nginxIngress = new k8s.helm.v3.Chart("ingress", {
    chart: "ingress-nginx",
    version: "4.10.0",
    fetchOpts: { repo: "https://kubernetes.github.io/ingress-nginx" },
    values: {
        controller: {
            replicaCount: 2,
            service: { type: "LoadBalancer" },
        },
    },
});
                    

Pulumi devient un “orchestrateur d’infra” complet : Cloud + cluster + workloads, avec un seul pulumi up.

3.2 Stack AWS IDEO-Lab (VPC + EC2 + S3)
Network + S3 (TypeScript)
import * as aws from "@pulumi/aws";

const vpc = new aws.ec2.Vpc("ideolab-vpc", {
    cidrBlock: "10.0.0.0/16",
    enableDnsHostnames: true,
});

const publicSubnet = new aws.ec2.Subnet("ideolab-public", {
    vpcId: vpc.id,
    cidrBlock: "10.0.1.0/24",
    mapPublicIpOnLaunch: true,
});

const bucket = new aws.s3.Bucket("ideolab-artifacts", {
    versioning: { enabled: true },
});
                    
EC2 bastion + outputs
const sg = new aws.ec2.SecurityGroup("bastion-sg", {
    vpcId: vpc.id,
    ingress: [{
        protocol: "tcp", fromPort: 22, toPort: 22,
        cidrBlocks: ["0.0.0.0/0"],   // à restreindre en prod !
    }],
    egress: [{ protocol: "-1", fromPort: 0, toPort: 0, cidrBlocks: ["0.0.0.0/0"] }],
});

const bastion = new aws.ec2.Instance("bastion", {
    ami: "ami-0c02fb55956c7d316",   // Ubuntu (ex.)
    instanceType: "t3.micro",
    subnetId: publicSubnet.id,
    vpcSecurityGroupIds: [sg.id],
    keyName: "ideolab-key",
});

export const bastionIp    = bastion.publicIp;
export const bucketName   = bucket.id;
export const vpcId        = vpc.id;
                    

Ces outputs sont visibles via pulumi stack output (et via Pulumi Cloud si tu l’utilises).

3.3 Pulumi vs Terraform – quand utiliser quoi ?
Quand Pulumi est particulièrement intéressant
  • Tu as déjà des équipes très dev-oriented (TS / Python / Go).
  • Tu veux factoriser un max le code infra, faire des libs internes.
  • Tu as des stacks dynamiques (multi-clients, multi-regions) calculées à partir de données.
  • Tu veux tester ton infra comme du code (TDD infra).
Coexistence possible

Tu peux très bien garder Terraform sur certains périmètres (legacy, équipes déjà outillées) et introduire Pulumi pour les nouvelles plateformes :

  • Terraform pour la “landing zone” globale.
  • Pulumi pour les workloads applicatifs / K8s / SaaS.
  • Interop via les IDs de ressources (VPC existant, subnets, etc.).
4.1 CI/CD & pipelines Pulumi
GitHub Actions (exemple)
name: Pulumi Deploy

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: pulumi/actions@v4
        with:
          command: up
          stack-name: ideolab/dev
        env:
          PULUMI_ACCESS_TOKEN: $
          AWS_ACCESS_KEY_ID: $
          AWS_SECRET_ACCESS_KEY: $
          AWS_REGION: eu-west-1
                    
Bonnes pratiques
  • 1 job “preview” (PR) + 1 job “deploy” (merge sur main).
  • Token Pulumi en secret CI, jamais commité.
  • Utiliser des stack tags pour tracer l’origine des déploiements.
  • Logs centralisés vers CloudWatch / Datadog / ELK.
4.2 Sécurité, secrets & Policy as Code
Gestion des secrets
# En CLI
pulumi config set db:password super-secret --secret

# En TypeScript
const dbPassword = pulumi.secret(
    pulumi.config.require("db:password")
);

# Propager au provider
const db = new aws.rds.Instance("db", {
    username: "ideolab",
    password: dbPassword,
    ...
});
                    
Policy as Code (CrossGuard)

CrossGuard permet de définir des politiques de sécurité (Node/TS) qui s’appliquent aux stacks :

// Exemple de règle simplifiée
policy.resourceOfType(aws.s3.Bucket, (bucket, args, reportViolation) => {
    if (!bucket.versioning || !bucket.versioning.enabled) {
        reportViolation("Les buckets S3 doivent avoir le versioning activé.");
    }
});
                    

Ces règles peuvent être intégrées dans les pipelines CI/CD pour bloquer un pulumi up non conforme (sécurité, coûts, best practices).

5.1 Cheat-sheet Pulumi (commandes rapides)
Commandes essentielles
pulumi new aws-typescript     # init projet
pulumi stack ls               # lister les stacks
pulumi stack select dev       # sélectionner une stack
pulumi config set key value   # config
pulumi config set key value --secret  # secret
pulumi preview                # plan
pulumi up                     # apply
pulumi destroy                # détruire
pulumi stack output           # lire les outputs
                    
Debug & inspection
pulumi stack export > state.json   # snapshot JSON
pulumi logs                        # logs de la dernière opération
pulumi whoami                      # organisation / user
                    

Tu peux ajouter un petit alias shell par environnement (ex: pud pour “pulumi up dev”, pup pour prod avec confirmation renforcée).