Pulumi infrastructure as actual code!
Pulumi allows you to write code in your favourite language. It supports languages such as Python, Javascript, C# and more.
The obvious benefit is your team can use their language of choice to write IaC. Lets be frank, DSLs are never going to be same compared to the expressiveness of your programming language of choice.
Additional benefits can include:
- Using design patterns such as factories (If you want to abstract more, maybe if you are multi-cloud)
- Unit testing using Unit test frameworks you use for your other applications (NUnit, MSUnit, XUnit)
- Language features such as lambdas, loops, conditionals and more
Pre-requisites
In this example i am using my Mac OS. For further details on other OS please visit :https://www.pulumi.com/
To install pulumi on Mac:
brew install pulumi
I am using dotnet core so have already installed the dotnet core 3.0 runtime.
Make sure you have installed azure cli:https://www.pulumi.com/docs/intro/cloud-providers/azure/setup/
Creating your first project
In this example repo i created a folder called pulumi-dotnecore. To create a new pulumi project run the following command:
Note when you run this you wull be redirected to the Pulumi portal where you will have to sign up if you have not already.
Pulumi structure
The Pulumi cli will create a dotnet console project with the following structure:
-azureiac
-Program.cs
-Pulumi.dev.yaml
-Pulumi.yaml
-StorageAccountStack.cs
-StorageAccountConfig.cs
-UnitTest.cs
A word about state
As you are aware Terraform requires state management of the tfstate file. And if you are using ARM there is no state file as it is managed by the ARM deployment. Pulumi gives you the perception that it is similar to ARM when using the Pulumi service backend.
https://www.pulumi.com/docs/intro/concepts/how-pulumi-works/
However, this is not entirely true. Pulumi tracks state via a checkpoint. This defaults to automatic state management when starting with the Pulumi service backend. However, if that does not fit your requirement you can defer to a json state file locally or use a remote backend in your chosen cloud provider. Further info can be found here:
https://www.pulumi.com/docs/intro/concepts/state/
The Pulumi stage/environment file
The file named Pulumi.dev.yaml is essentially the equivalent of your ARM parameters file or your Terraform .tfvars file. This is the file where you define your environment values for a particular stage of deployment (dev, uat, prod)
The Pulumi API framework provides you access to these values in your code via the config API. The key line is the config.Require method call on the Config object below.
var config = new Config();
var storageAccount = new Account("storage", new AccountArgs
{
ResourceGroupName = resourceGroup.Name,
Location = resourceGroup.Location,
AccountReplicationType =
config.Require(StorageConfig.StorageReplication),
AccountTier =
config.Require(StorageConfig.StorageAccountTier),
EnableHttpsTrafficOnly = true
});
The Pulumi Stack
Pulumi works on the notion of infrastructure defined as a stack. For example in C# you define your resources in a file that inherits from the Stack parent class:
class StorageAccountStack : Stack
{
}
Within the constructor of the class you then define the key infrastructure components of your stack. In the above example we define a storage account and a resource group. To see the full class go to:
https://github.com/romeelk/pulumi-dotnetcore/blob/master/azureiac/StorageAccountStack.cs
This code is executed from your Program.cs file.
class Program
{
static Task<int> Main() => Deployment.RunAsync<StorageAccountStack>
}
Outputs
Both ARM and Terraform allow you declare outputs of your IaC deployments. Pulumi offers this construct using properties in C#. The way you define outputs is as follows:
[Output] public Output<string> ConnectionString { get; set; }
[Output] public Output<string> StorageUri { get; set; }
The above properties must be set in your Stack file.
The Pulumi inner loop
To deploy the above code Pulumi CLI offers a similar concept to Terraform plan and apply. Instead of plan you use Pulumi preview. To apply the changes you then use pulumi plan.
pulumi preview
Previewing update (dev)
View Live: https://app.pulumi.com/Khan/azureiac/dev/previews/8178ef36-8fee-4b6a-8026-3b53d6c53703
Type Name Plan
+ pulumi:pulumi:Stack azureiac-dev create
+ ├─ azure:core:ResourceGroup resourceGroup create
+ └─ azure:storage:Account storage create
Resources:
+ 3 to create
When you are happy then use pulumi up:
pulumi up
Previewing update (dev)
View Live: https://app.pulumi.com/Khan/azureiac/dev/previews/8ad865af-806f-476e-ae30-bc20c23da746
Type Name Plan
+ pulumi:pulumi:Stack azureiac-dev create
+ ├─ azure:core:ResourceGroup resourceGroup create
+ └─ azure:storage:Account storage create
Resources:
+ 3 to create
Do you want to perform this update? [Use arrows to move, enter to select, type to filter]
yes
> no
details
A word of note for Terraform
Terraform is actively bridging the gap from DSL to enabling programmers to use a programming language. This is currently through the Cloud Development Kit by Hashicorp.
https://www.hashicorp.com/blog/cdk-for-terraform-enabling-python-and-typescript-support
Summary
Pulumi is fairly new in the IaC tooling ecosystem compared to Terraform. Terraform is widely used across multi-cloud providers. It is actively contributed to by the Open source community.
In the next part I will explore some basic gotchas in terms of how resources are named by default (particularly resource groups), how to use it in a CI/CD automation tool.
I hope this was a useful and beneficial introduction to Pulumi.