Pulumi Deep Dive - Benefits, Challenges and Pain Points

May 10, 2024

deep dive

In consulting, we always have the challenge of adapting to our clients' requirements. My first project required using Pulumi, but nobody had any experience with it.

After spending over a year utilizing Pulumi, I decided to share my insights by delivering my first tech talk on the topic. Held at the AWS User Group in Hamburg, the presentation covered the basics of Pulumi, its practical application, and some advanced concepts. This blog post serves as a summary of the key points discussed during the talk.

Key Features of Pulumi

Language Support

Pulumi supports multiple programming languages, including Python, JavaScript, Java, Go, and YAML, allowing teams to leverage their existing skills and preferences.

State Management

You may choose between Pulumi’s as a service backend (“Pulumi cloud”) or self-managed backends, such as storing state in an S3 bucket or a local file, providing flexibility and control over state storage.

Provider Ecosystem

With providers available for major cloud platforms like AWS, GCP, and Azure, as well as popular tools like GitHub, Slack, and Okta, Pulumi empowers users to manage a wide range of resources and services.

Project and Stack Management

Organize infrastructure code into projects (= system components) and stacks (= environments). With its configuration for environment-specific values (e.g., API keys and role ARNs), Pulumi helps you to achieve environment isolation.

Example Code: Creating and Connecting Resources in Pulumi

Let’s dive into an example code snippet demonstrating a common use case in IaC: how to create a role with a policy in Pulumi:

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const assumeRole = aws.iam.getPolicyDocument ({
  statements: [{
    effect: "Allow", 
    principals: [{
      type: "Service",
      identifiers: ["ec2.amazonaws.com"],
    }],
    actions: ["sts:AssumeRole"],
  }],
});

const role = new aws.iam.Role( "role", {
  name: "test-role",
  assumeRolePolicy: assumeRole.then(assumeRole =>
      assumeRole.json
  ),
});

const policyDoc = aws.iam.getPolicyDocument({
  statements: [{
    effect: "Allow", 
    actions: ["ec2:Describe*"], 
    resources: ["*"],
  }],
});

const policy = new aws. iam.Policy( "policy", {
  name: "test-policy",
  description: "A test policy",
  policy: policyDoc.then(policy => policy.json),
});

new aws.iam.RolePolicyAttachment( "test-attach", {
  role: role.name,
  policyArn: policy.arn,
});

As you can see, you need to define every single resource in pulumi and connect them (policies, role, role-policy-attachment). Basically, this looks like terraform code using a programming language. However, CDK fans might miss the simplicity of built-in CDK-functions like grant.

Advanced Concepts in Pulumi

Component Resources

Component resources allow grouping related resources with default values, enhancing code organization and reusability. Advanced usage involves bundling resources into external files and dynamically referencing them in the main entrypoint (index-file).

Dynamic Providers

Pulumi allows for the creation of custom providers, extending its functionality to meet specific use cases or integrate with external systems not covered by built-in providers.

Cross Guards

Pulumi offers tools called “resource policies” and “stack policies” for implementing access control and governance. Resource policies are checked for individual resources before they are created or updated, while stack policies are applied to all resources in the stack and are checked after the deployment or update. There are predefined policy packs available, but you can also create your own policies. Sharing these policy files across repositories can get messy, especially with self-managed backends, where you end up duplicating the policy code across projects. However, with Pulumi Cloud it’s easier since you can attach policies directly to it, and it’s used for all deployments across the organization.

Comparison with CDK and Terraform

While Pulumi shares similarities with both Terraform and AWS CDK, there are notable differences:

Unlike Terraform, Pulumi does not automatically update resource states before deployment, but users can manually trigger a state refresh using the pulumi refresh command.

Compared to CDK, Pulumi offers broader language support and flexible backend options. However, CDK’s built-in functions like grant for managing permissions and CloudFormation’s built-in state management provide conveniences that streamline certain aspects of infrastructure management.

Conclusion

To sum up, using Pulumi can be pretty easy once you’re used to it. However, unexpected problems may pop up, and finding help online can be tough because there aren’t as many people using it compared to other tools.

If you’re dealing with services across different cloud platforms like AWS and GCP, Pulumi or Terraform could be useful. But if I had to pick, I’d go with Pulumi because it lets you use programming languages, which makes things more flexible and easier to understand (e.g., using if-statements or loops).

For projects mainly on AWS, CDK is a good choice. It helps with security and you don’t have to worry about managing the state of your infrastructure. Plus, since it’s backed by AWS and has a bigger community, you can find more support and resources.

In the end, whether you pick Pulumi, Terraform, or CDK depends on what your project needs. Each tool has its own strengths, so you can choose the one that works best for you.

photo of Anne

Anne is a Cloud Consultant at superluminar. With her passion for software development and everything to do with the cloud, she is always striving to learn more about the latest technologies and trends and to expand her skills. In this blog, she shares her insights and her knowledge on AWS-specific topics.