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:
BucketObject: uploads your website content to the bucketBucketIAMBinding: 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.jsindex.ts__main__.pymain.goProgram.csProgram.fsProgram.vbApp.javaPulumi.yaml
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.jsindex.tsmain.pymain.goProgram.csProgram.fsProgram.vbApp.javaPulumi.yamlBucketObject 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.jsindex.tsmain.pymain.goProgram.csProgram.fsProgram.vbApp.javaPulumi.yamlBucketObject 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
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.
Thank you for your feedback!
If you have a question about how to use Pulumi, reach out in Community Slack.
Open an issue on GitHub to report a problem or suggest an improvement.
