Managing GCP Cloud Storage with Pulumi
Learn how to provision a Cloud Storage bucket with versioning, enforced public access prevention, and lifecycle rules using Pulumi and TypeScript
Managing GCP Cloud Storage with Pulumi
This guide provisions a Cloud Storage bucket with Pulumi - uniform bucket-level access, enforced public access prevention, versioning, and lifecycle rules that move old objects to cheaper storage before deleting them entirely.
The Bucket
// index.ts
import * as gcp from "@pulumi/gcp";
const bucket = new gcp.storage.Bucket("app-data", {
name: "my-app-data-bucket-001",
location: "US",
forceDestroy: false,
uniformBucketLevelAccess: true,
publicAccessPrevention: "enforced",
versioning: {
enabled: true,
},
lifecycleRules: [{
condition: { age: 30 },
action: { type: "SetStorageClass", storageClass: "NEARLINE" },
}, {
condition: { age: 365 },
action: { type: "Delete" },
}],
});
export const bucketName = bucket.name;
export const bucketUrl = bucket.url;
uniformBucketLevelAccess: true disables per-object ACLs entirely in favor of IAM-only access control - Google’s current recommended default, and a prerequisite for publicAccessPrevention to have any effect (ACL-based access can otherwise make individual objects public regardless of IAM policy). publicAccessPrevention: "enforced" is the hard override: nothing in the bucket can ever be made public, full stop, the direct equivalent of the four blockPublic*/ignorePublicAcls/restrictPublicBuckets flags on aws.s3.BucketPublicAccessBlock in the S3 guide - GCP just expresses the same guarantee as a single setting.
location: "US" is a multi-region location (not a specific region like us-central1) - Cloud Storage buckets can be regional, dual-region, or multi-region, trading availability for latency and cost; a multi-region bucket like this one survives the loss of an entire region.
Lifecycle Rules
The two rules above run independently and are evaluated in order against every object’s age: at 30 days an object transitions to Nearline storage (cheaper, with a minimum-30-day retention charge and a per-retrieval fee), and at 365 days it’s deleted outright - the same “cool down, then expire” pattern as the S3 guide’s lifecycle configuration and the Azure Storage guide’s management policy, expressed with Cloud Storage’s own condition/action shape.
Best Practices
uniformBucketLevelAccess: trueandpublicAccessPrevention: "enforced"by default on every bucket, the same reasoning as blocking public access by default on S3 and Azure Storage - opt out per-bucket only with a specific, reviewed reason.- Enable versioning before anything writes to the bucket, not after - an object overwritten while versioning was off has no prior version to recover.
- Pair versioning with a lifecycle rule targeting noncurrent versions, not just the rules shown here (which apply to
ageregardless of version state) - otherwise every historical version of every object accumulates storage cost indefinitely.
Conclusion
Compare the mechanism here to S3 and Azure Storage: all three clouds converge on the same shape (a bucket/account resource, a public-access lockdown, versioning, and age-based lifecycle rules) even though the exact property names and defaults differ enough that copying one cloud’s checklist onto another isn’t safe without checking.
For more Pulumi topics, check out: