Creating an AWS S3 Bucket with Pulumi in Python

This walks through creating an object storage bucket on all three major clouds with Pulumi and Python: an S3 bucket on AWS, a Cloud Storage bucket on Google Cloud, and a blob container on Azure. Same tool, same language, three providers. The examples use current resource names — AWS in particular changed which S3 resource is recommended.

How do you set up a Pulumi project for cloud storage?

A Pulumi program is a normal Python project. Create the project, then add the provider SDK you need. Each cloud has its own package: pulumi-aws, pulumi-gcp, and pulumi-azure-native.

mkdir buckets && cd buckets
pulumi new python --name buckets --yes

# add the provider SDKs you need
pip install pulumi-aws pulumi-gcp pulumi-azure-native

pulumi config set aws:region us-east-1
pulumi config set gcp:project my-gcp-project
pulumi config set azure-native:location eastus

Provider credentials come from the same environment the CLI tools use — the AWS credentials chain, gcloud auth application-default login, and an Azure CLI login or service principal. Pulumi does not manage auth itself.

How do you create an S3 bucket with Pulumi in Python?

Use aws.s3.Bucket. The older aws.s3.BucketV2 is now deprecated in favor of it. In the current model, most settings — versioning, encryption, public access blocking, lifecycle rules — are separate resources that reference the bucket, not inline arguments.

import pulumi_aws as aws

bucket = aws.s3.Bucket('assets',
    bucket='my-app-assets-prod',
    tags={'Environment': 'prod'})

aws.s3.BucketVersioning('assets-versioning',
    bucket=bucket.id,
    versioning_configuration={'status': 'Enabled'})

aws.s3.BucketServerSideEncryptionConfiguration('assets-sse',
    bucket=bucket.id,
    rules=[{
        'apply_server_side_encryption_by_default': {'sse_algorithm': 'AES256'},
    }])

aws.s3.BucketPublicAccessBlock('assets-pab',
    bucket=bucket.id,
    block_public_acls=True,
    block_public_policy=True,
    ignore_public_acls=True,
    restrict_public_buckets=True)

⚠ Note: a plain aws.s3.Bucket is not private by default in every account configuration. Add the BucketPublicAccessBlock resource unless you specifically need public objects.

How do you create a Google Cloud Storage bucket with Pulumi?

Use gcp.storage.Bucket. Google’s resource keeps the common settings inline: uniform_bucket_level_access replaces per-object ACLs with IAM, and versioning is a nested block.

import pulumi_gcp as gcp

bucket = gcp.storage.Bucket('assets',
    name='my-app-assets-prod',
    location='US',
    uniform_bucket_level_access=True,
    versioning={'enabled': True},
    lifecycle_rules=[{
        'action': {'type': 'Delete'},
        'condition': {'age': 365},
    }])

uniform_bucket_level_access=True is the setting you almost always want — it turns off object ACLs and makes IAM the single source of access truth.

How do you create an Azure Blob container with Pulumi?

Azure needs two resources: a storage.StorageAccount, then a storage.BlobContainer inside it. Both live in a resource group.

import pulumi_azure_native as azure_native

account = azure_native.storage.StorageAccount('assets',
    account_name='myappassetsprod',
    resource_group_name='my-rg',
    location='eastus',
    kind=azure_native.storage.Kind.STORAGE_V2,
    sku={'name': azure_native.storage.SkuName.STANDARD_LRS},
    allow_blob_public_access=False,
    minimum_tls_version=azure_native.storage.MinimumTlsVersion.TLS1_2)

container = azure_native.storage.BlobContainer('assets-container',
    account_name=account.name,
    resource_group_name='my-rg',
    container_name='assets',
    public_access=azure_native.storage.PublicAccess.NONE)

Azure’s blob versioning is a property of the account’s blob service rather than the container, so it is configured separately if you need it.

How do the three providers compare for bucket creation?

AWSGoogle CloudAzure
Pulumi resourceaws.s3.Bucketgcp.storage.Bucketstorage.StorageAccount + storage.BlobContainer
VersioningSeparate BucketVersioning resourceInline versioning blockAccount blob-service property
Public access controlSeparate BucketPublicAccessBlockInline public_access_preventionallow_blob_public_access + container public_access
ACL modelSeparate BucketAcl (or block it)uniform_bucket_level_accessRBAC / shared keys / SAS
Lifecycle rulesSeparate BucketLifecycleConfigurationInline lifecycle_rulesManagement policy on the account

AWS spreads configuration across many small resources; Google keeps it on one; Azure splits along the account/container line. Once the project is set up, running all three is one pulumi up.

pulumi preview      # show what will be created
pulumi up           # create it
pulumi destroy      # tear it down

For turning this into a reusable module and wiring it into CI, see the infrastructure documentation guide and the GitHub Actions workflow guide.

Frequently Asked Questions

Should I use aws.s3.Bucket or aws.s3.BucketV2 in Pulumi?

Use aws.s3.Bucket. BucketV2 is deprecated in the current AWS provider. In the current model, versioning, encryption, public-access blocking, and lifecycle rules are separate resources that reference the bucket by id.

Do I need separate resources for S3 bucket settings in Pulumi?

Yes, on AWS. Versioning is aws.s3.BucketVersioning, encryption is aws.s3.BucketServerSideEncryptionConfiguration, public access is aws.s3.BucketPublicAccessBlock, and lifecycle is aws.s3.BucketLifecycleConfiguration. Google Cloud keeps most of these inline on the bucket.

What is uniform bucket-level access on Google Cloud Storage?

A setting that disables per-object ACLs so IAM policies are the only way access is granted. Setting uniform_bucket_level_access=True in Pulumi is the recommended default for new buckets.

Can one Pulumi program manage buckets on AWS, GCP, and Azure at once?

Yes. Install pulumi-aws, pulumi-gcp, and pulumi-azure-native in the same project, set each provider’s config, and define resources from all three. A single pulumi up creates them together.

Quick Summary

  • Use aws.s3.Bucket (not the deprecated BucketV2); AWS bucket settings are separate resources referencing bucket.id.
  • gcp.storage.Bucket keeps versioning and uniform_bucket_level_access inline — set the latter to True for new buckets.
  • Azure needs a StorageAccount plus a BlobContainer; set allow_blob_public_access=False and minimum_tls_version to TLS1_2.
  • One Pulumi project can hold all three providers — install the SDKs, set each provider’s config, run pulumi up once.
  • pulumi preview before pulumi up, every time.

Building this into a shared module? Pair it with the GitHub Actions workflow guide to run pulumi preview on every pull request.