Skip to content

Commit d2f462d

Browse files
committed
Add the "Any provider with Pulumi" article
1 parent 8c6441f commit d2f462d

8 files changed

+187
-4
lines changed
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
---
2+
title: "Using Any Terraform Provider in Pulumi: A Guide with Netlify provider"
3+
lead: Manage Netlify Resources with Pulumi
4+
date: 2024-09-23
5+
image:
6+
src: /images/bridge_cloud_1.jpg
7+
badge:
8+
label: DevOps
9+
tags:
10+
- Pulumi
11+
- IaC
12+
- Netlify
13+
ImageAttribution: Picture of <a href="https://unsplash.com/fr/@modestasu">Modestas Urbonas</a> on <a href="https://unsplash.com/fr/photos/golden-gate-bridge-san-francisco-californie-vj_9l20fzj0">Unsplash</a>
14+
---
15+
16+
Pulumi recently announced a new feature that lets developers reuse [any Terraform or OpenTofu provider within a Pulumi program](https://www.pulumi.com/blog/any-terraform-provider/). In this article, we will explore this feature through a case study with [Netlify](https://www.netlify.com/).
17+
18+
## What are “Providers” for IaC tools?
19+
20+
Let’s first talk about what a provider is in the context of an infrastructure as code solution.
21+
22+
A provider is a plugin enabling an IaC tool to interact with infrastructure services (cloud providers, SaaS providers, or any other API). Specifically, a provider defines the resources and operations available on an infrastructure service and how to communicate with its API.
23+
24+
There are already many providers in the Terraform/OpenTofu ecosystem. Since a provider is just an abstraction over an API, a Terraform provider can be leveraged by other IaC tools. Instead of creating new providers for each infrastructure service, some IaC tools have found ways to generate their own providers from Terraform providers, allowing them to use the same resource model:
25+
26+
* [Crossplane](https://www.crossplane.io/) has a code generator framework called [Upjet](https://github.com/crossplane/upjet) to generate Crossplane providers from Terraform providers
27+
28+
* Pulumi can bridge Terraform provider thanks to [Pulumi Terraform Bridge](https://github.com/pulumi/pulumi-terraform-bridge)
29+
30+
That does not mean Pulumi or Crossplane use Terraform in their engines; they only use its providers to interact with some APIs. Apart from that, Pulumi and Crossplane have their ways of working, managing the state, and so on.
31+
32+
::callout{icon="i-heroicons-light-bulb"}
33+
You can check this [article](https://leebriggs.co.uk/blog/2021/11/06/pulumi-faqs) for additional information about Pulumi “using” Terraform providers.
34+
::
35+
36+
## About Pulumi Providers
37+
38+
Not all Pulumi providers are generated from Terraform providers. Some are “[native providers](https://www.pulumi.com/blog/pulumiup-native-providers/)” (like Azure Native, AWS Native, or Kubernetes providers) and offer significant benefits. For example, the [Azure Native provider](https://www.pulumi.com/registry/packages/azure-native/) is always up-to-date and has 100% API coverage. Therefore, using it instead of the Classic one bridged from the Terraform AzureRM provider is recommended.
39+
40+
::callout{icon="i-heroicons-light-bulb"}
41+
You can find the full list of native providers in the [Pulumi registry](https://www.pulumi.com/registry/) by filtering based on the provider type.
42+
::
43+
44+
Nevertheless, many providers in the Pulumi registry are providers bridged from Terraform providers. Some are maintained by Pulumi, some by vendors, and others by the community. Pulumi has created a boilerplate [repository](https://github.com/pulumi/pulumi-tf-provider-boilerplate) to help people build a new Pulumi provider that wraps an existing Terraform provider. However, there is still some maintenance work required, so not all Terraform providers are bridged and published in the Pulumi registry.
45+
46+
That's why Pulumi introduced support for generating full Pulumi SDKs locally for *any* Terraform provider. This not only solves the problem of missing Pulumi providers in the registry but also allows you to generate Pulumi SDKs for Terraform providers that are internal to your company.
47+
48+
That's it for the explanations. Now, let's see this new capability in action!
49+
50+
## Generate a Local Pulumi Package from the Netlify Terraform Provider
51+
52+
My blog is hosted on Netlify, and like many others, I manage it using the Netlify website. However, I realized it would be useful to have the infrastructure code to manage it as well. Even though my website is simple and doesn't require much, there are some site configurations and environment variables that would be better versioned in a code repository.
53+
54+
Unfortunately, there is currently no Netlify provider for Pulumi, but there is one for Terraform. Let's generate a local Pulumi SDK from it.
55+
56+
First, I can create a small Pulumi project in TypeScript (other languages would work too).
57+
58+
```bash
59+
pulumi new typescript
60+
```
61+
62+
Then, we can use the `pulumi package add` command to generate a TypeScript SDK from the Terraform Netlify provider.
63+
64+
```bash
65+
pulumi package add terraform-provider netlify/netlify
66+
```
67+
68+
::callout{icon="i-heroicons-chat-bubble-left-20-solid"}
69+
By default, this command uses the latest version of the Terraform Provider specified in the [OpenTofu registry](https://opentofu.org/docs/internals/provider-registry-protocol/). Alternatively, you can designate a different registry and select a different version to use.
70+
::
71+
72+
::callout{icon="i-heroicons-light-bulb"}
73+
If the Terraform provider you want to use to generate a Pulumi SDK is on your local file system, you can simply specify the path to the provider.
74+
::
75+
76+
![A terminal window displaying the process of adding the Netlify Terraform provider using the Pulumi package. The command `pulumi package add terraform-provider netlify/netlify` is used, followed by the generation of a Node.js SDK for the Netlify package. Instructions for adding the SDK to a Node.js project with the command `pnpm add netlify@file:sdk\netlify` and importing it in TypeScript code are also provided.](/posts/images/63.any-terraform-provider-with-pulumi_cli_1.png){.rounded-lg .mx-auto}
77+
78+
You can see that a `./sdks/netlify` folder has been created that contains the Pulumi TypeScript SDK for the Netlify provider.
79+
80+
![File directory structure displayed in a code editor. The root directory is named "AnyTerraformProvider," containing folders. Inside "sdks/netlify" there are various TypeScript files such as "deployKey.ts," "dnsRecord.ts," "dnsZone.ts," and others.](/posts/images/63.any-terraform-provider-with-pulumi_explorer.png){.rounded-lg .mx-auto}
81+
82+
We can then reference the dependency to this local package using the following command:
83+
84+
```bash
85+
pnpm add netlify@file:sdks\netlify
86+
```
87+
88+
Now, we can start creating the infrastructure code using the Netlify provider.
89+
90+
## Using the Netlify Provider
91+
92+
To use the Netlify provider, we need to provide the following configuration:
93+
94+
* A personal access token to authenticate with the Netlify API.
95+
96+
* The default team slug, which in our case is the team containing my blog website (note that this is not mandatory; we can also specify the team slug for each resource in the code that needs it).
97+
98+
99+
We can set this configuration using the following commands:
100+
101+
```bash
102+
pulumi config set netlify:token --secret xxxxxx
103+
pulumi config set netlify:defaultTeamSlug mynetlifyteam
104+
```
105+
106+
However, instead of doing this, I will use a [Pulumi ESC environment](https://www.pulumi.com/docs/esc/environments/) that I created with all the settings and secrets I need for my blog. This way, I can simply import this `blog/prod` environment into my configuration:
107+
108+
```bash
109+
pulumi config env add blog/prod
110+
```
111+
112+
My stack configuration file `Pulumi.dev.yaml` becomes short and simple:
113+
114+
```yaml [Pulumi.dev.yaml]
115+
environment:
116+
- blog/prod
117+
```
118+
119+
::callout{icon="i-heroicons-chat-bubble-left-20-solid"}
120+
Storing your configuration in the `Pulumi.dev.yaml` file is perfectly fine, you don’t have to use Pulumi ESC like I did. I just wanted to mention it here because that’s what I used and because I have been a big fan of [Pulumi ESC](https://www.pulumi.com/docs/esc/) since I discovered it. However, please be advised that Pulumi ESC is not free of charge (except for individuals, with some limitations).
121+
::
122+
123+
Since it’s a locally bridged provider, Pulumi does not have documentation for it on its registry yet (though it’s in Pulumi’s roadmap to [host static documentation for most used providers](https://github.com/pulumi/registry/issues/5302)). So we can rely on the documentation on the [HashiCorp Terraform Registry](https://registry.terraform.io/providers/netlify/netlify/latest/docs) (for some reason, the documentation is not available on the OpenTofu registry website). But, to be honest we don’t really need it because we have access to the SDK code with the documentation in it, and can rely on auto-completion to help us write the code. That’s the beauty of using programming languages for Infrastructure as Code.
124+
125+
The Netlify provider does not allow you to create a site; it only lets you retrieve existing sites and modify their configuration, domain, DNS, and environment variables. So, we can start by retrieving my blog site:
126+
127+
```typescript
128+
import * as netlify from "netlify";
129+
130+
const blog = netlify.getSiteOutput({
131+
name: "myawesomeblog"
132+
})
133+
134+
export const customDomain = blog.customDomain
135+
```
136+
137+
I added the `customDomain` output just to make sure the program was working correctly.
138+
139+
![Screenshot showing the output of a Pulumi stack. The details include the stack type as "pulumi:pulumi:Stack," the name as "AnyTerraformProvider-dev," and the status is not stated. The outputs section lists "blogDomain: 'techwatching.dev'." The resources section indicates "1 unchanged." The duration of the process is 12 seconds.](/posts/images/63.any-terraform-provider-with-pulumi_output_1.png){.rounded-lg .mx-auto}
140+
141+
My blog is a Nuxt application that uses the [Nuxt UI Pro](https://ui.nuxt.com/pro/getting-started) component library. For Netlify to be able to build the application, I have to set in Netlify the environment variable `NUXT_UI_PRO_LICENSE` with my license key. So, let’s do that.
142+
143+
My license key is already in my configuration (stored as a secret in my ESC `blog/prod` environment), so I can retrieve it like this:
144+
145+
```typescript
146+
const config = new Config()
147+
const nuxtUIKey = config.requireSecret("nuxt.UIProLicenseKey")
148+
```
149+
150+
Creating the environment variable is pretty straightforward:
151+
152+
```typescript
153+
new netlify.EnvironmentVariable("nuxtUIKeyEnvVar", {
154+
siteId: blog.id,
155+
key: "NUXT_UI_PRO_LICENSE",
156+
values: [{
157+
value: nuxtUIKey,
158+
context: "all"
159+
}]
160+
})
161+
```
162+
Because I used the requireSecret method on the config, Pulumi will treat the nuxtUIKey as a secret and encrypt it automatically in the state.
163+
164+
::callout{icon="i-heroicons-light-bulb"}
165+
There is also an option to manually indicate a property of a resource is a secret: I could have added additionalSecretOutputs option set to [“values”] (when declaring the environment variable) to ensure Pulumi encrypts this property on the resource. I don’t need to do it here because Pulumi automatically detect that nuxtUIKey is a secret.
166+
::
167+
168+
![Pulumi stack update summary showing the creation of a Netlify environment variable for the "AnyTerraformProvider-dev" stack. The status indicates that one resource was created and one remained unchanged, with a total duration of 8 seconds.](/posts/images/63.any-terraform-provider-with-pulumi_output_2.png){.rounded-lg .mx-auto}
169+
170+
Of course, I can configure many other things for my Netlify site, but I think this is enough to show Pulumi's support for using any Terraform provider.
171+
172+
## Summary
173+
174+
By allowing developers to generate local Pulumi SDKs from any Terraform provider, Pulumi addresses the issue of missing providers in its registry and enables the use of internal company-specific providers as well.
175+
176+
In this article, we have seen a practical application of this feature with a real-world example using the Netlify provider. It was impressive to see how easy it has become to use a Terraform provider from a Pulumi program. While most commonly used providers were already available, this feature opens up exciting possibilities for niche services or internal tools.
177+
178+
You can get more information about this feature on the [**Pulumi Terraform Bridge repository**](https://github.com/pulumi/pulumi-terraform-bridge/tree/master/dynamic). Don’t hesitate to experiment with it on your projects.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"zod": "^3.23.8"
3333
},
3434
"devDependencies": {
35+
"@iconify-json/vscode-icons": "^1.2.2",
3536
"@nuxt/eslint-config": "^0.5.6",
3637
"@nuxthq/studio": "^2.0.3",
3738
"@nuxtjs/seo": "2.0.0-rc.21",

pnpm-lock.yaml

Lines changed: 8 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/images/bridge_cloud_1.jpg

438 KB
Loading
53.6 KB
Loading
43.3 KB
Loading
46.4 KB
Loading
79.5 KB
Loading

0 commit comments

Comments
 (0)