Using Pulumi on GCP

If you haven’t already take a look at the previous post to get Pulumi up and running.

These steps are to get a better understanding of how to use Pulumi with Python and GCP.

I recommend creating a new project in GCP and using that project for demo purposes before going ahead with the rest of these steps.

That way you can simply delete the project later on.

I shall use a project called ‘throwaway-01’ in GCP as an example.

Create a throwaway project in GCP

Make sure you also associate a billing account with the project since you won’t be able to utilize all the resources on GCP without it.

gcloud projects create throwaway-01
gcloud alpha billing projects link throwaway-01 --billing-account=#######

Working with the Pulumi examples

Pulumi provides a lot of example projects available on their Github repository - https://github.com/pulumi/examples.git

This repository holds a multitude of projects with support for many Cloud providers; ranging from Azure to DigitalOcean.

The example below will carry out the following deployment.

  • Create a new Google Cloud compute instance
  • Create a new network
  • Create a new firewall
  • Install Nginx on the Google Cloud compute instance
git clone https://github.com/pulumi/examples.git
cd gcp-py-instance-nginx

You will find 4 files inside.

  1. main.py
  2. Pulumi.yaml
  3. README.md
  4. requirements.txt

Lets take a look at these files (I shall disregard the README.md and focus on the other files).

main.py

This is the Python code or as Pulumi calls it the ‘program’. In here you write the code to manage your cloud infrastructure.

Pulumi has its own API which you can use in your own programs.

 Note

I replaced ‘poc’ with ‘nginx’ below to make it more identifiable.

import pulumi
from pulumi_gcp import compute

script = "#!/bin/bash\nsudo touch /tmp/a.txt\nsudo yum install -y nginx\nsudo service nginx start"

addr = compute.address.Address("nginx")

network = compute.Network("nginx")

firewall = compute.Firewall(
    "nginx",
    network=network.self_link,
    allows=[
        {
            "protocol": "tcp",
            "ports": ["22"]
        },
        {
            "protocol": "tcp",
            "ports": ["80"]
        }
    ]
)


instance = compute.Instance(
    "nginx",
    name="nginx",
    machine_type="f1-micro",
    boot_disk={
        "initializeParams": {
            "image": "centos-cloud/centos-7-v20190116"
        }
    },
    network_interfaces=[
        {
            "network": network.id,
            "accessConfigs": [{
                "nat_ip": addr.address
            }]
        }
    ],
    metadata_startup_script=script,
)

# Export the DNS name of the bucket
pulumi.export("instance_name", instance.name)
pulumi.export("instance_network", instance.network_interfaces)
pulumi.export("external_ip", addr.address)

Pulumi.yaml

This defines the metadata of a project.

name: gcp-instance-nginx
runtime: python
description: Deploy a Nginx Server in a GCP instance

Notice the runtime binding to python.

requirements.txt

A list of dependency modules.

pulumi>=2.0.0,<3.0.0
pulumi-gcp>=3.0.0,<4.0.0

Running a deployment

The rest of the steps assume you are in the examples/gcp-py-instance-nginx directory.

 Warning

Before you begin make sure you have setup the following.

  • A service account in the project on GCP
  • Assign roles = { Compute Admin, Service Account User }
  • Saved the accounts' credential JSON locally
  • Python >= 3

Create a new stack

pulumi stack init gcp-py-instance-nginx
Created stack 'gcp-py-instance-nginx'

Open the Pulumi console - https://app.pulumi.com/

Where you’ll find the following stack.

Create Python virtual environment

python -m venv venv
source venv/bin/activate
pip install -r requirements.txt

Configure the project

export GOOGLE_PROJECT=<project name>
export GOOGLE_REGION=<region>
export GOOGLE_ZONE=<zone>
export GOOGLE_CREDENTIALS=<path to json credential file>

Deploy changes

pulumi up
Previewing update (gcp-py-instance-nginx):
        Type                     Name                                      Plan       
    +   pulumi:pulumi:Stack      gcp-instance-nginx-gcp-py-instance-nginx  create     
    +   ├─ gcp:compute:Network   nginx                                     create     
    +   ├─ gcp:compute:Address   nginx                                     create     
    +   ├─ gcp:compute:Firewall  nginx                                     create     
    +   └─ gcp:compute:Instance  nginx                                     create     
    
Resources:
    + 5 to create

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

Select yes to deploy changes.

You can monitor the deployment via the terminal or through the Pulumi console.

Updating (gcp-py-instance-nginx):
        Type                     Name                                      Status      
    +   pulumi:pulumi:Stack      gcp-instance-nginx-gcp-py-instance-nginx  created     
    +   ├─ gcp:compute:Network   nginx                                     created     
    +   ├─ gcp:compute:Address   nginx                                     created     
    +   ├─ gcp:compute:Firewall  nginx                                     created     
    +   └─ gcp:compute:Instance  nginx                                     created     
    
Outputs:
    external_ip     : "######"
    instance_name   : "nginx"
    instance_network: [
        [0]: {
            accessConfigs    : [
                [0]: {
                    natIp              : "########
                    network_tier       : "PREMIUM"
                }
            ]
            name             : "nic0"
            network          : "https://www.googleapis.com/compute/v1/projects/#####/global/networks/nginx-87752b8"
            networkIp        : "#####"
            subnetwork       : "https://www.googleapis.com/compute/v1/projects/#####/regions/#####/subnetworks/nginx-87752b8"
            subnetworkProject: "#####"
        }
    ]

Resources:
    + 5 created

Duration: 1m12s

Permalink: https://app.pulumi.com/###/gcp-instance-nginx/gcp-py-instance-nginx/updates/1

View stack output

pulumi stack output
Current stack outputs (3):
   OUTPUT            VALUE
   external_ip       #####
   instance_name     nginx
   instance_network [{accessConfigs":[{"natIp":"#####,"network_tier":"PREMIUM","publicPtrDomainName":""}],"aliasIpRanges":[],"name":"nic0","network":"https://www.googleapis.com/compute/v1/projects/####/global/networks/nginx-87752b8","networkIp":"###","subnetwork":"https://www.googleapis.com/compute/v1/projects/####/regions/#####/subnetworks/nginx-87752b8","subnetworkProject":"######"}]
   

You’ll notice the properties match the ones defined in the main.py

pulumi.export("instance_name", instance.name)
pulumi.export("instance_network", instance.network_interfaces)
pulumi.export("external_ip", addr.address)

Inspect the external IP

echo $(pulumi stack output external_ip)

Run a curl to test nginx is running

curl $(pulumi stack output external_ip)

######## prints out the nginx content ######
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
    <title>Welcome to CentOS</title>
    <style rel="stylesheet" type="text/css"> 

        html {
        background-image:url(img/html-background.png);
        background-color: white;
        font-family: "DejaVu Sans", "Liberation Sans", sans-serif;
        font-size: 0.85em;
        line-height: 1.25em;
        margin: 0 4% 0 4%;
        }
.....
##########

Destroy the resources deployed in the stack

pulumi destroy -y
Destroying (gcp-py-instance-nginx):
        Type                     Name                                      Status      
    -   pulumi:pulumi:Stack      gcp-instance-nginx-gcp-py-instance-nginx  deleted     
    -   ├─ gcp:compute:Firewall  nginx                                     deleted     
    -   ├─ gcp:compute:Instance  nginx                                     deleted     
    -   ├─ gcp:compute:Address   nginx                                     deleted     
    -   └─ gcp:compute:Network   nginx                                    deleted


The resources in the stack have been deleted, but the history and configuration associated with the stack are still maintained. 
If you want to remove the stack completely, run 'pulumi stack rm gcp-py-instance-nginx'.

Cleanup

Remove the stack

pulumi stack rm gcp-py-instance-nginx -y
Stack 'gcp-py-instance-nginx' has been removed!

Remove the Google Cloud project

gcloud projects delete throwaway-01 --quiet
Last updated on 23 Apr 2020
Published on 23 Apr 2020