Use Pulumi on Google Cloud with YAML

1. Overview

This lab teaches you how to use Pulumi, an Infrastructure as Code tool to provision and manage Google Cloud resources.

What you will learn

In this lab, you will learn how to do the following:

  • Install and configure Pulumi
  • Write YAML program to model your infrastructure on Google Cloud
  • Provision and manage Cloud resources using Pulumi
  • Use pulumi convert to convert YAML program into python program

2. Setup and Requirements

Self-paced environment setup

  1. Sign-in to the Google Cloud Console and create a new project or reuse an existing one. If you don't already have a Gmail or Google Workspace account, you must create one.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • The Project name is the display name for this project's participants. It is a character string not used by Google APIs. You can update it at any time.
  • The Project ID is unique across all Google Cloud projects and is immutable (cannot be changed after it has been set). The Cloud Console auto-generates a unique string; usually you don't care what it is. In most codelabs, you'll need to reference the Project ID (it is typically identified as PROJECT_ID). If you don't like the generated ID, you may generate another random one. Alternatively, you can try your own and see if it's available. It cannot be changed after this step and will remain for the duration of the project.
  • For your information, there is a third value, a Project Number which some APIs use. Learn more about all three of these values in the documentation.
  1. Next, you'll need to enable billing in the Cloud Console to use Cloud resources/APIs. Running through this codelab shouldn't cost much, if anything at all. To shut down resources so you don't incur billing beyond this tutorial, you can delete the resources you created or delete the whole project. New users of Google Cloud are eligible for the $300 USD Free Trial program.

3. Infrastructure setup

Install and Configure Pulumi

In Cloud Shell, run the following command to install Pulumi

curl -fsSL https://get.pulumi.com | sh

Add Pulumi to the path and view the help message from Pulumi

export PATH=${PATH}:~/.pulumi/bin
# view the help message to verify pulumi runs
pulumi -h

Run the following commands to set the project ID and authorize the access. You need to follow the instructions given by the commands

export PROJECT_ID=$(gcloud config get-value project)
gcloud auth application-default login

In Cloud Shell, create a GCS bucket and use it as backend

gsutil mb gs://pulumi-${PROJECT_ID}
pulumi login gs://pulumi-${PROJECT_ID}

Create a new project

In Cloud Shell, create the project root directory

mkdir pulumi-lab && cd pulumi-lab

Define project file(the entry point to Pulumi)

cat <<EOT > Pulumi.yaml
name: pulumi-lab
description: Try Pulumi

runtime: yaml
main: yaml-repo/
EOT

Define YAML resources

Create directory to hold cloud resource definitions in yaml format

mkdir yaml-repo

Create file yaml-repo/Pulumi.yaml with the following resource definitions

  1. Bucket
  2. IAM Binding
  3. A text object with the string "Hello World!"
  4. And some outputs
resources:
  # Create a GCP resource (Storage Bucket)
  my-bucket:
    type: gcp:storage:Bucket
    properties:
      location: US
      website:
        mainPageSuffix: index.html
      uniformBucketLevelAccess: true

  my-bucket-binding:
    type: gcp:storage:BucketIAMBinding
    properties:
      bucket: ${my-bucket.name}
      role: "roles/storage.objectViewer"
      members: ["allUsers"]

  index-object:
    type: gcp:storage:BucketObject
    properties:
      bucket: ${my-bucket}
      source:
        fn::stringAsset: Hello World!

outputs:
  bucketName: ${my-bucket.url}

Deploy the resources

Initialize and config stack

export PULUMI_CONFIG_PASSPHRASE=pulumi-lab
pulumi stack init dev
pulumi config set gcp:project $PROJECT_ID

Check stack config and you should see the key gcp:project with your project Id as the value

pulumi config

At this point the directory structure should look like the following

├── Pulumi.dev.yaml
├── Pulumi.yaml
└── yaml-repo
    └── Pulumi.yaml

Deploy the stack

pulumi up

This command evaluates your program and determines the resource updates to make. First, a preview is shown that outlines the changes that will be made when you the command

(Output)

Previewing update (dev):
Downloading plugin gcp v6.44.0: 45.69 MiB / 45.69 MiB [=============] 100.00% 1s
     Type                             Name               Plan
 +   pulumi:pulumi:Stack              pulumi-lab-dev     create
 +   ├─ gcp:storage:Bucket            my-bucket          create
 +   ├─ gcp:storage:BucketObject      index-object       create
 +   └─ gcp:storage:BucketIAMBinding  my-bucket-binding  create


Outputs:
    bucketName: output<string>

Resources:
    + 4 to create

Do you want to perform this update?  [Use arrows to move, type to filter]
  yes
> no
  details

Select yes and the resources will be provisioned. Your output should look like this

Do you want to perform this update? yes
Updating (dev):
     Type                             Name               Status
 +   pulumi:pulumi:Stack              pulumi-lab-dev     created (3s)
 +   ├─ gcp:storage:Bucket            my-bucket          created (1s)
 +   ├─ gcp:storage:BucketObject      index-object       created (0.78s)
 +   └─ gcp:storage:BucketIAMBinding  my-bucket-binding  created (5s)


Outputs:
    bucketName: "gs://my-bucket-874aa08"

Resources:
    + 4 created

Duration: 11s

Running the following command will print outputs that were defined

pulumi stack output

Run the following command to verify the change

gsutil ls $(pulumi stack output bucketName)

Your output will look like this

(output)

gs://my-bucket-11a9046/index-object-77a5d80

4. Convert YAML to Python

Let's convert the above example to a Pulumi Python program

pulumi convert --language python --out ./py-repo

Inspect the code that was generated in py-repo

cat py-repo/__main__.py

(output)

import pulumi
import pulumi_gcp as gcp

my_bucket = gcp.storage.Bucket("my-bucket",
    location="US",
    website=gcp.storage.BucketWebsiteArgs(
        main_page_suffix="index.html",
    ),
    uniform_bucket_level_access=True)
my_bucket_binding = gcp.storage.BucketIAMBinding("my-bucket-binding",
    bucket=my_bucket.name,
    role="roles/storage.objectViewer",
    members=["allUsers"])
index_object = gcp.storage.BucketObject("index-object",
    bucket=my_bucket.id,
    source=pulumi.StringAsset("Hello World!"))
pulumi.export("bucketName", my_bucket.url)
.......

Activate Python virtual environment

source py-repo/bin/activate

Update Pulumi.yaml project file to point to python program. Notice the runtime and the main entry have been changed

cat <<EOT > Pulumi.yaml
name: pulumi-lab
description: Try Pulumi

runtime: python
main: py-repo/
EOT

Try to redeploy the stack and select yes

pulumi up

There should not be any changes and your output should look similar to this

(output)

Previewing update (dev):
     Type                 Name            Plan
     pulumi:pulumi:Stack  pulumi-lab-dev


Resources:
    4 unchanged

Do you want to perform this update? yes
Updating (dev):
     Type                 Name            Status
     pulumi:pulumi:Stack  pulumi-lab-dev


Outputs:
    bucketName: "gs://my-bucket-c2b49ad"

Resources:
    4 unchanged

Duration: 6s

5. Delete the resources

Delete the resources that were created

pulumi destroy

Your confirmation will look like this

Previewing update (dev):
     Type                 Name            Plan
     pulumi:pulumi:Stack  pulumi-lab-dev


Resources:
    4 unchanged

Do you want to perform this update?  [Use arrows to move, type to filter]
  yes
> no
  details


Do you want to perform this destroy? yes
Destroying (dev):
     Type                             Name               Status
 -   pulumi:pulumi:Stack              pulumi-lab-dev     deleted
 -   ├─ gcp:storage:BucketIAMBinding  my-bucket-binding  deleted (5s)
 -   ├─ gcp:storage:BucketObject      index-object       deleted (1s)
 -   └─ gcp:storage:Bucket            my-bucket          deleted (0.73s)


Outputs:
  - bucketName: "gs://my-bucket-874aa08"

Resources:
    - 4 deleted

Duration: 10s

6. Congratulations!

Congratulations, you finished the lab!