Bucket

A private S3 bucket with encryption, TLS-only access and production versioning on by default. Grant access in one line instead of writing IAM policy.

Storage · anvil:aws:Bucket

Minimal

The smallest config that deploys.

import { App } from '@anvil-cloud/sdk';
import * as anvil from '@anvil-cloud/sdk';

export default new App({
  run(ctx) {
    const uploads = new anvil.aws.Bucket('uploads');
  },
});
import anvil_cloud as anvil

def infra(ctx: anvil.Context):
    uploads = anvil.aws.Bucket("uploads")

anvil.App(run=infra)
package main

import (
    "github.com/anvil-cloud/anvil/sdk/go/anvil"
    anvilaws "github.com/anvil-cloud/anvil/sdk/go/anvil/aws"
)

func main() {
    anvil.Run(anvil.AppConfig{
        Run: func(ctx *anvil.Context) error {
            _, err := anvilaws.NewBucket(ctx.PulumiCtx(), "uploads", nil)
            if err != nil {
                return err
            }
            return nil
        },
    })
}

Granting access

Grant compute access with one method per kind of access: grantRead, grantWrite, grantReadWrite, grantDelete or grantFullAccess. Pass key prefixes to limit it to part of the bucket.

uploads.grantRead(api);                      // the whole bucket
uploads.grantReadWrite(worker, ['inbox/*']); // only keys under inbox/
uploads.grant_read(api)                                # the whole bucket
uploads.grant_read_write(worker, paths=["inbox/*"])    # only keys under inbox/
// Go has no grant methods yet: give the Lambda the same actions with permissions.
worker, err := anvilaws.NewLambda(ctx.PulumiCtx(), "worker", &anvilaws.LambdaArgs{
    Runtime: anvilaws.LambdaRuntime_Nodejs24_x,
    Handler: pulumi.String("functions/worker/index.handler"),
    Permissions: anvilaws.LambdaPermissionArray{
        // grantReadWrite(worker, ['inbox/*']): objects under inbox/ only.
        anvilaws.LambdaPermissionArgs{
            Actions:  pulumi.ToStringArray([]string{"s3:GetObject", "s3:PutObject"}),
            Resource: uploads.Arn,
            Path:     pulumi.String("inbox"),
        },
        anvilaws.LambdaPermissionArgs{
            Actions:  pulumi.ToStringArray([]string{"s3:ListBucket"}),
            Resource: uploads.Arn,
        },
    },
})
if err != nil {
    return err
}

What Anvil creates

  • grantRead adds s3:GetObject and s3:ListBucket, grantWrite adds s3:PutObject and grantDelete adds s3:DeleteObject. grantReadWrite is read plus write, and grantFullAccess is all four. No grant can change the bucket's policy or settings.
  • The actions apply to this bucket only. With prefixes, object reads, writes and deletes apply only to keys under them.
  • s3:ListBucket isn't limited by prefix, so compute that can read part of the bucket can still list every key name in it.
  • Each grant is an inline policy on the compute's role, removed when you remove the grant.

Good to know

  • Public access is blocked, objects are encrypted with SSE-S3 and only TLS requests are allowed, with nothing to configure.
  • Versioning is always on in production stages. Set versioning to turn it on elsewhere, and versionRetention so old versions don't pile up.
  • Without storageTransition, objects use Intelligent-Tiering.
  • retention (Object Lock) can only be set when the bucket is created, and locked objects can't be deleted or overwritten until their period ends.

The Full example tab has a complete programand the runtime code that uses it. Every argument and attribute is on the Reference tab.