Pulumi Deployment#
InfraGuard supports Pulumi as an alternative to Terraform for infrastructure provisioning. Both engines create identical resources (VPS, firewall, SSH key) and produce the same outputs.
Prerequisites#
- Install the Pulumi CLI
- Install Python 3.12+ (Pulumi runs the InfraGuard program with the Python runtime)
- Install the provider’s Pulumi SDK:
pip install pulumi-digitalocean,pulumi-aws,pulumi-azure-native, orpulumi-hcloud - Set the provider’s credential environment variables (see Cloud Credentials)
Quick Start#
infraguard deploy run \
--domain cdn.example.com \
--upstream https://10.0.0.5:8443 \
--provider do \
--engine pulumi \
--c2-profile profiles/jquery.profile \
--ssh-key ~/.ssh/id_ed25519.pub \
--operator-ip 1.2.3.4/32The --engine pulumi flag is the only difference from a Terraform deployment. All other flags are the same.
How It Works#
The PulumiProvider class wraps the Pulumi CLI with the same interface as the Terraform provider:
- Stage: Copies the Pulumi project from
deploy/pulumi/into the working directory - Init stack: Creates (or selects) a Pulumi stack named from the domain
- Set config: Maps CLI flags to
infraguard:*config keys - Apply: Runs
pulumi up --yes - Outputs: Reads
instance_ip,instance_id,ssh_user, and other outputs from the stack
After provisioning, the deploy lifecycle is the same as Terraform: the CLI waits for bootstrap, SCPs config and profile files, starts Docker Compose, and polls the health endpoint.
State Management#
Pulumi uses a local file backend. State files live in the deploy working directory (.infraguard-deploy/ by default). No remote backend or Pulumi Cloud account is required.
PULUMI_BACKEND_URL=file:///path/to/work_dir
PULUMI_CONFIG_PASSPHRASE=""
PULUMI_SKIP_UPDATE_CHECK=trueThese are set automatically by the CLI. You do not need to export them.
Unlike Terraform, Pulumi state is not age-encrypted after apply. The --state-key and --state-identity flags are silently ignored when using --engine pulumi. If you need state encryption, use Pulumi’s built-in secrets provider or a remote backend.
Supported Providers#
| Provider | Alias | Pulumi Package | SSH User |
|---|---|---|---|
| DigitalOcean | do | pulumi-digitalocean | root |
| AWS | aws | pulumi-aws | ubuntu |
| Azure | azure | pulumi-azure-native | operator |
| Hetzner | hz | pulumi-hcloud | root |
Cloudflare Workers deployment is Terraform-only. There is no Pulumi equivalent.
Destroy#
infraguard deploy destroy --engine pulumiSelects the active stack in the working directory and runs pulumi destroy --yes.
Rotation#
infraguard rotate \
--domain cdn.example.com \
--upstream https://10.0.0.5:8443 \
--provider do \
--engine pulumi \
--c2-profile profiles/jquery.profile \
--ssh-key ~/.ssh/id_ed25519.pub \
--operator-ip 1.2.3.4/32Blue-green rotation works identically with Pulumi. The green instance is provisioned with Pulumi, health-checked, and the blue instance is destroyed with Pulumi.
Differences from Terraform#
| Terraform | Pulumi | |
|---|---|---|
| State format | JSON .tfstate | Pulumi checkpoint JSON |
| State encryption | age-encrypted by CLI | Not encrypted (use Pulumi secrets provider) |
| DO SSH key | Must pre-register on account | Created automatically |
| Language | HCL .tf files | Python |
| Binary | terraform | pulumi |
| Backend | Local directory | Local file backend |
Project Structure#
The Pulumi project lives in deploy/pulumi/:
deploy/pulumi/
Pulumi.yaml # Project definition with config schema
Pulumi.example.yaml # Example stack config
requirements.txt # Python dependencies
__main__.py # Entrypoint: reads config, dispatches to provider
cloud_init.py # Shared bootstrap script
provider_digitalocean.py # DO: Droplet + Tag + Firewall + SshKey
provider_aws.py # AWS: EC2 + SecurityGroup + KeyPair
provider_azure.py # Azure: RG + VNet + Subnet + PIP + NSG + NIC + VM
provider_hetzner.py # Hetzner: Server + Firewall + SshKey