Pulumi Python SDK
Infrastructure as Code SDK for Python. Current version: 3.227.0 (Mar 2026). Requires Pulumi CLI installed separately — 'pip install pulumi' alone does nothing useful. All resource properties return Output[T] not plain values — cannot use them directly as strings/ints. Must use .apply() or Output.all() to work with output values. pulumi.export() must be at top level, not inside apply(). CLI and SDK versions should be kept in sync.
Warnings
- breaking 'pip install pulumi' installs the SDK but does NOT install the CLI. Running Python files directly does nothing. Must install Pulumi CLI separately and run via 'pulumi up'.
- breaking All resource properties (bucket.bucket, instance.id, etc.) return Output[T] — not plain values. Printing them shows '<pulumi.output.Output object>'. Cannot concatenate or format directly.
- breaking pulumi.export() called inside .apply() callbacks or functions is silently ignored. Stack outputs must be registered at the top level of __main__.py.
- gotcha CLI version and SDK version should be kept in sync. Using CLI v3.114+ with SDK < v3.114 breaks --continue-on-error and other features added in that release.
- gotcha Provider packages (pulumi-aws, pulumi-azure-native, pulumi-gcp) must be installed separately. 'pip install pulumi' alone gives you no cloud resources.
- gotcha hasattr() does not work on Pulumi resource objects. Python output lifting overrides __getattr__ — hasattr will always return True for resource outputs even for non-existent properties.
- gotcha Pulumi programs must be run via 'pulumi up' or 'pulumi preview' — not 'python __main__.py'. Running directly produces no output and doesn't register resources.
Install
-
pip install pulumi -
curl -fsSL https://get.pulumi.com | sh
Imports
- Output / apply
import pulumi import pulumi_aws as aws bucket = aws.s3.Bucket('my-bucket') # Output values must be accessed via .apply() bucket.bucket.apply(lambda name: print(f'Bucket name: {name}')) # Combine multiple outputs with Output.all() pulumi.Output.all(bucket.bucket, bucket.region).apply( lambda args: print(f'Bucket {args[0]} in {args[1]}') ) # Export at TOP LEVEL — not inside apply() pulumi.export('bucket_name', bucket.bucket) pulumi.export('bucket_arn', bucket.arn) - pulumi.export
import pulumi import pulumi_aws as aws # Stack outputs — must be at top level of __main__.py bucket = aws.s3.Bucket('my-bucket', acl='private') # Export Output directly — Pulumi resolves it pulumi.export('bucket_name', bucket.bucket) pulumi.export('bucket_arn', bucket.arn) # Export transformed output pulumi.export('bucket_url', bucket.bucket.apply(lambda name: f'https://{name}.s3.amazonaws.com') )
Quickstart
# 1. Install CLI: curl -fsSL https://get.pulumi.com | sh
# 2. pip install pulumi pulumi-aws
# 3. pulumi new aws-python
# 4. Edit __main__.py:
import pulumi
import pulumi_aws as aws
# Create an S3 bucket
bucket = aws.s3.Bucket(
'my-bucket',
acl='private',
tags={'Environment': 'dev'}
)
# Create an EC2 security group
sg = aws.ec2.SecurityGroup(
'web-sg',
description='Allow HTTP',
ingress=[aws.ec2.SecurityGroupIngressArgs(
protocol='tcp',
from_port=80,
to_port=80,
cidr_blocks=['0.0.0.0/0']
)]
)
# Stack exports — must be at top level
pulumi.export('bucket_name', bucket.bucket)
pulumi.export('bucket_arn', bucket.arn)
pulumi.export('sg_id', sg.id)
# 5. pulumi up