Aller au contenu principal

Débutez avec Pulumi

Dans nos métiers, le provisionnement de l'infrastructure est devenue une partie essentielle du développement moderne. Les équipes de développement cherchent en permanence des moyens d'automatiser et de rationaliser la création, la configuration et la gestion de leurs ressources cloud. C'est là que l'infrastructure as code (IaC) entre en jeu et parmi les nombreux outils disponibles, Pulumi se distingue comme une solution puissante et polyvalente.

Qu'est-ce que Pulumi ?

Pulumi est bien plus qu'un simple outil d'infrastructure as code (IaC) ; il s'agit d'une plateforme complète qui vous permet de gérer votre infrastructure cloud à l'aide de langages de programmation familiers. Contrairement à de nombreux autres outils IaC, Pulumi n'est pas limité à un langage spécifique. Il prend en charge plusieurs langages populaires, notamment Python, TypeScript, Go, et bien d'autres. Cela signifie que vous pouvez écrire du code pour définir votre infrastructure en utilisant le langage que vous maîtrisez le mieux.

Pulumi vous offre également la possibilité de travailler avec de multiples fournisseurs de cloud, tels qu'Amazon Web Services (AWS), Microsoft Azure, Google Cloud Platform (GCP) et bien d'autres. Vous n'êtes donc pas lié à un fournisseur spécifique et pouvez déployer vos ressources sur la plateforme cloud qui correspond le mieux à vos besoins.

Concepts fondamentaux de Pulumi

Représentation déclarative

Pulumi adopte une approche déclarative pour la définition de l'infrastructure, ce qui signifie que vous spécifiez simplement l'état souhaité de votre infrastructure et Pulumi se charge de créer, mettre à jour ou supprimer les ressources pour atteindre cet état. Cette approche est plus intuitive que la programmation impérative, où vous devrez décrire les étapes pour atteindre l'état souhaité.

Stockage de l'état

Pulumi gère automatiquement l'état de votre infrastructure. Cela signifie qu'il garde une trace des ressources que vous avez créées, de leurs états actuels, et des dépendances entre elles. La gestion de l'état facilite la planification et l'exécution des mises à jour de votre infrastructure, tout en minimisant les risques d'incohérences.

Les Stacks

Un autre concept clé de Pulumi est celui des stacks. Les stacks vous permettent de gérer différents environnements, tels que ceux de développement, de pré-production et de production, en utilisant la même définition d'infrastructure.

Chaque stack a sa propre configuration, ce qui vous permet de déployer et de gérer votre infrastructure de manière cohérente, quelle que soit la phase l'environnement cible. Vous pouvez définir des variables spécifiques à chaque stack pour gérer les paramètres qui changent d'un environnement à l'autre.

La notion de plan et d'application des modifications

Avant d'appliquer des modifications à votre infrastructure, Pulumi génère un plan. Ce plan affiche les actions exactes que Pulumi va entreprendre pour mettre à jour votre infrastructure. Vous avez la possibilité de revoir le plan avant d'autoriser Pulumi à appliquer les modifications, ce qui vous permet de comprendre précisément ce qui va se passer.

Ce processus offre un niveau de sécurité supplémentaire, car il évite les modifications accidentelles ou non autorisées de votre infrastructure.

Gestion des dépendances entre les ressources

Lorsque vous définissez votre infrastructure en tant que code, il est courant d'avoir des dépendances entre différentes ressources. Par exemple, vous pourriez avoir besoin d'un serveur avant de créer une base de données qui lui est liée.

Pulumi gère automatiquement ces dépendances pour vous. Vous pouvez spécifier les dépendances entre les ressources dans votre code et Pulumi garantit que les ressources sont créées et mises à jour dans le bon ordre pour satisfaire ces dépendances.

Cela simplifie considérablement la gestion des infrastructures complexes, car vous n'avez pas à vous soucier de l'ordre d'exécution des opérations.

Installation de Pulumi

Pour commencer avec Pulumi, vous devrez installer l'outil sur votre système. L'installation est généralement simple et Pulumi est disponible pour les différentes plateformes, y compris Windows, macOS et Linux. Une fois installé, vous devrez configurer Pulumi pour qu'il utilise les informations d'authentification de votre fournisseur cloud.

Installation de Pulumi sur Linux

Dans une fenêtre de Terminal tapez la commande suivante :

curl -fsSL https://get.pulumi.com | sh

Installation de Pulumi sur MacOS

Dans une fenêtre Terminal tapez la commande suivante :

brew install pulumi/tap/pulumi

Installation de Pulumi sur Windows

Dans une fenêtre Powershell tapez la commande suivante :

winget install pulumi

Vérification de l'installation de Pulumi

Dans une fenêtre terminal tapez la commande suivante :

pulumi version
v3.92.0

Configuration de Pulumi

Le processus de configuration initiale peut varier en fonction de votre fournisseur de cloud, mais Pulumi fournit généralement des guides détaillés pour chacun d'eux. Assurez-vous de suivre ces guides pour configurer correctement votre environnement de développement. Exemple avec AWS

Ecrire son premier code d'infrastructure avec Pulumi

Maintenant que nous avons une meilleure compréhension de ce qu'est Pulumi, plongeons plus profondément dans la manière dont vous pouvez définir votre infrastructure en tant que code avec cet outil puissant.

Utilisation du backend local

Pour éviter de créer un state dans le cloud de pulumi, je fais le choix d'utiliser le backend local :

pulumi login --local
Logged in to internal as bob (file://~)

Création de notre première VM

Pour mieux comprendre comment Pulumi fonctionne, commençons par créer un nouveau projet :

pulumi new
Please choose a template (28/221 shown):
 aws-python                         A minimal AWS Python Pulumi program
This command will walk you through creating a new Pulumi project.

Enter a value or leave blank to accept the (default), and press <ENTER>.
Press ^C at any time to quit.

project name (project): pulumi-aws-python
project description (A minimal AWS Python Pulumi program):
Created project 'pulumi-aws-python'

stack name (dev):
Created stack 'dev'
Enter your passphrase to protect config/secrets:
Re-enter your passphrase to confirm:

aws:region: The AWS region to deploy into (us-east-1): eu-west-2
Saved config

Installing dependencies...

Creating virtual environment...
Finished creating virtual environment
Updating pip, setuptools, and wheel in virtual environment...
Requirement already satisfied: pip in ./venv/lib/python3.10/site-packages (22.0.2)
Collecting pip
  Using cached pip-23.3.1-py3-none-any.whl (2.1 MB)
Requirement already satisfied: setuptools in ./venv/lib/python3.10/site-packages (59.6.0)
Collecting setuptools
  Using cached setuptools-68.2.2-py3-none-any.whl (807 kB)
Collecting wheel
  Using cached wheel-0.41.3-py3-none-any.whl (65 kB)
Installing collected packages: wheel, setuptools, pip
  Attempting uninstall: setuptools
    Found existing installation: setuptools 59.6.0
    Uninstalling setuptools-59.6.0:
      Successfully uninstalled setuptools-59.6.0
  Attempting uninstall: pip
    Found existing installation: pip 22.0.2
    Uninstalling pip-22.0.2:
      Successfully uninstalled pip-22.0.2
Successfully installed pip-23.3.1 setuptools-68.2.2 wheel-0.41.3
Finished updating
Installing dependencies in virtual environment...
Collecting pulumi<4.0.0,>=3.0.0 (from -r requirements.txt (line 1))
  Using cached pulumi-3.94.2-py3-none-any.whl.metadata (11 kB)
Collecting pulumi-aws<7.0.0,>=6.0.2 (from -r requirements.txt (line 2))
  Downloading pulumi_aws-6.9.0-py3-none-any.whl.metadata (9.0 kB)
Collecting protobuf~=4.21 (from pulumi<4.0.0,>=3.0.0->-r requirements.txt (line 1))
  Using cached protobuf-4.25.1-cp37-abi3-manylinux2014_x86_64.whl.metadata (541 bytes)
Collecting grpcio==1.56.2 (from pulumi<4.0.0,>=3.0.0->-r requirements.txt (line 1))
  Using cached grpcio-1.56.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (4.0 kB)
Collecting dill~=0.3 (from pulumi<4.0.0,>=3.0.0->-r requirements.txt (line 1))
  Using cached dill-0.3.7-py3-none-any.whl.metadata (9.9 kB)
Collecting six~=1.12 (from pulumi<4.0.0,>=3.0.0->-r requirements.txt (line 1))
  Using cached six-1.16.0-py2.py3-none-any.whl (11 kB)
Collecting semver~=2.13 (from pulumi<4.0.0,>=3.0.0->-r requirements.txt (line 1))
  Using cached semver-2.13.0-py2.py3-none-any.whl (12 kB)
Collecting pyyaml~=6.0 (from pulumi<4.0.0,>=3.0.0->-r requirements.txt (line 1))
  Using cached PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (2.1 kB)
Collecting parver>=0.2.1 (from pulumi-aws<7.0.0,>=6.0.2->-r requirements.txt (line 2))
  Using cached parver-0.5-py3-none-any.whl.metadata (2.7 kB)
Collecting arpeggio>=1.7 (from parver>=0.2.1->pulumi-aws<7.0.0,>=6.0.2->-r requirements.txt (line 2))
  Using cached Arpeggio-2.0.2-py2.py3-none-any.whl.metadata (2.4 kB)
Collecting attrs>=19.2 (from parver>=0.2.1->pulumi-aws<7.0.0,>=6.0.2->-r requirements.txt (line 2))
  Using cached attrs-23.1.0-py3-none-any.whl (61 kB)
Using cached pulumi-3.94.2-py3-none-any.whl (191 kB)
Using cached grpcio-1.56.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (5.2 MB)
Downloading pulumi_aws-6.9.0-py3-none-any.whl (7.9 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.9/7.9 MB 46.9 MB/s eta 0:00:00
Using cached dill-0.3.7-py3-none-any.whl (115 kB)
Using cached parver-0.5-py3-none-any.whl (15 kB)
Using cached protobuf-4.25.1-cp37-abi3-manylinux2014_x86_64.whl (294 kB)
Using cached PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (705 kB)
Using cached Arpeggio-2.0.2-py2.py3-none-any.whl (55 kB)
Installing collected packages: arpeggio, six, semver, pyyaml, protobuf, grpcio, dill, attrs, pulumi, parver, pulumi-aws
Successfully installed arpeggio-2.0.2 attrs-23.1.0 dill-0.3.7 grpcio-1.56.2 parver-0.5 protobuf-4.25.1 pulumi-3.94.2 pulumi-aws-6.9.0 pyyaml-6.0.1 semver-2.13.0 six-1.16.0
Finished installing dependencies
Finished installing dependencies

Your new project is ready to go!

To perform an initial deployment, run `pulumi up`

Pulumi nous propose de choisr parmi des templates existants. J'ai choisi Aws-Python. Pulumi créé le nécessaire pour notre premier projet, avec un environnement virtuel. Il nous invite à lancer la commande pulumi up. Soyons fou :

pulumi up
Enter your passphrase to unlock config/secrets
    (set PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILE to remember):
Enter your passphrase to unlock config/secrets
Previewing update (dev):
Downloading plugin: 176.66 MiB / 176.66 MiB [=======================] 100.00% 3s
                                                                                [resource plugin aws-6.9.0] installing
     Type                 Name                   Plan
 +   pulumi:pulumi:Stack  pulumi-aws-python-dev  create
 +   └─ aws:s3:Bucket     my-bucket              create

Outputs:
    bucket_name: output<string>

Resources:
    + 2 to create

Do you want to perform this update?  [Use arrows to move, type to filter]
  yes
> no
  details

Mince, il propose de créer un premier bucket ! Regardons en détail le contenu du dossier :

ls -alrt
total 36
drwxrwxr-x 11 bob bob 4096 nov.  20 10:20 ..
-rw-r--r--  1 bob bob   46 nov.  20 11:06 requirements.txt
-rw-r--r--  1 bob bob  129 nov.  20 11:06 Pulumi.yaml
-rw-r--r--  1 bob bob  219 nov.  20 11:06 __main__.py
-rw-r--r--  1 bob bob   12 nov.  20 11:06 .gitignore
-rw-r--r--  1 bob bob  117 nov.  20 11:07 Pulumi.dev.yaml
drwxrwxr-x  5 bob bob 4096 nov.  20 11:07 venv
drwxrwxr-x  2 bob bob 4096 nov.  20 11:10 __pycache__
drwxrwxr-x  4 bob bob 4096 nov.  20 11:10 .

On y retrouve donc :

  • Pulumi.dev.yaml : contient la configuration de la pile que vous initialisez
  • Pulumi.yaml : Définit le projet
  • main.py : contient un exemple de programme avec les importations Pulumi et définit les ressources du programme.
  • requirements.txt : contient les exigences de la version Pulumi
  • un dossier venv contenant un environnement virtuel Python

Le contenu du fichier main.py :

"""An AWS Python Pulumi program"""

import pulumi
from pulumi_aws import s3

# Create an AWS resource (S3 Bucket)
bucket = s3.Bucket('my-bucket')

# Export the name of the bucket
pulumi.export('bucket_name', bucket.id)

Dans cet exemple, nous importons les modules nécessaires de Pulumi et d'AWS, puis nous créons un objet Bucket en lui donnant un nom ("my-bucket"). Pulumi se charge ensuite de créer le bucket S3 correspondant dans votre compte AWS lorsque vous exécutez ce code.

On relance le provisionnement en répondant yes cette fois :

pulumi up                                                 12:03:17
Enter your passphrase to unlock config/secrets
    (set PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILE to remember):
Enter your passphrase to unlock config/secrets
Previewing update (dev):
     Type                 Name                   Plan
     pulumi:pulumi:Stack  pulumi-aws-python-dev
 +   └─ aws:s3:Bucket     my-bucket              create

Outputs:
  + bucket_name: output<string>

Resources:
    + 1 to create
    1 unchanged

Do you want to perform this update? yes
Updating (dev):
     Type                 Name                   Status
     pulumi:pulumi:Stack  pulumi-aws-python-dev
 +   └─ aws:s3:Bucket     my-bucket              created (0.91s)

Outputs:
  + bucket_name: "my-bucket-5c4d53d"

Resources:
    + 1 created
    1 unchanged

Duration: 2s

Pulumi affiche en sortie le nom du bucket, Vérifions avec la CLI d'AWS si le bucket a bien été créé avec ce nom :

aws s3 ls
2023-11-20 12:03:28 my-bucket-5c4d53d

Je cherche la documentation sur ce provider AWS. Chaque objet est largement détaillé avec pas mal d'exemples.

Je vois que je peux par exemple ajouter des tags. Je modifie le code.

"""An AWS Python Pulumi program"""

import pulumi
from pulumi_aws import s3

# Create an AWS resource (S3 Bucket)
bucket = s3.Bucket(
    "my-bucket",
    tags={
        "Environment": "Dev",
        "Name": "My bucket",
    },
)

# Export the name of the bucket
pulumi.export("bucket_name", bucket.id)

Avant de modifier je vérifie les tags actuels :

aws s3api get-bucket-tagging --bucket my-bucket-5c4d53d

An error occurred (NoSuchTagSet) when calling the GetBucketTagging operation: The TagSet does not exist

Aucun tag. Je lance la mise à jour :

pulumi up
Enter your passphrase to unlock config/secrets
    (set PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILE to remember):
Enter your passphrase to unlock config/secrets
Previewing update (dev):
     Type                 Name                   Plan       Info
     pulumi:pulumi:Stack  pulumi-aws-python-dev
 ~   └─ aws:s3:Bucket     my-bucket              update     [diff: +tags,tagsAll]

Resources:
    ~ 1 to update
    1 unchanged

Do you want to perform this update? yes
Updating (dev):
     Type                 Name                   Status              Info
     pulumi:pulumi:Stack  pulumi-aws-python-dev
 ~   └─ aws:s3:Bucket     my-bucket              updated (0.65s)     [diff: +tags,tagsAll]

Outputs:
    bucket_name: "my-bucket-5c4d53d"

Resources:
    ~ 1 updated
    1 unchanged

Duration: 2s

On voit bien la différence avec diff: tags...

Vérifions les tags maintenant :

aws s3api get-bucket-tagging --bucket my-bucket-5c4d53d
{
    "TagSet": [
        {
            "Key": "Environment",
            "Value": "Dev"
        },
        {
            "Key": "Name",
            "Value": "My bucket"
        }
    ]
}

Nickel. Chrome. On en reste là. Détruisons le tout.

pulumi destroy
Enter your passphrase to unlock config/secrets
    (set PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILE to remember):
Enter your passphrase to unlock config/secrets
Previewing destroy (dev):
     Type                 Name                   Plan
 -   pulumi:pulumi:Stack  pulumi-aws-python-dev  delete
 -   └─ aws:s3:Bucket     my-bucket              delete

Outputs:
  - bucket_name: "my-bucket-5c4d53d"

Resources:
    - 2 to delete

Do you want to perform this destroy? yes
Destroying (dev):
     Type                 Name                   Status
 -   pulumi:pulumi:Stack  pulumi-aws-python-dev  deleted (0.00s)
 -   └─ aws:s3:Bucket     my-bucket              deleted (0.42s)

Outputs:
  - bucket_name: "my-bucket-5c4d53d"

Resources:
    - 2 deleted

Duration: 1s

The resources in the stack have been deleted, but the history and configuration associated with the stack are still maintained.
If you want to remove the stack completely, run `pulumi stack rm dev`.

Gestion des secrets

La sécurité et la gestion des secrets sont des aspects critiques de la gestion de l'infrastructure.

Pulumi reconnaît l'importance de la sécurité et offre des fonctionnalités intégrées pour gérer les informations sensibles telles que les mots de passe, les clés d'API, les jetons d'accès et d'autres données confidentielles.

Stockage sécurisé des secrets

Pulumi propose un coffre-fort de sécurité intégré qui permet de stocker de manière sécurisée les secrets, les clés API et autres données sensibles. Ces informations sont chiffrées au repos et lors de leur transmission.

Vous pouvez utiliser des variables secrètes dans votre code Pulumi pour accéder aux informations sensibles stockées dans le coffre-fort de sécurité. Ces variables ne sont jamais exposées en clair dans votre code ou dans les fichiers de configuration, ce qui renforce la sécurité de votre infrastructure.

Pulumi utilise des mécanismes de chiffrement solides pour garantir que vos secrets sont stockés en toute sécurité. Cela inclut le chiffrement des données en transit et au repos, ainsi que la gestion des clés de chiffrement.

Gestion des configurations spécifiques à chaque environnement

Outre la gestion des secrets, Pulumi vous permet de gérer des configurations spécifiques à chaque environnement. Cela signifie que vous pouvez personnaliser les paramètres de votre infrastructure en fonction de l'environnement dans lequel vous déploierez votre code.

Par exemple, vous pouvez avoir des configurations différentes pour le développement, la pré-production et la production. Pulumi vous permet de gérer ces configurations de manière centralisée et de les utiliser dans votre code.

Exemple d'utilisation de secrets et de configurations

Voici un exemple simple de la manière dont vous pouvez utiliser des secrets et des configurations dans votre code Pulumi :

On commence par créer un secret avec la CLI :

pulumi config set --secret my-secret-value test
Enter your passphrase to unlock config/secrets
    (set PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILE to remember):

Utilisons le dans notre exemple précédent pour créer un Objet avec son contenu :

import pulumi
from pulumi_aws import s3

config = pulumi.Config()

superSecret = config.require_secret("my-secret-value")

# Create a private bucket
bucket = s3.Bucket('my-bucket', acl="private")

# Create an object from the secret value
bucketObject = s3.BucketObject("secret", bucket=bucket.id, key="secret", content=superSecret)

Dans cet exemple, nous utilisons pulumi.Config() pour accéder aux configurations spécifiques à l'environnement. Nous utilisons ensuite la fonction config.require_secret pour récuperer le secret. Ces éléments sont ensuite utilisés pour créer un bucket S3 contenant un objet.

On lance la création :

pulumi up
Enter your passphrase to unlock config/secrets
    (set PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILE to remember):
Enter your passphrase to unlock config/secrets
Previewing update (dev):
     Type                    Name                   Plan
 +   pulumi:pulumi:Stack     pulumi-aws-python-dev  create
 +   ├─ aws:s3:Bucket        my-bucket              create
 +   └─ aws:s3:BucketObject  secret                 create

Resources:
    + 3 to create

Do you want to perform this update? yes
Updating (dev):
     Type                    Name                   Status
 +   pulumi:pulumi:Stack     pulumi-aws-python-dev  created (0.01s)
 +   ├─ aws:s3:Bucket        my-bucket              created (1s)
 +   └─ aws:s3:BucketObject  secret                 created (0.14s)

Resources:
    + 3 created

Duration: 3s

On copie le fichier pour en voir le contenu :

aws s3 cp s3://my-bucket-d0d1147/secret .
download: s3://my-bucket-d0d1147/secret to ./secret

cat secret
test

C'est la bonne valeur. L'exemple n'est pas génial mais montre que Pulumi gère parfaitement les secrets.

Conclusion

J'espére que cette introduction sur Pulumi vous a aidé à débuter avec Pulumi. N'oubliez pas que Pulumi évolue constamment, alors tenez-vous au courant avec les dernières fonctionnalités et les meilleures pratiques pour en tirer le meilleur parti.

Plus d'infos

  • Site Officiel : Pulumi
  • Le projet : Ce site propose le code source de Pulumi, ainsi que des exemples d'utilisation et des contributions de la communauté.