Pulumi: Deploy Instrastructure as Code using your favorite programming language

6 min read | by Jordi Prats

Unlike Terraform that uses it's own language to deploy Instrastructure as Code (IaC), with Pulumi we can choose between any of the supported programming languages. For this tutorial we are going to use python deploying an object to a Kubernetes cluster

First we will have to install Pulumi as follows:

$ curl -fsSL https://get.pulumi.com | sh === Installing Pulumi v3.16.0 === + Downloading https://get.pulumi.com/releases/sdk/pulumi-v3.16.0-linux-x64.tar.gz... % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 61.3M 100 61.3M 0 0 32.2M 0 0:00:01 0:00:01 --:--:-- 32.1M + Extracting to /home/pet2cattle/.pulumi/bin + Adding $HOME/.pulumi/bin to $PATH in /home/pet2cattle/.bashrc === Pulumi is now installed! 🍹 === + Please restart your shell or add /home/pet2cattle/.pulumi/bin to your $PATH + Get started with Pulumi: https://www.pulumi.com/docs/quickstart 

With Pulumi we can choose between several backends to store it's state, being Pulumi's cloud service the default. We are going to use it for simplicity's sake, so we'll have to register using the following URL:

https://app.pulumi.com/account/tokens 

Once registered we'll have to login using the CLI and the access token we will be able to create once registered:

$ pulumi login Manage your Pulumi stacks by logging in. Run `pulumi login --help` for alternative login options. Enter your access token from https://app.pulumi.com/account/tokens  or hit <ENTER> to log in using your browser :    Welcome to Pulumi!  Pulumi helps you create, deploy, and manage infrastructure on any cloud using  your favorite language. You can get started today with Pulumi at:  https://www.pulumi.com/docs/get-started/  Tip of the day: Resources you create with Pulumi are given unique names (a randomly  generated suffix) by default. To learn more about auto-naming or customizing resource  names see https://www.pulumi.com/docs/intro/concepts/resources/#autonaming. Logged in to pulumi.com as jordiprats (https://app.pulumi.com/jordiprats) 

To be able to create the first python project using Pulumi we'll need to have installed the following dependencies:

  • python 3
  • python 3 venv

On Debian/Ubuntu we can just run:

sudo apt install python3.8 python3.8-venv 

At this point we are all set up to be able to init a new project using pulumi new. Since we want to have installed the dependencies to deploy to Kubernetes using Python we are going to use kubernetes-python. Using the -n flag we telling Pulumi how we want to name our new project:

$ pulumi new kubernetes-python -n simple-example This command will walk you through creating a new Pulumi project. Enter a value or leave blank to accept the (default), and press <ENTER>. Press ^C at any time to quit. project description: (A minimal Python Pulumi program)  Created project 'simple-example' 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' Creating virtual environment... Finished creating virtual environment (...) To perform an initial deployment, run 'pulumi up' 

By default we will have an example on how to push a Deployment, but as a first step we are going to create a Namespace using the following code:

import pulumi from pulumi_kubernetes.core.v1 import Namespace config = pulumi.Config() demoNS = Namespace("test1") pulumi.export("demoNS", demoNS.metadata.apply(lambda m: m.name)) 

This code tells pulumi to deploy a namespace tha it's resource name is going to be test1, we can check what is going to do using pulumi preview (which is equivalent to terraform plan)

$ pulumi preview Previewing update (dev) View Live: https://app.pulumi.com/jordiprats/simple-example/dev/previews/f08d0489-26fb-49aa-9054-1de352d2ea36  Type Name Plan   + pulumi:pulumi:Stack simple-example-dev create   + └─ kubernetes:core/v1:Namespace test1 create  Resources:  + 2 to create 

Using pulumi up it's going to apply it (equivalent to terraform apply)

$ pulumi up Previewing update (dev) View Live: https://app.pulumi.com/jordiprats/simple-example/dev/previews/3d81b12e-4933-4534-9823-6fe748ca7a88  Type Name Plan   + pulumi:pulumi:Stack simple-example-dev create   + └─ kubernetes:core/v1:Namespace test1 create  Resources:  + 2 to create Do you want to perform this update? yes Updating (dev) View Live: https://app.pulumi.com/jordiprats/simple-example/dev/updates/1  Type Name Status   + pulumi:pulumi:Stack simple-example-dev created   + └─ kubernetes:core/v1:Namespace test1 created  Outputs:  demoNS: "test1-78gjwsog" Resources:  + 2 created Duration: 6s 

If we check the list of namespaces we will see that it have created a namespace named after pulumi's resource but not exactly what we have called it:

$ kubectl get ns NAME STATUS AGE (...) test1-78gjwsog Active 23s $ kubectl describe ns test1-78gjwsog Name: test1-78gjwsog Labels: app.kubernetes.io/managed-by=pulumi  kubernetes.io/metadata.name=test1-78gjwsog Annotations: pulumi.com/autonamed: true Status: Active No resource quota. No LimitRange resource. 

We can destroy what we have just created by using pulumi destroy (this command is the same we would use with terraform)

$ pulumi destroy Previewing destroy (dev) View Live: https://app.pulumi.com/jordiprats/simple-example/dev/previews/62fdf19d-32af-493e-8e88-1d30bb7b5a34  Type Name Plan   - pulumi:pulumi:Stack simple-example-dev delete   - └─ kubernetes:core/v1:Namespace test1 delete  Outputs:  - demoNS: "test1-78gjwsog" Resources:  - 2 to delete Do you want to perform this destroy? yes Destroying (dev) View Live: https://app.pulumi.com/jordiprats/simple-example/dev/updates/2  Type Name Status   - pulumi:pulumi:Stack simple-example-dev deleted   - └─ kubernetes:core/v1:Namespace test1 deleted  Outputs:  - demoNS: "test1-78gjwsog" Resources:  - 2 deleted Duration: 11s 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 dev'. 

To be able to specify the exact name of the resource we will have to set it's metadata, just like we do with Kubernetes when we create the YAML definition of the object. The code that does it looks like this:

import pulumi from pulumi_kubernetes.core.v1 import Namespace config = pulumi.Config() demoNS = Namespace("test1", metadata={ "name": "test1" }) pulumi.export("demoNS", demoNS.metadata.apply(lambda m: m.name)) 

If we apply this code:

$ pulumi up Previewing update (dev) View Live: https://app.pulumi.com/jordiprats/simple-example/dev/previews/4402bdd0-45d6-4642-94d2-8d06e8a83982  Type Name Plan   + pulumi:pulumi:Stack simple-example-dev create   + └─ kubernetes:core/v1:Namespace test1 create  Resources:  + 2 to create Do you want to perform this update? yes Updating (dev) View Live: https://app.pulumi.com/jordiprats/simple-example/dev/updates/5  Type Name Status   + pulumi:pulumi:Stack simple-example-dev created   + └─ kubernetes:core/v1:Namespace test1 created  Outputs:  demoNS: "test1" Resources:  + 2 created Duration: 6s 

We will be able to see that now the namespace doesn't have an auto-generated name but the same exact name we have definied:

$ kubectl get ns NAME STATUS AGE (...) test1 Active 19s $ kubectl describe ns test1 Name: test1 Labels: app.kubernetes.io/managed-by=pulumi  kubernetes.io/metadata.name=test1 Annotations: <none> Status: Active No resource quota. No LimitRange resource. 

You can also find this example's code on this GitHub repo


Posted on 02/11/2021