Aller au contenu
English
English
Infrastructure as Code medium

Pulumi - inputs, outputs, config et secrets

10 min de lecture

logo pulumi

Quand un premier lab Pulumi fonctionne, le problème suivant arrive vite : les valeurs importantes sont codees en dur dans __main__.py. Le nom de la VM, la mémoire, l'utilisateur ou un mot de passe de lab deviennent alors penibles a modifier et faciles a exposer au mauvais endroit. Ce guide vous montre comment passer a une stack plus propre avec pulumi config, des outputs utiles et un secret gere par Pulumi.

Le scenario a été rejoue le 1 avril 2026 sur KVM/libvirt avec une VM nommée pulumi-config-vm, un réseau pulumi-config-net, des outputs lisibles via pulumi stack output et un secret adminPassword masque dans la CLI puis stocke en mode secure: dans le fichier de stack.

Flux Pulumi entre la configuration CLI, la stack, le code Python, le backend local, preview, up et les outputs

  • lire des valeurs simples depuis Pulumi Config ;
  • lire une valeur secrète avec require_secret() ;
  • injecter cette valeur dans un flux cloud-init ;
  • exporter les informations utiles avec pulumi.export() ;
  • vérifier que Pulumi masque bien le secret dans la CLI.

Reprenez le projet local déjà prepare dans les guides précédents :

Fenêtre de terminal
cd ~/pulumi-kvm-local
source venv/bin/activate
pulumi stack select dev

Verification : la stack dev doit être selectionnee sans erreur.

Nous allons remplacer les valeurs codees en dur par des lectures de config. Remplacez __main__.py par ce programme :

import pulumi
import pulumi_libvirt as libvirt
config = pulumi.Config()
network_name = config.get("networkName") or "pulumi-lab-net"
vm_name = config.get("vmName") or "pulumi-lab-vm"
vm_memory_mib = config.get_int("vmMemoryMiB") or 2048
vm_vcpu = config.get_int("vmVcpu") or 2
admin_user = config.get("adminUser") or "devops"
admin_password = config.require_secret("adminPassword")
provider = libvirt.Provider(
"libvirt-provider",
uri="qemu:///system",
)
lab_network = libvirt.Network(
"pulumi-lab-network",
name=network_name,
autostart=True,
domain=libvirt.NetworkDomainArgs(name="pulumi.lab"),
forward=libvirt.NetworkForwardArgs(mode="nat"),
ips=[
libvirt.NetworkIpArgs(
address="192.168.151.1",
prefix=24,
dhcp=libvirt.NetworkIpDhcpArgs(
ranges=[
libvirt.NetworkIpDhcpRangeArgs(
start="192.168.151.50",
end="192.168.151.200",
)
]
),
)
],
opts=pulumi.ResourceOptions(provider=provider),
)
vm_disk = libvirt.Volume(
"pulumi-lab-vm-disk",
name="pulumi-lab-vm.qcow2",
pool="default",
capacity=20,
capacity_unit="GiB",
backing_store=libvirt.VolumeBackingStoreArgs(
path="/var/lib/libvirt/images/ubuntu-22.04-cloudimg.qcow2",
format=libvirt.VolumeBackingStoreFormatArgs(type="qcow2"),
),
target=libvirt.VolumeTargetArgs(
format=libvirt.VolumeTargetFormatArgs(type="qcow2"),
),
opts=pulumi.ResourceOptions(provider=provider),
)
cloudinit_disk = libvirt.CloudinitDisk(
"pulumi-lab-cloudinit",
name=f"{vm_name}-cloudinit.iso",
meta_data=f"""instance-id: {vm_name}
local-hostname: {vm_name}
""",
user_data=pulumi.Output.format(
"""#cloud-config
hostname: {0}
manage_etc_hosts: true
users:
- name: {1}
groups: sudo
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
chpasswd:
list: |
{1}:{2}
expire: false
ssh_pwauth: true
package_update: true
packages:
- qemu-guest-agent
runcmd:
- systemctl enable --now qemu-guest-agent
""",
vm_name,
admin_user,
admin_password,
),
opts=pulumi.ResourceOptions(provider=provider),
)
lab_vm = libvirt.Domain(
"pulumi-lab-vm",
type="kvm",
name=vm_name,
memory=vm_memory_mib,
memory_unit="MiB",
vcpu=vm_vcpu,
running=True,
os=libvirt.DomainOsArgs(
type="hvm",
type_arch="x86_64",
),
devices=libvirt.DomainDevicesArgs(
disks=[
libvirt.DomainDevicesDiskArgs(
device="disk",
source=libvirt.DomainDevicesDiskSourceArgs(
volume=libvirt.DomainDevicesDiskSourceVolumeArgs(
pool="default",
volume=vm_disk.name,
),
),
target=libvirt.DomainDevicesDiskTargetArgs(
dev="vda",
bus="virtio",
),
),
libvirt.DomainDevicesDiskArgs(
device="cdrom",
read_only=True,
source=libvirt.DomainDevicesDiskSourceArgs(
file=libvirt.DomainDevicesDiskSourceFileArgs(
file=cloudinit_disk.path,
),
),
target=libvirt.DomainDevicesDiskTargetArgs(
dev="hda",
bus="ide",
),
),
],
interfaces=[
libvirt.DomainDevicesInterfaceArgs(
source=libvirt.DomainDevicesInterfaceSourceArgs(
network=libvirt.DomainDevicesInterfaceSourceNetworkArgs(
network=lab_network.name,
),
),
model=libvirt.DomainDevicesInterfaceModelArgs(type="virtio"),
),
],
),
opts=pulumi.ResourceOptions(provider=provider),
)
pulumi.export("network_name", lab_network.name)
pulumi.export("network_id", lab_network.id)
pulumi.export("network_uuid", lab_network.uuid)
pulumi.export("vm_name", lab_vm.name)
pulumi.export("vm_disk_name", vm_disk.name)
pulumi.export("admin_user", pulumi.Output.from_input(admin_user))
pulumi.export("vm_memory_mib", pulumi.Output.from_input(vm_memory_mib))

Le point clé est simple : le code ne porte plus toutes les valeurs utiles en dur. Il lit maintenant une partie du contexte dans la stack.

Ajoutez maintenant les valeurs de stack. Les cinq premières sont des valeurs simples. La dernière est un secret.

Fenêtre de terminal
pulumi config set vmName pulumi-config-vm --stack dev
pulumi config set networkName pulumi-config-net --stack dev
pulumi config set vmMemoryMiB 3072 --stack dev
pulumi config set vmVcpu 2 --stack dev
pulumi config set adminUser devops --stack dev
pulumi config set --secret adminPassword 'Pulumi-Devops-2026!' --stack dev

Vous pouvez ensuite observer la configuration sans exposer le secret :

Fenêtre de terminal
pulumi config --stack dev

Verification : la sortie doit montrer adminPassword [secret] et non la valeur en clair.

Si vous voulez comprendre ou la stack stocke cette information, lisez aussi le fichier de stack :

Fenêtre de terminal
sed -n '1,120p' Pulumi.dev.yaml

Vous devez y voir une entrée secure: pour adminPassword.

Lancez maintenant le preview :

Fenêtre de terminal
pulumi preview --stack dev

Dans le scenario valide, Pulumi annonce au minimum :

  • network_name : "pulumi-config-net" ;
  • vm_name : "pulumi-config-vm" ;
  • vm_memory_mib : 3072 ;
  • la création du réseau, du disque, du cloud-init et du domaine.

Le secret n'apparaît pas en clair. C'est exactement ce que vous cherchez ici.

Fenêtre de terminal
pulumi up --stack dev --yes
pulumi stack output vm_name --stack dev
pulumi stack output admin_user --stack dev
pulumi stack output vm_memory_mib --stack dev

Verification : dans le lab rejoue, les outputs retournent respectivement :

  • pulumi-config-vm ;
  • devops ;
  • 3072.

Les outputs servent a exposer les informations utiles a la fin du run sans vous obliger a inspecter directement le state brut.

Fenêtre de terminal
virsh list --all | grep pulumi-config-vm
virsh domstate pulumi-config-vm
virsh domiflist pulumi-config-vm

Vous devez voir :

  • une VM pulumi-config-vm en état running ;
  • une interface reliee au réseau pulumi-config-net ;
  • un modèle virtio.

Cette etape prouve que vos valeurs de config ont bien pilote la création réelle de la ressource et pas seulement le texte du programme.

Fenêtre de terminal
pulumi destroy --stack dev --yes

Le workflow reste le même : un bon guide Pulumi ne s'arrête pas a up, il inclut toujours le retour a un état propre.

L'objet Config() est votre point d'entrée vers les valeurs de stack. Il ne lit pas des variables Python globales. Il lit la configuration rattachée au projet et a la stack selectionnee.

Ces méthodes servent a récupérer des valeurs non secretes. Ici, elles pilotent le nom du réseau, le nom de la VM, la mémoire et le nombre de vCPU.

require_secret() indique a Pulumi qu'une valeur doit être traitée comme sensible. C'est pour cela que adminPassword reste masque dans la CLI et stocke sous forme secure: dans Pulumi.dev.yaml.

Le bloc cloud-init doit assembler des valeurs simples et une valeur secrète. pulumi.Output.format() permet de composer cette chaîne sans casser le modèle de données de Pulumi.

Les exports servent a publier les informations que vous voulez relire après up. Ici, ils rendent le run plus lisible pour vérifier rapidement le nom de la VM, l'utilisateur configure ou la mémoire choisie.

SymptômeCause probableSolution
Missing required configuration variable 'adminPassword'Le secret n'a pas été définiRejouez pulumi config set --secret adminPassword ... --stack dev
Le secret apparaît en clair dans votre codeValeur collee directement dans __main__.pyRemplacez-la par config.require_secret()
vm_memory_mib ne change pasLa valeur n'est pas relue depuis la stack couranteVérifiez pulumi stack select dev puis relancez preview
La VM porte encore l'ancien nomLa ressource existante n'a pas été recreee dans le bon contexteRelisez le preview, appliquez puis vérifiez avec virsh
  • Pulumi Config évite de coder en dur toutes les valeurs utiles dans __main__.py.
  • require_secret() permet de traiter une valeur sensible sans l'afficher en clair dans la CLI.
  • pulumi.export() rend le résultat du run plus lisible.
  • Le bon réflexe reste config -> preview -> up -> output -> verification -> destroy.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens ce site gratuitement, sans publicité, sans profilage et sans compte à créer. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn