1. Docs
  2. Infrastructure as Code
  3. Get Started
  4. Google Cloud
  5. Make an update

Get started with Pulumi and Google Cloud

    Make an update

    Now you will update your project to serve a static website out of your Google Cloud Storage bucket. You will change your code and then re-run pulumi up which will 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 as well as subsequent updates.

    To turn your bucket into a static website, you will add two new Google Cloud Storage resources:

    1. BucketObject: uploads your website content to the bucket
    2. BucketIAMBinding: makes the bucket publicly accessible

    Add an index.html

    First, from within your project directory, create a new index.html file with some content in it.

    cat <<EOT > index.html
    <html>
        <body>
            <h1>Hello, Pulumi!</h1>
        </body>
    </html>
    EOT
    
    cat <<EOT > index.html
    <html>
        <body>
            <h1>Hello, Pulumi!</h1>
        </body>
    </html>
    EOT
    
    @"
    <html>
      <body>
        <h1>Hello, Pulumi!</h1>
      </body>
    </html>
    "@ | Out-File -FilePath index.html
    

    Now that you have an index.html file with some content, open index.js index.ts __main__.py main.go Program.cs Program.fs Program.vb App.java Pulumi.yaml and modify it to add that file to your storage bucket.

    For this, you’ll use Pulumi’s FileAsset class to assign the content of the file to a new BucketObject.

    In index.ts, create the BucketObject by adding this code immediately following the bucket definition:

    // Upload the file
    const bucketObject = new gcp.storage.BucketObject("index.html", {
        bucket: bucket.name,
        name: "index.html",
        source: new pulumi.asset.FileAsset("index.html")
    });
    

    In __main__.py, create a new bucket object by adding this code immediately following the bucket definition:

    # Upload the file
    bucket_object = storage.BucketObject(
        "index.html",
        bucket=bucket.name,
        name="index.html",
        source=pulumi.FileAsset("index.html")
    )
    

    In main.go, create the BucketObject by adding this code immediately following the bucket definition:

    // Upload the file
    _, err = storage.NewBucketObject(ctx, "index.html", &storage.BucketObjectArgs{
        Bucket: bucket.Name,
        Name:   pulumi.String("index.html"),
        Source: pulumi.NewFileAsset("index.html"),
    })
    if err != nil {
        return err
    }
    

    In Program.cs, create the BucketObject by adding this code immediately following the bucket definition:

    // Upload the file
    var bucketObject = new BucketObject("index.html", new BucketObjectArgs
    {
        Bucket = bucket.Name,
        Name = "index.html",
        Source = new FileAsset("./index.html")
    });
    

    In index.js index.ts main.py main.go Program.cs Program.fs Program.vb App.java Pulumi.yaml , import the following additional classes, then create the BucketObject immediately following the bucket definition by adding this code:

    // ...
    import com.pulumi.asset.FileAsset;
    import com.pulumi.gcp.storage.BucketIAMBinding;
    import com.pulumi.gcp.storage.BucketIAMBindingArgs;
    import com.pulumi.gcp.storage.BucketObject;
    import com.pulumi.gcp.storage.BucketObjectArgs;
    
    public class App {
        public static void main(String[] args) {
            Pulumi.run(ctx -> {
                // Create a Google Cloud resource (Storage Bucket)
                var bucket = new Bucket("my-bucket", BucketArgs.builder()
                    // existing bucket configuration
                    .build());
    
                // Upload the file
                var bucketObject = new BucketObject("index.html", BucketObjectArgs.builder()
                    .bucket(bucket.name())
                    .name("index.html")
                    .source(new FileAsset("index.html"))
                    .build()
                );
    
                // ...
            });
        }
    }
    

    In index.js index.ts main.py main.go Program.cs Program.fs Program.vb App.java Pulumi.yaml , create the BucketObject right below the bucket itself.

    # Upload the file
    index-html:
        type: gcp:storage:BucketObject
        properties:
            bucket: ${my-bucket}
            name: index.html
            source:
                fn::fileAsset: ./index.html
    

    Notice how you provide the name of the bucket you created earlier as an input for the BucketObject. This tells Pulumi which bucket the object should live in.

    Below the BucketObject, add an IAM binding allowing the contents of the bucket to be viewed anonymously over the Internet:

    const bucketBinding = new gcp.storage.BucketIAMBinding("my-bucket-binding", {
        bucket: bucket.name,
        role: "roles/storage.objectViewer",
        members: ["allUsers"]
    });
    
    bucket_iam_binding = storage.BucketIAMBinding(
        "my-bucket-binding",
        bucket=bucket.name,
        role="roles/storage.objectViewer",
        members=["allUsers"],
    )
    
    _, err = storage.NewBucketIAMBinding(ctx, "my-bucket-binding", &storage.BucketIAMBindingArgs{
        Bucket: bucket.Name,
        Role:   pulumi.String("roles/storage.objectViewer"),
        Members: pulumi.StringArray{
            pulumi.String("allUsers"),
        },
    })
    if err != nil {
        return err
    }
    
    var bucketBinding = new BucketIAMBinding("my-bucket-binding", new BucketIAMBindingArgs
    {
        Bucket = bucket.Name,
        Role = "roles/storage.objectViewer",
        Members = new[]
        {
            "allUsers",
        },
    });
    
    var bucketBinding = new BucketIAMBinding("my-bucket-binding", BucketIAMBindingArgs.builder()
        .bucket(bucket.name())
        .role("roles/storage.objectViewer")
        .members("allUsers")
        .build());
    
    my-bucket-binding:
      type: gcp:storage:BucketIAMBinding
      properties:
        bucket: ${my-bucket.name}
        role: "roles/storage.objectViewer"
        members:
          - allUsers
    
    If you encounter permission errors when uploading files, the IAM binding may still be propagating. The component examples later in this tutorial show how to add explicit dependencies between resources to ensure proper ordering.

    Now that index.html exists in the bucket, modify the bucket to serve the file as a static website.

    To do that, update the bucket definition to configure its website property. Then, to align with Google Cloud Storage recommendations, set its uniform bucket-level access property to true:

    const bucket = new gcp.storage.Bucket("my-bucket", {
        location: "US",
        website: {
            mainPageSuffix: "index.html"
        },
        uniformBucketLevelAccess: true
    });
    

    Export the website URL

    Now to export the website’s public URL for easy access, add the url export as shown in this example:

    // Export the DNS name of the bucket
    export const bucketName = bucket.url;
    
    // Export the bucket's public URL
    export const url = pulumi.concat("http://storage.googleapis.com/", bucket.name, "/", bucketObject.name);
    
    bucket = storage.Bucket(
        "my-bucket",
        location="US",
        website={
            "main_page_suffix": "index.html"
        },
        uniform_bucket_level_access=True,
    )
    

    Export the website URL

    Now to export the website’s public URL for easy access, add the url export as shown in this example:

    # Export the DNS name of the bucket
    pulumi.export("bucket_name", bucket.url)
    
    # Export the bucket's public URL
    pulumi.export(
        "url",
        pulumi.Output.concat(
            "http://storage.googleapis.com/", bucket.id, "/", bucket_object.name
        ),
    )
    
    bucket, err := storage.NewBucket(ctx, "my-bucket", &storage.BucketArgs{
        Location: pulumi.String("US"),
        Website: storage.BucketWebsiteArgs{
            MainPageSuffix: pulumi.String("index.html"),
        },
        UniformBucketLevelAccess: pulumi.Bool(true),
    })
    if err != nil {
        return err
    }
    

    Export the website URL

    Now to export the website’s public URL for easy access, add the url export as shown in this example:

    // Export the DNS name of the bucket
    ctx.Export("bucketName", bucket.Url)
    
    // Export the bucket's public URL
    ctx.Export("url", pulumi.Sprintf("http://storage.googleapis.com/%s/%s", bucket.Name, bucketObject.Name))
    
    var bucket = new Bucket("my-bucket", new BucketArgs
    {
        Location = "US",
        Website = new Pulumi.Gcp.Storage.Inputs.BucketWebsiteArgs
        {
            MainPageSuffix = "index.html"
        },
        UniformBucketLevelAccess = true
    });
    

    Export the website URL

    Now to export the website’s public URL for easy access, add the url export to your return Dictionary as shown in this example:

    return new Dictionary<string, object?>
    {
        ["bucketName"] = bucket.Url,
        ["url"] = Output.Format($"http://storage.googleapis.com/{bucket.Name}/{bucketObject.Name}")
    };
    
    var bucket = new Bucket("my-bucket", BucketArgs.builder()
        .location("US")
        .website(BucketWebsiteArgs.builder()
            .mainPageSuffix("index.html")
            .build())
        .uniformBucketLevelAccess(true)
        .build());
    

    Export the website URL

    Now to export the website’s public URL for easy access, add the url export as shown in this example:

    // Export the DNS name of the bucket
    ctx.export("bucketName", bucket.url());
    
    // Export the bucket's public URL
    ctx.export("url", Output.format("http://storage.googleapis.com/%s/%s", bucket.name(), bucketObject.name()));
    
    resources:
      my-bucket:
        type: gcp:storage:Bucket
        properties:
          location: US
          website:
            mainPageSuffix: index.html
          uniformBucketLevelAccess: true
    
    outputs:
      bucketName: ${my-bucket.url}
      url: http://storage.googleapis.com/${my-bucket.name}/${index-html.name}
    

    We prepend http:// using a helper because the bucket’s URL is an output property that Google Cloud assigns at deployment time, not a raw string, meaning its value is not known in advance.

    Deploy the changes

    To deploy the changes, run pulumi up again and it will figure out the deltas:

    $ pulumi up
    
    > pulumi up
    

    Just like the first time you will see a preview of the changes before they happen:

    Previewing update (dev):
    
         Type                             Name               Plan
         pulumi:pulumi:Stack              quickstart-dev
     +   ├─ gcp:storage:BucketObject      index.html         create
     +   └─ gcp:storage:BucketIAMBinding  my-bucket-binding  create
     ~   └─ gcp:storage:Bucket            my-bucket          update
    
    Outputs:
      + url: "http://storage.googleapis.com/my-bucket-a2b3c4d/index.html"
    
    Resources:
        + 2 to create
        ~ 1 to update
        3 changes. 1 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
         pulumi:pulumi:Stack              quickstart-dev
     +   ├─ gcp:storage:BucketObject      index.html         created
     +   └─ gcp:storage:BucketIAMBinding  my-bucket-binding  created
     ~   └─ gcp:storage:Bucket            my-bucket          updated
    
    Outputs:
        bucketName: "gs://my-bucket-a2b3c4d"
      + url       : "http://storage.googleapis.com/my-bucket-a2b3c4d/index.html"
    
    Resources:
        + 2 created
        ~ 1 updated
        2 unchanged
    
    Duration: 8s
    

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

    $ curl $(pulumi stack output url)
    

    This will reveal your new website!

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

    Feel free to experiment, such as changing the contents of index.html and redeploying.

    Next, wrap the website into an infrastructure abstraction.

      Neo just got smarter about infrastructure policy automation