Get started with Pulumi & AWS
Pulumi’s infrastructure-as-code SDK helps you create, deploy, and manage AWS containers, serverless functions, and infrastructure using familiar programming languages.
This tutorial takes you through the steps to easily deploy a static website.
Install Pulumi
$ brew install pulumi/tap/pulumi
$ curl -fsSL https://get.pulumi.com | sh
> choco install pulumi
Or explore more installation options.
Install language runtime
Install Node.js.
Install Python version 3.7 or later. To reduce potential issues with setting up your Python environment on Windows or macOS, you should install Python through the official Python installer.
pip
is required to install dependencies. If you installed Python from source, with an installer from
python.org, or via Homebrew you should
already have pip
. If Python is installed using your OS package manager, you may have to install pip
separately, see
Installing pip/setuptools/wheel with Linux Package Managers. For example, on Debian/Ubuntu you must run sudo apt install python3-venv python3-pip
.
If you're having trouble setting up Python on your machine, see Python 3 Installation & Setup Guide for detailed installation instructions on various operating systems and distributions.
Install Go.
Pulumi requires a supported version of Go— this typically refers to the two most recent minor releases. If
you're using Linux, your distribution may not provide an up to date version of the Go compiler. To check what version of Go you have installed, use:
go version
.
Install .NET SDK.
Pulumi will need the dotnet
executable in order to build and run your Pulumi .NET application. Ensure that the dotnet
executable can be found
on your path after installation.
Install Java 11 or later and Apache Maven 3.6.1 or later.
Pulumi will need the java
, javac
, and mvn
executables in order to build and run your Pulumi Java application. Ensure that these
executables can be found on your path after installation.
Good news! You don’t have to install anything else to write Pulumi programs in YAML.
Configure Pulumi to access AWS
Pulumi requires cloud credentials to manage and provision resources. Use an IAM user account that has programmatic access with rights to deploy and manage resources.
If you have previously installed and configured the AWS CLI, Pulumi will respect and use your configuration settings.
If you do not have the AWS CLI installed or plan on using Pulumi from within a CI/CD pipeline, retrieve your access key ID and secret access key and then set the AWS_ACCESS_KEY_ID
and AWS_SECRET_ACCESS_KEY
environment variables on your workstation:
$ export AWS_ACCESS_KEY_ID=<YOUR_ACCESS_KEY_ID>
$ export AWS_SECRET_ACCESS_KEY=<YOUR_SECRET_ACCESS_KEY>
$ export AWS_ACCESS_KEY_ID=<YOUR_ACCESS_KEY_ID>
$ export AWS_SECRET_ACCESS_KEY=<YOUR_SECRET_ACCESS_KEY>
> $env:AWS_ACCESS_KEY_ID = "<YOUR_ACCESS_KEY_ID>"
> $env:AWS_SECRET_ACCESS_KEY = "<YOUR_SECRET_ACCESS_KEY>"
Create project
Let’s create your first Pulumi project, stack, and program. Pulumi projects and stacks organize Pulumi code. Projects are similar to GitHub repos and stacks are an instance of code with separate configuration. Projects can have multiple stacks for different development environments or for different cloud configurations.
$ mkdir quickstart && cd quickstart
$ pulumi new aws-javascript
$ mkdir quickstart && cd quickstart
$ pulumi new aws-typescript
$ mkdir quickstart && cd quickstart
$ pulumi new aws-python
$ mkdir quickstart && cd quickstart
$ pulumi new aws-go
$ mkdir quickstart && cd quickstart
$ pulumi new aws-csharp
$ mkdir quickstart && cd quickstart
$ pulumi new aws-java
$ mkdir quickstart && cd quickstart
$ pulumi new aws-yaml
The pulumi new
command creates a Pulumi project with basic scaffolding.
You will be asked for a project name and project description.
This command will walk you through creating a Pulumi project.
Enter a value or leave blank to accept the (default), and press <ENTER>.
Press ^C at any time to quit.
project name: (quickstart)
project description: (A minimal AWS Pulumi program)
Created project 'quickstart'
Then you will be asked for a stack name.
Please enter your desired stack name.
To create a stack in an organization, use the format <org-name>/<stack-name> (e.g. `acmecorp/dev`).
stack name: (dev)
Created stack 'dev'
Finally, you will be prompted for a configuration value for the stack, specifically the AWS region.
aws:region: The AWS region to deploy into: (us-west-2)
Saved config
After the command completes, the project and stack will be ready.
Review project
Let’s review some of the generated project files:
Pulumi.yaml
defines the project.
Pulumi.yaml
defines both the project and the program that manages your stack resources.
Pulumi.dev.yaml
contains configuration values for the stack you just initialized.
Program.cs
is the Pulumi program that defines your stack resources.
src/main/java/myproject
defines the project’s Java package root.
is the Pulumi program that defines your stack resources.index.js
index.ts
main.py
main.go
Program.cs
Program.fs
Program.vb
App.java
Pulumi.yaml
"use strict";
const pulumi = require("@pulumi/pulumi");
const aws = require("@pulumi/aws");
const awsx = require("@pulumi/awsx");
// Create an AWS resource (S3 Bucket)
const bucket = new aws.s3.Bucket("my-bucket");
// Export the name of the bucket
exports.bucketName = bucket.id;
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx";
// Create an AWS resource (S3 Bucket)
const bucket = new aws.s3.Bucket("my-bucket");
// Export the name of the bucket
export const bucketName = bucket.id;
import pulumi
from pulumi_aws import s3
# Create an AWS resource (S3 Bucket)
bucket = s3.Bucket('my-bucket')
# Export the name of the bucket
pulumi.export('bucket_name', bucket.id)
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v5/go/aws/s3"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
// Create an AWS resource (S3 Bucket)
bucket, err := s3.NewBucket(ctx, "my-bucket", nil)
if err != nil {
return err
}
// Export the name of the bucket
ctx.Export("bucketName", bucket.ID())
return nil
})
}
using Pulumi;
using Pulumi.Aws.S3;
using System.Collections.Generic;
return await Deployment.RunAsync(() =>
{
// Create an AWS resource (S3 Bucket)
var bucket = new Bucket("my-bucket");
// Export the name of the bucket
return new Dictionary<string, object?>
{
["bucketName"] = bucket.Id
};
});
package myproject;
import com.pulumi.Pulumi;
import com.pulumi.aws.s3.Bucket;
public class App {
public static void main(String[] args) {
Pulumi.run(ctx -> {
// Create an AWS resource (S3 Bucket)
var bucket = new Bucket("my-bucket");
// Export the name of the bucket
ctx.export("bucketName", bucket.bucket());
});
}
}
name: quickstart
runtime: yaml
description: A minimal AWS Pulumi YAML program
resources:
# Create an AWS resource (S3 Bucket)
my-bucket:
type: aws:s3:Bucket
outputs:
# Export the name of the bucket
bucketName: ${my-bucket.id}
exports.bucketName = bucket.id;
export const bucketName = bucket.id;
pulumi.export('bucket_name', bucket.id)
ctx.Export("bucketName", bucket.ID())
return new Dictionary<string, object?>
{
["bucketName"] = bucket.Id
};
ctx.export("bucketName", bucket.bucket());
outputs:
bucketName: ${my-bucket.id}
Deploy stack
Let’s deploy the stack:
$ pulumi up
This command evaluates the program and determines what resources need updates. A preview is shown that outlines the changes that will be made when you run the update:
Previewing update (dev):
Type Name Plan
+ pulumi:pulumi:Stack quickstart-dev create
+ └─ aws:s3:Bucket my-bucket create
Resources:
+ 2 to create
Do you want to perform this update?
> yes
no
details
Once the preview has finished choose yes
to create your new S3 bucket in AWS.
Do you want to perform this update? yes
Updating (dev):
Type Name Status
+ pulumi:pulumi:Stack quickstart-dev created (4s)
+ └─ aws:s3:Bucket my-bucket created (2s)
Outputs:
bucketName: "my-bucket-58ce361"
Resources:
+ 2 created
Duration: 5s
$ pulumi stack output bucketName
$ pulumi stack output bucketName
$ pulumi stack output bucket_name
$ pulumi stack output bucketName
$ pulumi stack output bucketName
$ pulumi stack output bucketName
$ pulumi stack output bucketName
Modify program
Now that your S3 bucket is provisioned, let’s add a file to it. In the project directory, create a new file called index.html
along with some content:
echo '<html>
<body>
<h1>Hello, Pulumi!</h1>
</body>
</html>' > index.html
echo '<html>
<body>
<h1>Hello, Pulumi!</h1>
</body>
</html>' > index.html
@"
<html>
<body>
<h1>Hello, Pulumi!</h1>
</body>
</html>
"@ | Out-File -FilePath index.html
Open the program and add this file to the S3 bucket. This uses Pulumi’s FileAsset
resource to assign the content of the file to a new BucketObject
:
In index.js
, create the BucketObject
right after creating the bucket itself:
// Create an S3 Bucket object
const bucketObject = new aws.s3.BucketObject("index.html", {
bucket: bucket.id,
source: new pulumi.asset.FileAsset("./index.html")
});
In index.ts
, create the BucketObject
right after creating the bucket itself:
// Create an S3 Bucket object
const bucketObject = new aws.s3.BucketObject("index.html", {
bucket: bucket.id,
source: new pulumi.asset.FileAsset("./index.html")
});
In __main__.py
, create a new bucket object by adding the following right after creating the bucket itself:
# Create an S3 Bucket object
bucketObject = s3.BucketObject(
'index.html',
bucket=bucket.id,
source=pulumi.FileAsset('./index.html')
)
In main.go
, create the BucketObject
right after creating the bucket itself:
// Create an S3 Bucket object
_, err = s3.NewBucketObject(ctx, "index.html", &s3.BucketObjectArgs{
Bucket: bucket.ID(),
Source: pulumi.NewFileAsset("./index.html"),
})
if err != nil {
return err
}
In Program.cs
, create a new BucketObject
right after creating the bucket itself.
// Create an S3 Bucket object
var bucketObject = new BucketObject("index.html", new BucketObjectArgs
{
Bucket = bucket.BucketName,
Source = new FileAsset("./index.html")
});
In index.js
index.ts
main.py
main.go
Program.cs
Program.fs
Program.vb
App.java
Pulumi.yaml
FileAsset
, BucketObject
, and BucketObjectArgs
classes, then create the BucketObject
right after creating the bucket itself.
package myproject;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.s3.Bucket;
import com.pulumi.aws.s3.BucketObject;
import com.pulumi.aws.s3.BucketObjectArgs;
import com.pulumi.asset.FileAsset;
public class App {
public static void main(String[] args) {
Pulumi.run(ctx -> {
// Create an AWS resource (S3 Bucket)
var bucket = new Bucket("my-bucket");
// Create an S3 Bucket object
new BucketObject("index.html", BucketObjectArgs.builder()
.bucket(bucket.id())
.source(new FileAsset("./index.html"))
.build()
);
// Export the name of the bucket
ctx.export("bucketName", bucket.bucket());
});
}
}
In index.js
index.ts
main.py
main.go
Program.cs
Program.fs
Program.vb
App.java
Pulumi.yaml
BucketObject
right below the bucket itself.
name: quickstart
runtime: yaml
description: A minimal AWS Pulumi YAML program
resources:
# Create an AWS resource (S3 Bucket)
my-bucket:
type: aws:s3:Bucket
# Create an S3 Bucket object
index.html:
type: aws:s3:BucketObject
properties:
bucket: ${my-bucket}
source:
fn::fileAsset: ./index.html
outputs:
# Export the name of the bucket
bucketName: ${my-bucket.id}
This bucket object is part of the Bucket
that we deployed earlier because we reference the bucket name in the properties of the bucket object.
We refer to this relationship as the BucketObject
being a child resource of the S3 Bucket
that is the parent resource.
Deploy changes
Deploy the changes by running pulumi up
again.
$ pulumi up
Pulumi will run the preview
step of the update, which computes the minimally disruptive change to achieve the desired state described by the program.
Previewing update (dev):
Type Name Plan
pulumi:pulumi:Stack quickstart-dev
+ └─ aws:s3:BucketObject index.html create
Resources:
+ 1 to create
2 unchanged
Do you want to perform this update?
> yes
no
details
Choose yes
to proceed with the update and upload the index.html
file to your bucket:
Do you want to perform this update? yes
Updating (dev):
Type Name Status
pulumi:pulumi:Stack quickstart-dev
+ └─ aws:s3:BucketObject index.html created (0.98s)
Outputs:
bucketName: "my-bucket-58ce361"
Resources:
+ 1 created
2 unchanged
Duration: 3s
Verify the object was created in your bucket by checking the AWS Console or by running the following AWS CLI command:
$ aws s3 ls $(pulumi stack output bucketName)
$ aws s3 ls $(pulumi stack output bucketName)
$ aws s3 ls $(pulumi stack output bucket_name)
$ aws s3 ls $(pulumi stack output bucketName)
$ aws s3 ls $(pulumi stack output bucketName)
$ aws s3 ls $(pulumi stack output bucketName)
$ aws s3 ls $(pulumi stack output bucketName)
Notice that your index.html
file has been added to the bucket:
2023-04-20 17:01:06 118 index.html
Modify program again
Update the Bucket
declaration to add a website
property and make index.html
the home page of the website:
const bucket = new aws.s3.Bucket("my-bucket", {
website: {
indexDocument: "index.html",
},
});
bucket = s3.Bucket("my-bucket",
website=s3.BucketWebsiteArgs(
index_document="index.html",
),
)
bucket, err := s3.NewBucket(ctx, "my-bucket", &s3.BucketArgs{
Website: &s3.BucketWebsiteArgs{
IndexDocument: pulumi.String("index.html"),
},
})
if err != nil {
return err
}
using Pulumi.Aws.S3.Inputs;
var bucket = new Bucket("my-bucket", new()
{
Website = new BucketWebsiteArgs
{
IndexDocument = "index.html",
},
});
import com.pulumi.aws.s3.BucketArgs;
import com.pulumi.aws.s3.inputs.BucketWebsiteArgs;
var bucket = new Bucket("my-bucket", BucketArgs.builder()
.website(BucketWebsiteArgs.builder()
.indexDocument("index.html")
.build())
.build());
my-bucket:
type: aws:s3:Bucket
properties:
website:
indexDocument: index.html
Lastly, you’ll make a few adjustments to make these resources accessible on the Internet.
For the bucket itself, you’ll need two new resources: a BucketOwnershipControls
resource, to define the bucket’s file-ownership settings, and a BucketPublicAccessBlock
resource to allow the bucket to be accessed publicly.
For the BucketObject
, you’ll need an access-control (ACL) setting of public-read
to allow the page to be accessed anonymously (e.g., in a browser) and a content type of text/html
to tell AWS to serve the file as a web page.
const ownershipControls = new aws.s3.BucketOwnershipControls("ownership-controls", {
bucket: bucket.id,
rule: {
objectOwnership: "ObjectWriter"
}
});
const publicAccessBlock = new aws.s3.BucketPublicAccessBlock("public-access-block", {
bucket: bucket.id,
blockPublicAcls: false,
});
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: publicAccessBlock });
ownership_controls = s3.BucketOwnershipControls(
'ownership-controls',
bucket=bucket.id,
rule=s3.BucketOwnershipControlsRuleArgs(
object_ownership='ObjectWriter',
),
)
public_access_block = s3.BucketPublicAccessBlock(
'public-access-block', bucket=bucket.id, block_public_acls=False
)
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=[public_access_block]),
)
_, err = s3.NewBucketOwnershipControls(ctx, "ownership-controls", &s3.BucketOwnershipControlsArgs{
Bucket: bucket.ID(),
Rule: &s3.BucketOwnershipControlsRuleArgs{
ObjectOwnership: pulumi.String("ObjectWriter"),
},
})
if err != nil {
return err
}
publicAccessBlock, err := s3.NewBucketPublicAccessBlock(ctx, "public-access-block", &s3.BucketPublicAccessBlockArgs{
Bucket: bucket.ID(),
BlockPublicAcls: pulumi.Bool(false),
})
if err != nil {
return err
}
_, 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{
publicAccessBlock,
}))
if err != nil {
return err
}
var ownershipControls = new BucketOwnershipControls("ownership-controls", new()
{
Bucket = bucket.Id,
Rule = new BucketOwnershipControlsRuleArgs
{
ObjectOwnership = "ObjectWriter",
},
});
var publicAccessBlock = new BucketPublicAccessBlock("public-access-block", new()
{
Bucket = bucket.Id,
BlockPublicAcls = false,
});
var indexHtml = new BucketObject("index.html", new()
{
Bucket = bucket.Id,
Source = new FileAsset("./index.html"),
ContentType = "text/html",
Acl = "public-read",
}, new CustomResourceOptions
{
DependsOn = new[]
{
publicAccessBlock,
},
});
import com.pulumi.aws.s3.BucketOwnershipControls;
import com.pulumi.aws.s3.BucketOwnershipControlsArgs;
import com.pulumi.aws.s3.inputs.BucketOwnershipControlsRuleArgs;
import com.pulumi.aws.s3.BucketPublicAccessBlock;
import com.pulumi.aws.s3.BucketPublicAccessBlockArgs;
import com.pulumi.resources.CustomResourceOptions;
import com.pulumi.asset.FileAsset;
var ownershipControls = new BucketOwnershipControls("ownershipControls", BucketOwnershipControlsArgs.builder()
.bucket(bucket.id())
.rule(BucketOwnershipControlsRuleArgs.builder()
.objectOwnership("ObjectWriter")
.build())
.build());
var publicAccessBlock = new BucketPublicAccessBlock("publicAccessBlock", BucketPublicAccessBlockArgs.builder()
.bucket(bucket.id())
.blockPublicAcls(false)
.build());
var indexHtml = new BucketObject("index.html", BucketObjectArgs.builder()
.bucket(bucket.id())
.source(new FileAsset("./index.html"))
.contentType("text/html")
.acl("public-read")
.build(), CustomResourceOptions.builder()
.dependsOn(publicAccessBlock)
.build());
ownership-controls:
type: aws:s3:BucketOwnershipControls
properties:
bucket: ${my-bucket.id}
rule:
objectOwnership: ObjectWriter
public-access-block:
type: aws:s3:BucketPublicAccessBlock
properties:
bucket: ${my-bucket.id}
blockPublicAcls: false
index.html:
type: aws:s3:BucketObject
properties:
bucket: ${my-bucket.id}
source: ${homepage}
contentType: text/html
acl: public-read
options:
dependsOn:
- ${public-access-block}
The BucketObject
includes the Pulumi resource option dependsOn
. This setting tells Pulumi that the BucketObject
relies indirectly on the BucketPublicAccessBlock
, which is responsible for enabling public access to its contents.
At the end of the program, export the bucket’s endpoint URL:
exports.bucketEndpoint = pulumi.interpolate`http://${bucket.websiteEndpoint}`;
export const bucketEndpoint = pulumi.interpolate`http://${bucket.websiteEndpoint}`;
pulumi.export('bucket_endpoint', pulumi.Output.concat('http://', bucket.website_endpoint))
ctx.Export("bucketEndpoint", bucket.WebsiteEndpoint.ApplyT(func(websiteEndpoint string) (string, error) {
return fmt.Sprintf("http://%v", websiteEndpoint), nil
}).(pulumi.StringOutput))
return new Dictionary<string, object?>
{
// ...
["bucketEndpoint"] = bucket.WebsiteEndpoint.Apply(websiteEndpoint => $"http://{websiteEndpoint}"),
};
// ...
ctx.export("bucketEndpoint", bucket.websiteEndpoint().applyValue(websiteEndpoint -> String.format("http://%s", websiteEndpoint)));
outputs:
# ...
bucketEndpoint: http://${my-bucket.websiteEndpoint}
Deploy website
Update your stack to deploy these changes to AWS:
$ pulumi up
Review the preview of the changes before deploying:
Previewing update (dev):
Type Name Plan Info
pulumi:pulumi:Stack quickstart-dev
~ ├─ aws:s3:Bucket my-bucket update [diff: +website]
+ ├─ aws:s3:BucketOwnershipControls ownership-controls create
+ ├─ aws:s3:BucketPublicAccessBlock public-access-block create
~ └─ aws:s3:BucketObject index.html update [diff: ~acl,contentType]
Outputs:
+ bucketEndpoint: output<string>
Resources:
+ 2 to create
~ 2 to update
4 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 Info
pulumi:pulumi:Stack quickstart-dev
~ ├─ aws:s3:Bucket my-bucket updated (3s) [diff: +website]
+ ├─ aws:s3:BucketOwnershipControls ownership-controls created (0.84s)
+ ├─ aws:s3:BucketPublicAccessBlock public-access-block created (1s)
~ └─ aws:s3:BucketObject index.html updated (0.53s) [diff: ~acl,contentType]
Outputs:
+ bucketEndpoint: "http://my-bucket-dfd6bd0.s3-website-us-east-1.amazonaws.com"
bucketName : "my-bucket-dfd6bd0"
Resources:
+ 2 created
~ 2 updated
4 changes. 1 unchanged
Duration: 8s
Check out your new website at the URL or make a curl
request:
$ curl $(pulumi stack output bucketEndpoint)
$ curl $(pulumi stack output bucketEndpoint)
$ curl $(pulumi stack output bucket_endpoint)
$ curl $(pulumi stack output bucketEndpoint)
$ curl $(pulumi stack output bucketEndpoint)
$ curl $(pulumi stack output bucketEndpoint)
$ curl $(pulumi stack output bucketEndpoint)
<html>
<body>
<h1>Hello, Pulumi!</h1>
</body>
</html>
Destroy stack
Now that you’ve seen how to deploy changes to our program, let’s clean up and tear down the resources that are part of your stack.
To destroy resources, run the following:
$ pulumi destroy
Previewing destroy (dev):
Type Name Plan
- pulumi:pulumi:Stack quickstart-dev delete
- ├─ aws:s3:BucketObject index.html delete
- ├─ aws:s3:BucketOwnershipControls ownership-controls delete
- ├─ aws:s3:BucketPublicAccessBlock public-access-block delete
- └─ aws:s3:Bucket my-bucket delete
Outputs:
- bucketEndpoint: "http://my-bucket-dfd6bd0.s3-website-us-east-1.amazonaws.com"
- bucketName : "my-bucket-dfd6bd0"
Resources:
- 5 to delete
Do you want to perform this destroy? yes
Destroying (dev):
Type Name Status
- pulumi:pulumi:Stack quickstart-dev deleted
- ├─ aws:s3:BucketObject index.html deleted (1s)
- ├─ aws:s3:BucketPublicAccessBlock public-access-block deleted (0.28s)
- ├─ aws:s3:BucketOwnershipControls ownership-controls deleted (0.47s)
- └─ aws:s3:Bucket my-bucket deleted (0.39s)
Outputs:
- bucketEndpoint: "http://my-bucket-dfd6bd0.s3-website-us-east-1.amazonaws.com"
- bucketName : "my-bucket-dfd6bd0"
Resources:
- 5 deleted
Duration: 4s
To delete the stack itself, run pulumi stack rm
. This removes the stack and the update history from Pulumi Cloud.
Next steps
Congrats! You’ve deployed your first project on AWS with Pulumi. Try a next step!
- Dive into Learn Pulumi for a comprehensive walkthrough of key Pulumi concepts in the context of a real-life application.
- Explore how-to guides: static websites, EC2 virtual machines, EKS clusters, Fargate containers, and serverless applications.
- Learn how Pulumi works from its architecture to key concepts, including stacks, state, configuration, and secrets.
- Read through the latest blog posts about using Pulumi with AWS.
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.