Le savoir n'a guère d'intérêt s'il n'est pas partagé.

L'infrastructure en tant que code (IaC) a transformé la gestion informatique, et Terraform d'HashiCorp a été un pilier dans ce domaine.

Mais que se passerait-il si vous pouviez combiner la puissance des providers de Terraform avec la flexibilité des langages de programmation populaires comme Python et JavaScript ?

C'est précisément ce que propose Pulumi et le Pulumi Terraform Bridge. 🙏

Vous avez été relativement nombreux/ses à vouloir un article sur ce sujet, c'est maintenant chose faite.

Avant de commencer, je souhaiterais préciser qu'il vous faudra quelques connaissances en Go pour y arriver sans trop de difficulté. 🫡

C'est parti, allons-y !

Récupération des prérequis 😄

Vous devez, avant de commencer avoir en votre possession :

  • GNU Make (pour le Makefile)
  • Golang (vers 1.18 ou supérieur)
  • Goreleaser
  • Git (obviously).

Et le "Terraform Bridge Provider Boilerplate" : https://github.com/pulumi/pulumi-tf-provider-boilerplate

Ce projet doit être forké chez vous, en respectant le format suivant :

pulumi-<provider_name>

Ce qui donne par exemple :

pulumi-maas

Ni plus, ni moins ! Ne reste plus qu'à cloner le repo chez vous :

git clone https://github.com/juhnny5/pulumi-maas.git
cd pulumi-maas/

Initialiser le repo

Le boilerplate, comme son nom l'indique fournit une base qu'il va falloir un peu adapter par rapport à notre provider à créer.

Pour ce faire, nous allons utiliser la commande suivante :

make prepare NAME=maas REPOSITORY=github.com/juhnny5/pulumi-maas

Cette commande va venir modifier les différents fichiers nécessaires au build et à la transformation, notamment le go.mod par exemple.

Elle va également générer les ressources qui vont nous être utiles par la suite :

  • provider/cmd/pulumi-resource-maas
  • provider/cmd/pulumi-tfgen-maas
  • provider/resources.go

Cette commande modifie pas mal de choses, mais, dans mon cas, je vais chercher à convertir un provider qui n'est pas dans le repo officiel de Terraform. En effet, le provider de MaaS est sur le repo de l'orga du même nom, à savoir :

GitHub - maas/terraform-provider-maas: Terraform MAAS provider
Terraform MAAS provider. Contribute to maas/terraform-provider-maas development by creating an account on GitHub.

Ainsi, dans le fichier Makefile, il me suffira de modifier les variables de cette sorte :

PROJECT_NAME := maas Package

SHELL            := /bin/bash
PACK             := maas
ORG              := juhnny5
PROJECT          := github.com/${ORG}/pulumi-${PACK}
NODE_MODULE_NAME := @juhnny5/${PACK}
TF_NAME          := ${PACK}
PROVIDER_PATH    := provider
VERSION_PATH     := ${PROVIDER_PATH}/pkg/version.Version

TFGEN           := pulumi-tfgen-${PACK}
PROVIDER        := pulumi-resource-${PACK}
VERSION         := $(shell pulumictl get version)

TESTPARALLELISM := 4

WORKING_DIR     := $(shell pwd)

OS := $(shell uname)
EMPTY_TO_AVOID_SED := ""

Dans mon exemple, j'utilise un provider Terraform en source qui est un peu particulier. En effet, si on regarde le go.mod du projet, celui-ci, on se rend compte que le nom du projet n'est pas le FQDN du projet sur GitHub mais simplement le nom du repo.

module terraform-provider-maas

go 1.19

require (
	github.com/bflad/tfproviderlint v0.28.1
	github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320
	github.com/hashicorp/terraform-plugin-docs v0.16.0
	github.com/hashicorp/terraform-plugin-sdk/v2 v2.27.0
	github.com/maas/gomaasclient v0.0.0-20230512141257-d73401ee0dc8
	github.com/stretchr/testify v1.8.4
)

Ainsi, côté boilerplate, il faudra modifier notre go.mod également pour que nous puissions utiliser cette lib en entrée. Ainsi donc :

vi provider/go.mod

On va remplacer le nom en spécifiant la version souhaitée. 🙃

module github.com/juhnny5/pulumi-maas/provider

go 1.19

replace (
	github.com/hashicorp/terraform-plugin-sdk/v2 => github.com/pulumi/terraform-plugin-sdk/v2 v2.0.0-20230710100801-03a71d0fca3d
	terraform-provider-maas v1.2.0 => github.com/maas/terraform-provider-maas v1.2.0
)

require (
	github.com/pulumi/pulumi-terraform-bridge/v3 v3.57.0
	github.com/pulumi/pulumi/sdk/v3 v3.76.1
	terraform-provider-maas v1.2.0
)

Ainsi, nous pourrons utiliser le nom terraform-provider-maas dans notre code. A noter que cette subtilité n'est pas toujours présente, cette manipulation est à réaliser dans le cas ou le provider source ne spécifie pas le FQDN Github dans son go.mod.

Maintenant, il suffit de rajouter la dépendance vers ce provider Terraform dans notre fichier de resources. 🫡

Pour ce faire :

vi provider/resources.go

Et y ajouter terraform-provider-maas/maas, de cette sorte :

package maas

import (
	"fmt"
	"path/filepath"

	"terraform-provider-maas/maas"

	"github.com/juhnny5/pulumi-maas/provider/pkg/version"
	"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge"
	"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge/tokens"
	shim "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim"
	shimv2 "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/sdk-v2"
	"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
)

Un coup de tfgen 🥸

Pour continuer, il faut lancer la commande :

make tfgen

Modifier les infos du futur provider Pulumi 👀

Par défaut, lorsque vous avez fait initialisé le projet, le fichier resources.go a ajouté plusieurs infos concernant le projet.  😋

Nous allons y modifier d'autres infos avant de générer les SDKs. Le nom du Publisher :

	prov := tfbridge.ProviderInfo{
		P:    p,
		Name: "maas",
		// DisplayName is a way to be able to change the casing of the provider
		// name when being displayed on the Pulumi registry
		DisplayName: "MAAS",
		// The default publisher for all packages is Pulumi.
		// Change this to your personal name (or a company name) that you
		// would like to be shown in the Pulumi Registry if this package is published
		// there.
		Publisher: "Julien Briault",
provider/resources.go

Mais également la description et l'org Github si celle-ci n'a pas été correctement modifiée.

Description:       "A Pulumi package for creating and managing Canonical Metal-As-A-Service (MAAS) resources.",
GitHubOrg: "juhnny5",
Repository: "https://github.com/juhnny5/pulumi-maas",
Keywords:   []string{"pulumi", "maas", "category/cloud"},

Ensuite, nous allons spécifier la configuration de chaque SDK à générer. Quand je parle de chaque SDK, j'entends par chaque langage de programmation à supporter par votre provider. 😈

Ainsi, vous pouvez obtenir ce résultat :

		JavaScript: &tfbridge.JavaScriptInfo{
			// List any npm dependencies and their versions
			Dependencies: map[string]string{
				"@pulumi/pulumi": "^3.0.0",
			},
			DevDependencies: map[string]string{
				"@types/node": "^10.0.0", // so we can access strongly typed node definitions.
				"@types/mime": "^2.0.0",
			},
			// See the documentation for tfbridge.OverlayInfo for how to lay out this
			// section, or refer to the AWS provider. Delete this section if there are
			// no overlay files.
			//Overlay: &tfbridge.OverlayInfo{},
		},
		Python: &tfbridge.PythonInfo{
			// List any Python dependencies and their version ranges
			PackageName: "pulumi-maas",
			Requires: map[string]string{
				"pulumi": ">=3.0.0,<4.0.0",
			},
		},
		Golang: &tfbridge.GolangInfo{
			ImportBasePath: filepath.Join(
				fmt.Sprintf("github.com/juhnny5/pulumi-%[1]s/sdk/", mainPkg),
				tfbridge.GetModuleMajorVersion(version.Version),
				"go",
				mainPkg,
			),
			GenerateResourceContainerTypes: true,
		},
		CSharp: &tfbridge.CSharpInfo{
			PackageReferences: map[string]string{
				"Pulumi": "3.*",
			},
		},

Générer le code ! ☺️

Maintenant que l'on est tout bon sur les dépendances et dans l'initialisation du projet, nous allons pouvoir transformer notre code Terraform en code Pulumi et générer les SDKs appropriés. 😄

make build_sdks

Vous retrouverez automatiquement le code des différents SDKS dans sdk/.

Une fois que c'est fait, il ne reste plus qu'à release votre code sur Github, je ne vais pas expliquer comment fonctionne Goreleaser ici, par contre, je vous invite à créer votre fichier de cette sorte :

archives:
  - id: archive
    name_template: '{{ .Binary }}-{{ .Tag }}-{{ .Os }}-{{ .Arch }}'
before:
  hooks:
    - make tfgen
builds:
  - binary: pulumi-resource-maas
    dir: provider
    env:
      - CGO_ENABLED=0
    goarch:
      - amd64
      - arm64
    goos:
      - darwin
      - windows
      - linux
    ldflags:
      # The line below MUST align with the module in current provider/go.mod
      - -X github.com/juhnny5/pulumi-maas/provider/pkg/version.Version={{.Tag }}
    main: ./cmd/pulumi-resource-maas/
changelog:
  skip: true
release:
  disable: false
  prerelease: auto
snapshot:
  name_template: '{{ .Tag }}-SNAPSHOT'
.goreleaser.yml

A noter que vous trouverez un exemple dans le dossier deploymen-templates/. Vous y trouverez également le README expliquant comment release votre projet.

Pour terminer, vous trouverez tout le code disponible ici si vous souhaitez un exemple concret :

GitHub - juhnny5/pulumi-maas
Contribute to juhnny5/pulumi-maas development by creating an account on GitHub.

Vous l'aurez compris, plus d'excuse pour ne pas migrer vers Pulumi. Le prochain article traitera de comment publier ces différentes ressources (sdks). 🤭