Skip to main content

Python Pulumi Wrapper - Dataclass-based configuration framework for Pulumi infrastructure as code

Project description

pypuwa

Python Pulumi Wrapper — Dataclass-based configuration framework for Pulumi infrastructure as code.

Define your infrastructure config in Python dataclasses. Get interpolation, secret management, environment variable generation, and a deploy CLI for free.

Install

pip install pypuwa

Quick Start

from dataclasses import dataclass, field
from typing import FrozenSet
from pypuwa import (
    BaseStackConfig,
    ProviderConfig,
    BaseDatabaseConfig,
    BaseAppRunnerConfig,
    Secret,
    secret,
    create_config,
)


# 1. Define your service configs
@dataclass(kw_only=True)
class MyDatabaseConfig(BaseDatabaseConfig):
    INSTANCE_ID: str = "{stack}-my-api-db"
    NAME: str = "my_api"
    USERNAME: str = "api_user"


@dataclass(kw_only=True)
class MyAppConfig(BaseAppRunnerConfig):
    SERVICE_NAME: str = "{stack}-my-api"
    CPU: str = "2 vCPU"
    MEMORY: str = "4 GB"

    # Application env vars
    DEBUG: str = "False"
    DATABASE_NAME: str = "${services.database.NAME}"
    API_SECRET: Secret = secret()

    _NON_ENV_FIELDS: FrozenSet[str] = frozenset(["CPU", "MEMORY"])


# 2. Compose into a stack config
@dataclass(kw_only=True)
class MyServicesConfig:
    database: MyDatabaseConfig = field(default_factory=MyDatabaseConfig)
    app: MyAppConfig = field(default_factory=MyAppConfig)


@dataclass(kw_only=True)
class MyStackConfig(BaseStackConfig):
    services: MyServicesConfig = field(default_factory=MyServicesConfig)


# 3. Define environments
staging_config = MyStackConfig(
    providers=ProviderConfig(aws={"region": "us-east-1"}),
)

production_config = MyStackConfig(
    providers=ProviderConfig(aws={"region": "us-west-2"}),
    services=MyServicesConfig(
        database=MyDatabaseConfig(MULTI_AZ=True, ALLOCATED_STORAGE=100),
        app=MyAppConfig(CPU="4 vCPU", MEMORY="8 GB"),
    ),
)

# 4. Create the config resolver (auto-detects environment from Pulumi stack)
config = create_config(
    environments={"staging": staging_config, "production": production_config}
)

CLI

pypuwa includes a deploy orchestrator that syncs your Python config to Pulumi YAML, manages secrets interactively, and runs pulumi up.

# Full deploy: sync config → set new secrets → pulumi up
pypuwa deploy staging

# Preview only (sync + show changes, no deploy)
pypuwa preview production

# Sync config to Pulumi YAML without deploying
pypuwa sync staging

# With options
pypuwa deploy production --backup          # Backup existing YAML before sync
pypuwa deploy staging --dry-run            # Sync config, skip deploy
pypuwa deploy staging --runner "uv run pulumi"  # Custom Pulumi runner
pypuwa deploy staging --project-name my-infra   # Custom config prefix

What pypuwa deploy does

  1. Sync — Generates Pulumi.<env>.yaml from your Python config, preserving existing encrypted secrets
  2. New secrets — Detects new secret() fields and prompts you to set values (arrow-key selection, all pre-selected)
  3. Existing secrets — Offers to update existing secret values (arrow-key selection)
  4. Deploy — Runs pulumi up on the selected stack

Interactive secret management

New secrets are detected automatically and presented with arrow-key navigation:

? Select secrets to set now (space to toggle, enter to confirm):
  ❯ ◉ services.api.database.PASSWORD
    ◉ services.api.app_runner.DJANGO_SECRET_KEY
    ◉ services.redis.AUTH_TOKEN

All new secrets are pre-selected — just hit enter to set them all, or space to deselect ones you want to skip. Values are entered with hidden input.

Features

Interpolation

SERVICE_NAME: str = "{stack}-my-service"        # -> "staging-my-service"
DB_NAME: str = "${services.database.NAME}"      # -> resolved from config tree

Secrets

# Auto-inferred from field name
PASSWORD: Secret = secret()                     # -> loaded from Pulumi encrypted config

# Explicit key
API_KEY: Secret = secret("EXTERNAL_API_KEY")

# Cross-service reference
REDIS_PASS: Secret = secret("${services.redis.AUTH_TOKEN}")

Environment Variables

@dataclass(kw_only=True)
class MyConfig(BaseAppRunnerConfig):
    DEBUG: str = "True"
    DATABASE_URL: str = "postgres://..."
    CPU: str = "2 vCPU"                         # Infrastructure, not an env var

    _NON_ENV_FIELDS: FrozenSet[str] = frozenset(["CPU", "MEMORY"])

config = MyConfig(SERVICE_NAME="api")
env_vars = config.env_dict()
# {"SERVICE_NAME": "api", "DEBUG": "True", "DATABASE_URL": "postgres://..."}
# CPU and MEMORY are excluded

Configuration Manager

from pypuwa import ConfigurationManager

manager = ConfigurationManager(
    environments={"staging": staging_config, "production": production_config}
)

# Generate resolved config
stack = manager.generate_stack_config("my-stack", "staging")

# Generate Pulumi YAML
yaml_config = manager.to_pulumi_yaml("my-stack", "production")

# Validate
issues = manager.validate("staging")

Project Structure

A project using pypuwa looks like this:

my-infra/
├── __main__.py                     # Pulumi entry point
├── Pulumi.yaml                     # Pulumi project definition
├── Pulumi.staging.yaml             # Generated (secrets encrypted here)
├── Pulumi.production.yaml          # Generated
├── config/
│   ├── base/
│   │   ├── stack.py                # MyStackConfig(BaseStackConfig)
│   │   └── services/
│   │       ├── api.py              # Database + AppRunner configs
│   │       ├── worker.py           # Worker config
│   │       └── redis.py            # Cache config
│   └── environments/
│       ├── staging.py              # staging_config = MyStackConfig(...)
│       └── production.py           # production_config = MyStackConfig(...)
├── components/                     # Your cloud-specific Pulumi wrappers
│   ├── database.py
│   ├── container_app.py
│   └── redis.py
└── services/                       # Deployment orchestration
    ├── api/entrypoint.py
    └── worker/entrypoint.py

See examples/basic-project for a complete working example.

Requirements

  • Python >= 3.11
  • Pulumi >= 3.0

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pypuwa-0.1.3.tar.gz (25.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pypuwa-0.1.3-py3-none-any.whl (21.8 kB view details)

Uploaded Python 3

File details

Details for the file pypuwa-0.1.3.tar.gz.

File metadata

  • Download URL: pypuwa-0.1.3.tar.gz
  • Upload date:
  • Size: 25.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pypuwa-0.1.3.tar.gz
Algorithm Hash digest
SHA256 144460cd0ca561bc59d9a2c44f0fa1cc25ff793a963fef9a90c0eec64ac4471a
MD5 2f8ccba9a8777c1335e98bda92dcf830
BLAKE2b-256 3764ff67942e6cf159f499098b5b2133a29bb3d3d1a3a10c6154136ce7b03fac

See more details on using hashes here.

Provenance

The following attestation bundles were made for pypuwa-0.1.3.tar.gz:

Publisher: publish.yml on cafadev/pypuwa

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pypuwa-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: pypuwa-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 21.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pypuwa-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 e503a4ba31da29d897f506993e672af7b5ae0f55b2c40d10c567ed6f1fd4fefd
MD5 e655e95fee5803d20f22caf9ec65c04b
BLAKE2b-256 54caef8f600ffddb991e8faaa36fcd7b11f76cc5e10ebd8b8942caa4d16bbb5c

See more details on using hashes here.

Provenance

The following attestation bundles were made for pypuwa-0.1.3-py3-none-any.whl:

Publisher: publish.yml on cafadev/pypuwa

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page