Featured image of post J'ai réécrit mon IaC Cloudflare avec Pulumi

J'ai réécrit mon IaC Cloudflare avec Pulumi

Retour d'expérience après la migration de mon infrastructure Cloudflare de Terraform vers Pulumi : langage, CLI, tests, policy as code et secrets.

Mon infrastructure Cloudflare (DNS, Workers, règles de sécurité) était décrite en Terraform. Par curiosité, je l’ai réécrite avec Pulumi. Voici mon retour, avec ce que j’ai aimé et ce qu’il me reste à vérifier.

Qu’est-ce que Pulumi ?

Pulumi est un outil d’Infrastructure as Code qui permet de décrire l’infrastructure dans un langage de programmation classique (Python, TypeScript, Go, C#, Java) plutôt que dans un DSL comme le HCL. Le principe reste le même que Terraform : un état désiré, un état réel, un diff, puis un apply (ici pulumi up).

J’ai choisi Python pour ce projet.

Un vrai langage plutôt que du HCL

C’est le point qui m’a le plus plu. Les structures conditionnelles et les boucles deviennent naturelles à écrire.

En HCL, une ressource optionnelle passe par count ou for_each avec un ternaire :

resource "cloudflare_record" "www" {
  count   = var.enable_www ? 1 : 0
  zone_id = var.zone_id
  name    = "www"
  type    = "CNAME"
  content = var.target
}

En Python, c’est un simple if :

if config.get_bool("enable_www"):
    cloudflare.DnsRecord(
        "www",
        zone_id=zone_id,
        name="www",
        type="CNAME",
        content=target,
    )

On gagne aussi les fonctions, les classes, les types et l’autocomplétion de l’IDE. Une liste d’enregistrements DNS devient une liste Python que l’on parcourt avec une boucle for, sans contorsion.

Une CLI simple et bien faite

La CLI est claire et cohérente :

pulumi preview   # équivalent de terraform plan
pulumi up        # équivalent de terraform apply
pulumi stack     # gestion des environnements (stacks)
pulumi config    # configuration par stack

Les stacks remplacent avantageusement les workspaces Terraform : chaque environnement a sa propre config et son propre état.

Tests, policy as code et lint

Parce que l’infra est du code Python, je peux utiliser l’écosystème Python tel quel.

Tests unitaires

Pulumi permet de mocker le moteur et de tester les ressources sans rien déployer. J’ai ajouté des tests pytest qui vérifient par exemple qu’aucun enregistrement DNS n’est créé sans proxy lorsque ce n’est pas voulu.

@pytest.mark.parametrize("name", ["root-aaaa", "www-aaaa"])
def test_proxiable_dns_records_are_proxied(resources, name):
    record = of_type(resources, DNS)[name]
    assert record["type"] == "AAAA"
    assert record["proxied"] is True
    assert record["content"] == "100::"
    assert record["ttl"] == 1

Policy as code

Avec CrossGuard, on écrit des règles de conformité qui sont évaluées pendant le preview et bloquent le déploiement en cas de violation.

def dns_records_must_be_proxied(args: ResourceValidationArgs, report: ReportViolation):
    if args.resource_type != DNS_RECORD_TYPE:
        return
    if args.props.get("type") in PROXIABLE_TYPES and args.props.get("proxied") is not True:
        report(f"Le record DNS {args.name} ({args.props.get('type')}) doit avoir proxied=True.")


PolicyPack(
    name="website-policies",
    enforcement_level=EnforcementLevel.MANDATORY,
    policies=[
        ResourceValidationPolicy(
            name="dns-records-proxied",
            description="Les records DNS A, AAAA et CNAME doivent passer par le proxy Cloudflare.",
            validate=dns_records_must_be_proxied,
        ),
    ],
)

Lint avec ruff

Pour le lint et le formatage, ruff s’intègre sans effort. Un seul outil, très rapide, et la même qualité de code que sur un projet applicatif.

ruff check .
ruff format .

La gestion des secrets

Les secrets sont chiffrés dans la configuration de la stack :

pulumi config set --secret cloudflare:apiToken

La valeur est stockée chiffrée dans Pulumi.<stack>.yaml, donc versionnable sans risque, et elle reste masquée dans les sorties. Côté Terraform, un secret finit souvent en clair dans le state, alors qu’ici le chiffrement est natif.

La plateforme Pulumi Cloud

Je n’ai pas testé la plateforme cloud, mais elle semble complète sur le papier :

  • Drift detection : détecter les écarts entre le code et l’infrastructure réelle.
  • Governance : politiques appliquées à l’échelle de l’organisation.
  • IDP (Internal Developer Platform) : catalogue et templates pour les équipes.

À tester sur un prochain projet.

Les limites et questions ouvertes

  • Taille des projets : je ne gère ici qu’une petite infra. Je ne sais pas encore à quoi ressemble un gros repo Pulumi avec des dizaines de composants, ni comment l’outil se comporte à cette échelle (temps de preview, organisation en projets et stacks, ComponentResource).
  • Liberté du langage : plus de puissance, c’est aussi plus de façons d’écrire du code illisible. Cela demande de la discipline.
  • Écosystème et recrutement : Terraform reste le standard, avec plus de modules, d’exemples et de profils qui le connaissent.

Conclusion

Je n’ai pas regretté la migration. Pour un nouveau projet, je me demande sérieusement si je ne commencerais pas par Pulumi avant Terraform, surtout dès qu’il y a de la logique conditionnelle ou des besoins de tests.

Reste à voir comment la technologie vieillit sur une grosse base de code. Si vous avez un retour d’expérience sur un gros repo Pulumi, je suis preneur.

Généré avec Hugo
Thème Stack conçu par Jimmy