Declarative Multi-Cloud with Pulumi Python SDK

This article explores how the Pulumi Python SDK enables engineers to define declarative cloud infrastructure across Amazon Web Services (AWS), Microsoft Azure, and Google Cloud Platform (GCP). It details the core mechanics of Pulumi's object-oriented approach, explaining how imperative Python code is translated into a deterministic desired-state graph, how cross-cloud dependencies are managed seamlessly, and how resources across disparate cloud providers are orchestrated within a unified codebase.

The Declarative Model in an Imperative Language

Pulumi allows developers to write standard Python while maintaining a strictly declarative infrastructure model. Instead of executing direct API calls during program execution, running a Pulumi Python script instantiates resource objects that describe the desired end state of the infrastructure.

When you run pulumi up, the Pulumi runtime executes the Python code to construct a Directed Acyclic Graph (DAG) representing all declared resources and their relationships. The engine compares this graph against the existing state stored in the Pulumi state backend, calculates the necessary create, read, update, or delete (CRUD) operations, and displays a preview before applying changes. This separation of resource declaration from execution ensures idempotent deployments.

Core Abstractions: Resources, Inputs, and Outputs

The foundation of Pulumi's Python SDK lies in three primary abstractions:

Dependencies are handled automatically through Outputs. Passing an Output from an AWS resource as an Input to a GCP resource causes Pulumi's engine to resolve the dependency order implicitly, eliminating the need to manually configure provisioning sequences.

import pulumi
import pulumi_aws as aws
import pulumi_gcp as gcp

# AWS S3 Bucket
aws_bucket = aws.s3.BucketV2("app-backup-aws")

# GCP Cloud Storage Bucket using the AWS bucket name in its metadata
gcp_bucket = gcp.storage.Bucket(
    "app-backup-gcp",
    location="US",
    labels={"paired-aws-bucket": aws_bucket.id}
)

pulumi.export("aws_bucket_name", aws_bucket.id)
pulumi.export("gcp_bucket_name", gcp_bucket.name)

Multi-Cloud Definitions: AWS, Azure, and GCP

Pulumi delivers distinct, actively maintained packages for each major cloud provider, allowing native definitions across diverse architectures:

Amazon Web Services (pulumi_aws and pulumi_aws_native)

The AWS provider interfaces with AWS Cloud APIs. Engineers define resources like VPCs, ECS clusters, and DynamoDB tables by instantiating typed classes with clear keyword arguments.

Microsoft Azure (pulumi_azure_native)

Pulumi's native Azure provider is generated directly from the Azure Resource Manager (ARM) specifications. It provides zero-day support for all Azure services, exposing exact ARM templates as idiomatic Python classes with complete typing support.

Google Cloud Platform (pulumi_gcp and pulumi_google_native)

Resources in GCP, such as Google Kubernetes Engine (GKE) clusters, BigQuery datasets, and Cloud Functions, are declared via dedicated GCP packages, allowing configurations tailored to Google Cloud's specific networking and IAM models.

Managing Cross-Cloud Workflows in a Unified Program

Because Pulumi uses standard Python, multi-cloud architectures do not require distinct configuration languages or disjointed state files. A single __main__.py file or a modularized Python package can import multiple providers simultaneously.

import pulumi
import pulumi_aws as aws
import pulumi_azure_native as azure
import pulumi_gcp as gcp

# 1. AWS Networking
vpc = aws.ec2.Vpc("multi-cloud-vpc", cidr_block="10.0.0.0/16")

# 2. Azure Resource Group
resource_group = azure.resources.ResourceGroup("multi-cloud-rg")

# 3. GCP Storage
bucket = gcp.storage.Bucket("multi-cloud-bucket", location="US")

Cross-cloud references are evaluated uniformly. If an Azure virtual machine requires an access key stored in AWS Secrets Manager, or a GCP analytics pipeline reads from an Azure Blob storage endpoint, the Pulumi engine chains these dependencies into a single execution plan.

The Deployment Lifecycle

  1. Code Execution: The Python runtime evaluates the code, instantiating resource classes and wiring dependencies using Output.apply() or direct assignment.
  2. Graph Generation: The SDK sends the declared resource definitions to the central Pulumi CLI engine via gRPC.
  3. Diff and Plan: The engine compares the requested graph against the recorded state and computes the delta.
  4. Provider Execution: The engine calls the respective provider plugins (AWS, Azure, GCP) to execute the necessary REST API requests concurrently whenever the dependency graph permits.
  5. State Persistence: Once the providers confirm resource creation or modification, the new state is securely written to the backend.