Pulumi: Code Infrastructure As Code

PengB
5 min readDec 19, 2022

--

Photo by Yulia Matvienko on Unsplash

After posting my last blog Terraform “Zero Cloud”: how to create a GitLab Runner, I thought about writing a blog on its young brother Pulumi, another IaC tool. The first stable version of Pulumi is released in December 2017.

In the Blog Labs section, I will use Pulumi Python to run a docker container running a Super Mario Web Game.

If you are a Terraform user, you understand in 10 seconds that what is Pulumi:

  • Terraform and Pulumi are both using Declarative way.
  • Architecture is the same, they all use Providers to translate configuration files to API calls.
  • Like Terraform Registry, Pulumi has his own registry - Pulumi Registry, in which you find the Pulumi Providers.
  • Both depend on Resource and Date (get functions for Pulumi), both support Module
  • They manage State and support different local workspaces, called “stack” in Pulumi.
  • Similar Command lines.
  • 💡 Different in declarative language, HCL(mix of json and yaml) for Terraform and native programming language(Python, TypeScript, JavaScript, Go, C#, F#, Java, yaml) for Pulumi.

The main difference of the 2 tools are located in coding language. You can even convert Terraform script easily to Pulumi supported programming language.

So we have already Terraform, why Pulumi? It’s interesting in the tech world like in the real world, there are always the similar tools, and all existing for some raisons around business, marketing, customer, or even their foundators. That’s why we have Coca-cola and also Pepsi, AWS and also GCP/Azure, docker and podman etc.

Pulumi gives programmers a framework to do IaC with their favorite programming language!

To give you a first image on Pulumi and Terraform, we use python and docker provider.

Terraform example:

terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "2.23.1"
}
}
}

provider "docker" {
host = "unix:///var/run/docker.sock"
}

# Pulls the image
resource "docker_image" "supermario" {
name = "pengbai/docker-supermario:latest"
}

# Create a container
resource "docker_container" "supermario" {
image = docker_image.ubuntu.image_id
name = "supermario"
}

Pulumi Python example :

# install python pulumi docker provider package
➜ pip install pulumi_docker
# configure provider config
➜ pulumi config set docker:host unix:///var/run/docker.sock
import pulumi_docker as docker

image = docker.RemoteImage("supermario",
name='pengbai/docker-supermario:latest'
)

container = docker.Container("supermario",
image=image.image_id,
name = "supermario"
)

Blog Labs

Let’s run a Super Mario Web Game in docker container on my laptop to test Pulumi by Python!

Create project “pulumi_docker” by Python code:

➜  tree -a pulumi_docker
pulumi_docker
├── pulumi-backend
├── Pulumi.yaml
├── requirements.txt
└── __main__.py

Same as Terraform, Pulumi use backend to save state, here we use local backend in the pulumi-backend folder.

Let’s check the project configuration metadata Pulumi.yaml

name: docker-python
runtime: python
description: A pulumi app that run docker container on local laptop
backend:
url: file://./pulumi-backend
stackConfigDir: config/

It’s a python applicaion, we need dependencies lib and defined in requrement.txt

pulumi>=3.49.0
pulumi-docker>=3.6.1

Don’t forget to install these python lib by:

➜  pip install -r requirements.txt

In the main function we define the Resource managed by Pulumi:

import pulumi_docker as docker

# pull image
image = docker.RemoteImage("supermario",
name='pengbai/docker-supermario:latest', keep_locally=True
)

# run container
container = docker.Container("supermario",
image=image.image_id, name = "supermario",
ports=[{
"internal": 8080,
"external": 8600
}]
)

That’s all for the example!

Run Pulumi Cli

Before play pulumi commands. Ensure that the python requirements are installed.

Basic Pulumi commands:

  • pulumi stack init
  • pulumi config set [key] [value]
  • pulumi preview
  • pulumi up

Create stack dev, and create passphrase:

# create environment - pulumi stack
➜ pulumi stack init
stack name: (dev)
Created stack 'dev'
Enter your passphrase to protect config/secrets:
Re-enter your passphrase to confirm:

Check stack:

➜  pulumi stack ls
NAME LAST UPDATE RESOURCE COUNT
dev* n/a n/a

Add Docker Provider config:

➜  pulumi config set docker:host unix:///var/run/docker.sock

Run preview to check the changes, enter the passphrase created during the stack init:

➜  pulumi preview
Enter your passphrase to unlock config/secrets
(set PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILE to remember):
Previewing update (dev):
Type Name Plan
+ pulumi:pulumi:Stack docker-python-dev create
+ ├─ docker:index:RemoteImage supermario create
+ └─ docker:index:Container supermario create


Resources:
+ 3 to create

It rests only to run up:

➜  pulumi up
Enter your passphrase to unlock config/secrets
(set PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILE to remember):
Previewing update (dev):
Type Name Plan
+ pulumi:pulumi:Stack docker-python-dev create
+ ├─ docker:index:RemoteImage supermario create
+ └─ docker:index:Container supermario create


Resources:
+ 3 to create

Do you want to perform this update? details
+ pulumi:pulumi:Stack: (create)
[urn=urn:pulumi:dev::docker-python::pulumi:pulumi:Stack::docker-python-dev]
+ docker:index/remoteImage:RemoteImage: (create)
[urn=urn:pulumi:dev::docker-python::docker:index/remoteImage:RemoteImage::supermario]
[provider=urn:pulumi:dev::docker-python::pulumi:providers:docker::default_3_6_1::04da6b54-80e4-46f7-96ec-b56ff0331ba9]
keepLocally: true
name : "pengbai/docker-supermario"
+ docker:index/container:Container: (create)
[urn=urn:pulumi:dev::docker-python::docker:index/container:Container::supermario]
[provider=urn:pulumi:dev::docker-python::pulumi:providers:docker::default_3_6_1::04da6b54-80e4-46f7-96ec-b56ff0331ba9]
attach : false
containerReadRefreshTimeoutMilliseconds: 15000
image : output<string>
logs : false
mustRun : true
name : "supermario"
ports : [
[0]: {
external : 8600
internal : 8080
ip : "0.0.0.0"
protocol : "tcp"
}
]
readOnly : false
removeVolumes : true
restart : "no"
rm : false
start : true
stdinOpen : false
tty : false
wait : false
waitTimeout : 60

Do you want to perform this update? yes
Updating (dev):
Type Name Status
+ pulumi:pulumi:Stack docker-python-dev created (0.01s)
+ ├─ docker:index:RemoteImage supermario created (17s)
+ └─ docker:index:Container supermario created (3s)


Resources:
+ 3 created

Duration: 22s

check current stack state:

➜  pulumi stack
Enter your passphrase to unlock config/secrets
(set PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILE to remember):
Current stack is dev:
Managed by WL-GHB0GB3
Last updated: 29 minutes ago (2022-12-19 19:20:47.9362312 +0100 CET)
Pulumi version used: v3.49.0
Current stack resources (4):
TYPE NAME
pulumi:pulumi:Stack docker-python-dev
├─ docker:index/remoteImage:RemoteImage supermario
├─ docker:index/container:Container supermario
└─ pulumi:providers:docker default_3_6_1

Current stack outputs (0):
No output values currently in this stack

Use `pulumi stack select` to change stack; `pulumi stack ls` lists known ones

OK, now we have the docker image pulled and the container super-mario game running on my laptop:

➜  docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
pengbai/docker-supermario latest 077ed8c9236a 3 years ago 646MB

➜ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6c9812cd40c5 077ed8c9236a "catalina.sh run" 30 minutes ago Up 30 minutes 0.0.0.0:8600->8080/tcp supermario

Let’s check on navigator the localhost:8600:

Great! The game running in the container is ready!

Then you can remove all these resources by running “pulumi destroy”:

➜  pulumi destroy
Enter your passphrase to unlock config/secrets
(set PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILE to remember):
Previewing destroy (dev):
Type Name Plan
- pulumi:pulumi:Stack docker-python-dev delete
- ├─ docker:index:Container supermario delete
- └─ docker:index:RemoteImage supermario delete


Resources:
- 3 to delete

Do you want to perform this destroy? yes
Destroying (dev):
Type Name Status
- pulumi:pulumi:Stack docker-python-dev deleted
- ├─ docker:index:Container supermario deleted (0.45s)
- └─ docker:index:RemoteImage supermario deleted (0.00s)


Resources:
- 3 deleted

Duration: 1s

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`.

What do you think about the Pulumi? 😉

Happy Reading!

--

--

PengB

an IT guy @Adeo DevDataOps. Like nature and self-challenge. Former @WorldlineExpertCommunity.