Skip to main content
Pulumi logo Pulumi logo
  1. Docs
  2. Infrastructure as Code
  3. Get Started
  4. AWS
  5. Make an update

Get started with Pulumi and AWS

    Make an update

    Next, you’ll update your project to serve a static website out of your AWS S3 bucket. You’ll change your code and then rerun pulumi up to update your infrastructure.

    Add new resources

    Pulumi knows how to evolve your current infrastructure to your project’s new desired state, both for the first deployment and for later updates.

    To turn your bucket into a static website, start by adding three new AWS S3 resources:

    1. BucketWebsiteConfiguration: configures your bucket as a website
    2. BucketOwnershipControls: allows bucket access controls to be configured
    3. BucketPublicAccessBlock: configures public access blocking for your bucket; new S3 buckets block public access by default so you don’t allow access over the internet by accident

    Open index.js index.ts __main__.py main.go Program.cs Program.fs Program.vb App.java Pulumi.yaml main.tf in your editor and add them right after your S3 bucket:

    // Bucket...
    
    // Turn the bucket into a website:
    const website = new aws.s3.BucketWebsiteConfiguration("website", {
        bucket: bucket.id,
        indexDocument: {
            suffix: "index.html",
        },
    });
    
    // Permit access control configuration:
    const ownershipControls = new aws.s3.BucketOwnershipControls("ownership-controls", {
        bucket: bucket.id,
        rule: {
            objectOwnership: "ObjectWriter"
        }
    });
    
    // Enable public access to the website:
    const publicAccessBlock = new aws.s3.BucketPublicAccessBlock("public-access-block", {
        bucket: bucket.id,
        blockPublicAcls: false,
    });
    
    # Bucket ...
    
    # Turn the bucket into a website:
    website = s3.BucketWebsiteConfiguration("website",
        bucket=bucket.id,
        index_document={
            "suffix": "index.html",
        })
    
    # Permit access control configuration:
    ownership_controls = s3.BucketOwnershipControls(
        'ownership-controls',
        bucket=bucket.id,
        rule={
            "object_ownership": 'ObjectWriter',
        },
    )
    
    # Enable public access to the website:
    public_access_block = s3.BucketPublicAccessBlock(
        'public-access-block', bucket=bucket.id, block_public_acls=False
    )
    
    // Bucket ...
    
    // Turn the bucket into a website:
    website, err := s3.NewBucketWebsiteConfiguration(ctx, "website", &s3.BucketWebsiteConfigurationArgs{
        Bucket: bucket.ID(),
        IndexDocument: &s3.BucketWebsiteConfigurationIndexDocumentArgs{
            Suffix: pulumi.String("index.html"),
        },
    })
    if err != nil {
        return err
    }
    
    // Permit access control configuration:
    ownershipControls, err := s3.NewBucketOwnershipControls(ctx, "ownership-controls", &s3.BucketOwnershipControlsArgs{
        Bucket: bucket.ID(),
        Rule: &s3.BucketOwnershipControlsRuleArgs{
            ObjectOwnership: pulumi.String("ObjectWriter"),
        },
    })
    if err != nil {
        return err
    }
    
    // Enable public access to the website:
    publicAccessBlock, err := s3.NewBucketPublicAccessBlock(ctx, "public-access-block", &s3.BucketPublicAccessBlockArgs{
        Bucket:          bucket.ID(),
        BlockPublicAcls: pulumi.Bool(false),
    })
    if err != nil {
        return err
    }
    
    // Bucket ...
    
    // Turn the bucket into a website:
    var website = new BucketWebsiteConfiguration("website", new()
    {
        Bucket = bucket.Id,
        IndexDocument = new BucketWebsiteConfigurationIndexDocumentArgs
        {
            Suffix = "index.html",
        },
    });
    
    // Permit access control configuration:
    var ownershipControls = new BucketOwnershipControls("ownership-controls", new()
    {
        Bucket = bucket.Id,
        Rule = new BucketOwnershipControlsRuleArgs
        {
            ObjectOwnership = "ObjectWriter",
        },
    });
    
    // Enable public access to the website:
    var publicAccessBlock = new BucketPublicAccessBlock("public-access-block", new()
    {
        Bucket = bucket.Id,
        BlockPublicAcls = false,
    });
    

    Also make sure you’ve imported the additional types being used at the top of the file:

    using Pulumi.Aws.S3.Inputs;
    
    // Bucket ...
    
    // Turn the bucket into a website:
    var website = new BucketWebsiteConfiguration("website", BucketWebsiteConfigurationArgs.builder()
        .bucket(bucket.id())
        .indexDocument(BucketWebsiteConfigurationIndexDocumentArgs.builder()
            .suffix("index.html")
            .build())
        .build());
    
    // Permit access control configuration:
    var ownershipControls = new BucketOwnershipControls("ownership-controls", BucketOwnershipControlsArgs.builder()
        .bucket(bucket.id())
        .rule(BucketOwnershipControlsRuleArgs.builder()
            .objectOwnership("ObjectWriter")
            .build())
        .build());
    
    // Enable public access to the website:
    var publicAccessBlock = new BucketPublicAccessBlock("public-access-block", BucketPublicAccessBlockArgs.builder()
        .bucket(bucket.id())
        .blockPublicAcls(false)
        .build());
    

    Also replace the imports at the top with this so you have access to all the new types:

    import com.pulumi.*;
    import com.pulumi.core.*;
    import com.pulumi.asset.FileAsset;
    import com.pulumi.resources.*;
    
    import com.pulumi.aws.s3.*;
    import com.pulumi.aws.s3.inputs.*;
    
    import java.util.Map;
    
    # ...
    resources:
      # Bucket ...
    
      # Turn the bucket into a website:
      website:
        type: aws:s3:BucketWebsiteConfiguration
        properties:
          bucket: ${my-bucket.id}
          indexDocument:
            suffix: index.html
    
      # Permit access control configuration:
      ownership-controls:
        type: aws:s3:BucketOwnershipControls
        properties:
          bucket: ${my-bucket.id}
          rule:
            objectOwnership: ObjectWriter
    
      # Enable public access to the website:
      public-access-block:
        type: aws:s3:BucketPublicAccessBlock
        properties:
          bucket: ${my-bucket.id}
          blockPublicAcls: false
    
    # Bucket ...
    
    # Turn the bucket into a website:
    resource "aws_s3_bucket_website_configuration" "website" {
      bucket = aws_s3_bucket.my-bucket.id
      index_document {
        suffix = "index.html"
      }
    }
    
    # Permit access control configuration:
    resource "aws_s3_bucket_ownership_controls" "ownership-controls" {
      bucket = aws_s3_bucket.my-bucket.id
      rule {
        object_ownership = "ObjectWriter"
      }
    }
    
    # Enable public access to the website:
    resource "aws_s3_bucket_public_access_block" "public-access-block" {
      bucket            = aws_s3_bucket.my-bucket.id
      block_public_acls = false
    }
    

    Notice that resources can reference each other, which forms automatic dependencies between them. Pulumi uses this information to parallelize deployments safely.

    Add an index.html

    Next, add a new file called index.html to your current directory with these contents:

    <html>
        <body>
            <h1>Hello, Pulumi!</h1>
        </body>
    </html>
    

    Then open index.js index.ts __main__.py main.go Program.cs Program.fs Program.vb App.java Pulumi.yaml main.tf and create a bucket object after the three other new resources:

    // Other resources ...
    
    // Create an S3 Bucket object
    const bucketObject = new aws.s3.BucketObject("index.html", {
        bucket: bucket.id,
        source: new pulumi.asset.FileAsset("index.html"),
        contentType: "text/html",
        acl: "public-read",
    }, { dependsOn: [ownershipControls, publicAccessBlock] });
    
    # Other resources ...
    
    # Create an S3 Bucket object
    bucket_object = s3.BucketObject(
        'index.html',
        bucket=bucket.id,
        source=pulumi.FileAsset('index.html'),
        content_type='text/html',
        acl='public-read',
        opts=pulumi.ResourceOptions(depends_on=[ownership_controls, public_access_block]),
    )
    
    // Other resources ...
    
    // Create an S3 Bucket object
    _, err = s3.NewBucketObject(ctx, "index.html", &s3.BucketObjectArgs{
        Bucket:      bucket.ID(),
        Source:      pulumi.NewFileAsset("index.html"),
        ContentType: pulumi.String("text/html"),
        Acl:         pulumi.String("public-read"),
    }, pulumi.DependsOn([]pulumi.Resource{ownershipControls, publicAccessBlock}))
    if err != nil {
        return err
    }
    
    // Other resources ...
    
    // Create an S3 Bucket object
    var bucketObject = new BucketObject("index.html", new()
    {
        Bucket = bucket.Id,
        Source = new FileAsset("index.html"),
        ContentType = "text/html",
        Acl = "public-read",
    }, new CustomResourceOptions
    {
        DependsOn = new Resource[]
        {
            ownershipControls,
            publicAccessBlock,
        },
    });
    
    // Other resources ...
    
    // Create an S3 Bucket object
    var bucketObject = new BucketObject("index.html", BucketObjectArgs.builder()
        .bucket(bucket.id())
        .source(new FileAsset("index.html"))
        .contentType("text/html")
        .acl("public-read")
        .build(), CustomResourceOptions.builder()
            .dependsOn(
                ownershipControls,
                publicAccessBlock)
            .build());
    
    # ...
    resources:
      # Other resources ...
    
      # Create an S3 Bucket object
      index.html:
        type: aws:s3:BucketObject
        properties:
          bucket: ${my-bucket.id}
          source:
            fn::fileAsset: index.html
          contentType: text/html
          acl: public-read
        options:
          dependsOn:
            - ${ownership-controls}
            - ${public-access-block}
    
    # Other resources ...
    
    # Create an S3 Bucket object
    resource "aws_s3_bucket_object" "index-html" {
      bucket       = aws_s3_bucket.my-bucket.id
      key          = "index.html"
      source       = fileasset("index.html")
      content_type = "text/html"
      acl          = "public-read"
      depends_on = [
        aws_s3_bucket_ownership_controls.ownership-controls,
        aws_s3_bucket_public_access_block.public-access-block,
      ]
    }
    

    A dotted label like index.html couldn’t be referenced in expressions (and Terraform’s grammar doesn’t allow one), so the resource is labeled index-html and the key attribute names the object in the bucket.

    This uploads the index.html file to your bucket using a Pulumi concept called an asset.

    The bucket object also declares that it dependsOn the other resources. That is because those other resources need to be created first so that AWS permits the object’s public-read ACL. Pulumi usually tracks dependencies automatically, but these are invisible to Pulumi because those specific resources cause side effects within AWS.

    Export the website URL

    To export the website’s URL for easy access, add this to the end of your program:

    // Export the bucket's autoassigned URL:
    export const url = pulumi.interpolate`http://${website.websiteEndpoint}`;
    
    # Export the bucket's autoassigned URL:
    pulumi.export('url', pulumi.Output.concat('http://', website.website_endpoint))
    
    // Export the bucket's autoassigned URL:
    ctx.Export("url", website.WebsiteEndpoint.ApplyT(func(websiteEndpoint string) (string, error) {
        return fmt.Sprintf("http://%v", websiteEndpoint), nil
    }).(pulumi.StringOutput))
    

    Also make sure you’ve imported fmt at the top of the file:

    import (
    	"fmt"
    
    	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/s3"
    	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
    )
    
    // Export the bucket's autoassigned URL:
    return new Dictionary<string, object?>
    {
        // ...
        ["url"] = website.WebsiteEndpoint.Apply(websiteEndpoint => $"http://{websiteEndpoint}"),
    };
    
    // Export the bucket's autoassigned URL:
    ctx.export("url", website.websiteEndpoint().applyValue(
        websiteEndpoint -> String.format("http://%s", websiteEndpoint)));
    
    # ...
    outputs:
      # ...
      url: http://${website.websiteEndpoint}
    
    # Export the bucket's autoassigned URL:
    output "url" {
      value = "http://${aws_s3_bucket_website_configuration.website.website_endpoint}"
    }
    

    The bucket’s website endpoint is an output property that AWS assigns at deployment time, so its value isn’t known in advance. For values like these, Pulumi has output-aware helpers that you can use to prepend plain strings like http://.

    Deploy the changes

    To deploy the changes, run pulumi up again. Pulumi figures out the deltas:

    $ pulumi up
    
    > pulumi up
    

    Just like the first time, you’ll see a preview of the changes before they happen:

    Previewing update (dev):
    
         Type                                    Name                 Plan       Info
         pulumi:pulumi:Stack                     quickstart-dev
     +   ├─ aws:s3:BucketWebsiteConfiguration    website              create
     +   ├─ aws:s3:BucketOwnershipControls       ownership-controls   create
     +   ├─ aws:s3:BucketPublicAccessBlock       public-access-block  create
     +   └─ aws:s3:BucketObject                  index.html           create
    
    Outputs:
      + url: output<string>
    
    Resources:
        + 4 to create
        2 unchanged
    
    Do you want to perform this update?
    > yes
      no
      details
    

    Choose yes to perform the deployment:

    Do you want to perform this update? yes
    Updating (dev):
    
         Type                                    Name                 Status              Info
         pulumi:pulumi:Stack                     quickstart-dev
     +   ├─ aws:s3:BucketWebsiteConfiguration    website              created (0.51s)
     +   ├─ aws:s3:BucketOwnershipControls       ownership-controls   created (0.84s)
     +   ├─ aws:s3:BucketPublicAccessBlock       public-access-block  created (1s)
     +   └─ aws:s3:BucketObject                  index.html           created (0.53s)
    
    Outputs:
      + url: "http://my-bucket-dfd6bd0.s3-website-us-east-1.amazonaws.com"
        bucketName    : "my-bucket-dfd6bd0"
    
    Resources:
        + 4 created
        2 unchanged
    
    Duration: 8s
    

    In a few seconds, your new website will be ready. Curl the endpoint to see it live:

    $ curl $(pulumi stack output url)
    
    > curl (pulumi stack output url)
    

    The response is your new website:

    <html>
        <body>
            <h1>Hello, Pulumi!</h1>
        </body>
    </html>
    

    Try experimenting, such as by changing the contents of index.html and redeploying.

    Next, wrap the website into an infrastructure abstraction.

      The infrastructure as code platform for any cloud.