Post

Pulumi in Azure - Part 1

Introduction

I’ve been using Terraform for a while now and heard good things about Pulumi. So I decided to give it a try!

And I thought… Why not share that experience.


Imperative vs Declarative

First things first.

If you’re not familiar with the difference between imperative and declarative languages, here’s a quick explanation:

An imperative language describes how you want to achieve it = step by step. Using languages such as Python, Java etc.

A declarative language describes what you want to achieve = the end result. Terraform HCL, Bicep and PowerShell DSC are good examples of this.

Let’s try to create a resource group and a storage account in Azure.

In an imperative language like PowerShell, you could execute something like this:

1
2
3
4
5
6
7
8
9
10
11
$rgName = "rg-demo-01"
$location = "West Europe"
$stName = "stbadeby01"

New-AzResourceGroup -Name $rgName -Location $location

New-AzStorageAccount -Name $stName `
          -ResourceGroupName $rgName `
          -Location $location `
          -SkuName Standard_GRS `
          -Kind StorageV2

In a declarative language (in this case Terraform/HCL) you would define the resources like this:

1
2
3
4
5
6
7
8
9
10
11
12
resource "azurerm_resource_group" "rg" {
  name     = "rg-demo-01"
  location = "West Europe"
}

resource "azurerm_storage_account" "staccount" {
  name                     = "stbadeby01"
  resource_group_name      = azurerm_resource_group.rg.name
  location                 = azurerm_resource_group.rg.location
  account_tier             = "Standard"
  account_replication_type = "GRS"
}

If we change the name of the resource group in the Terraform code, it will detect that the state in Azure is different from the code and therefore re-create the resource group - including the storage account using the updated names.


Difference between Pulumi and Terraform

Pulumi and Terraform are both tools used for Infrastructure as Code (IaC).
They’re both open source and free! (with a few limitations).

Terraform uses HCL which is their (HashiCorps) own declarative language.
Don’t confuse it with a programming language. Because it’s not.

Pulumi on the other hand supports the following languages:

  • TypeScript
  • Go
  • .NET
  • Python
  • Java
  • YAML

So there’s no need to learn a new language if you already know one of these - that’s a huge perk if you ask me.


What about Modules?

A great thing about IaC in general is the ability to re-use code. Terraform for example has a great way of doing this with modules - the same thing with Bicep and Nested Templates in ARM Templates.

What about Pulumi…? Well, Pulumi has modules too. But they’re called Component Resources (Packages), and work in a slightly different way.

I’ll cover that in a later post - stay tuned! 🦥


State file

Pulumi’s state is per default stored in their own hosted service (aka Pulumi Service), but it’s possible to host it yourself if you like.


Deploy a Pulumi project using Python

In the following example I will deploy my resources in Azure and use Python as language. It’s fairly easy to translate to other providers and languages if you like.


Install Pulumi and dependencies

Let’s start by installing Pulumi. I’ll be using homebrew for macOS.

1
brew install pulumi/tap/pulumi

Alternatively you can download the install files from here

Verify that you have Python 3.7 or later installed as well as pip.

1
2
python3 --version
pip3 --version

Output:
python3 --version
pip3 --version


Log in to Azure

To connect to your Azure account, use Azure CLI.

1
az login

If you don’t have Azure CLI installed, you can install it by following the instructions here.


Create a new Pulumi project

Make a new directory and create your pulumi project inside it.

You might be asked to authenticate to pulumi.

1
2
mkdir demo-project && cd demo-project
pulumi new azure-python

You will be asked for a project name, description and stack name. I will just leave it default for now.

The project will be created and all dependencies will be installed. step 1 A set of default files will be created:

  • __ main __.py
  • Pulumi.dev.yaml
  • Pulumi.yaml
  • requirements.txt


Configure the project

Edit the __ main __.py file to contain the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
"""Deploy a basic Pulumi project."""

import pulumi
from pulumi_azure_native import storage
from pulumi_azure_native import resources


# Variables
config = pulumi.Config()
tags = config.require_object("tags")

# Create an Azure Resource Group
rg = resources.ResourceGroup("resourceGroup",
    resource_group_name = config.require('resourceGroupName'),
    location=config.require('location'),
    tags = tags
)

# Create an Storage Account
st = storage.StorageAccount("storageAccount",
    account_name = config.require('storageAccountName'),
    resource_group_name = rg.name,
    location = rg.location,
    sku = storage.SkuArgs(
        name = storage.SkuName.STANDARD_LRS
        ),
    kind = storage.Kind.STORAGE_V2,
    tags = tags
)

After editing the main-file, edit the Pulumi.dev.yaml and insert the following:

1
2
3
4
5
6
7
8
config:
  azure-native:location: westeurope
  demo-project:location: westeurope
  demo-project:tags:
    environment: dev
    owner: PBA
  demo-project:resourceGroupName: rg-demo-01
  demo-project:storageAccountName: stbadeby01

We could have specified all the variables in the main-file, but I prefer to keep them in a separated yaml-file.

This way we can easily change the variables without having to edit the code. Also the ability to have configurations for different environments (prod, dev, test etc.) and use the same main-file.


Deploy the project

Now we’re ready to deploy the project!

Simply run pulumi up to deploy the project: step 2

Pulumi will output what it intends to do, and ask you to confirm the deployment.

After 27 seconds, the deployment is complete and you will see the following output: step 3

Aaaaaand it’s also deployed in Azure! 🙌 step 4

If we want to change the name of the resource group from rg-demo-01 to rg-dev-01, we can simply edit the Pulumi.dev.yaml file, change the value of demo-project:resourceGroupName to rg-demo-02 and run pulumi up again.

Because it’s declarative, Pulumi will know that the resource group needs to be renamed, and because you can’t rename a resource group in Azure, it will destroy the old one and create a new with the new name - including the storage account. step 5 step 6

If we want to destroy the resources, we simply run pulumi destroy and everything will be cleaned up.


Conclusion

I’ve been a Terraform advocate for a long time and it definitely has its place. But I’m really impressed with Pulumi and I think it’s a great alternative to Terraform. Getting started with Pulumi is a breeze, particularly if you are familiar with the programming languages that are supported natively.

That’s it for now… Part 2 will be published in a couple of weeks! 👊


Pulumi CLI Cheat Sheet

TypeSyntaxExample
List stackspulumi stack lspulumi stack ls
Create a new stackpulumi stack initpulumi stack init
Select a stackpulumi stack select [name]pulumi stack select pba-dev
Deploy selected stackpulumi uppulumi up
Deploy specific stackpulumi up --stack [name]pulumi up –stack pba-dev
Destroy selected stackpulumi destroypulumi destroy
Destroy specific stackpulumi destroy --stack [name]pulumi destroy –stack pba-dev
Set config valuepulumi config set [name]:[type] [value]pulumi config set azure-native:location westeurope