
Pulumi
Primeros pasos con esta herramienta de IaC.
📋Tabla de contenido
🌅Introducción
Pulumi es una herramienta que permite administrar infraestructura como código (IaC), principalmente en los servicios cloud (AWS, GCP y Azure) y en kubernetes.
Se puede hacer uso de esta herramienta utilizando varios lenguajes de programación (typescript, python, go, java…), en este viaje analizando pulumi me centro en su uso con python como lenguaje de programación y administrando recursos en Google Cloud Platform.
Comandos principales
pulumi new # Crea un nuevo proyecto usando un template
pulumi stack # Administra tus stacks
pulumi config # Configura variables como claves, regiones, etc.
pulumi up # Vista previa e implementación de cambios
pulumi preview # Vista previa de los cambios
pulumi destroy # Elimina tu programa y su infraestructura
🌍Enlaces de interés
Pulumi Get Started
Pulumi Concepts
Pulumi Registry
Using Pulumi
🛠️Instalación
Instalación en ubuntu
curl -fsSL https://get.pulumi.com | sh
# Agregar al PATH
export PATH=$PATH:/root/.pulumi/bin
Comprobar la versión instalada:
pulumi version
🚀Get started
En este ejemplo para iniciarte con pulumi, vas a configurar el entorno necesario para crear un bucket en GCP, agregarle un fichero, actualizar los recursos, acceder al fichero mediante su url y eliminarlo todo.
Para trabajar con pulumi para generar recursos en GCP con python es necesario instalar:
El primer paso será configurar gcloud, para trabajar en tus proyectos.
# Realiza el login con tu usuario de GCP
gcloud auth login
# Verifica que te has identificado correctamente
gcloud auth list
# Muestra la lista de proyectos que tienes disponible con tu usuario
gcloud projets list
# Configura gcloud para trabajar con el proyecto donde quieres generar los recursos
gcloud config set project <PROJECT_ID>
# Muestra la configuración de gcloud para asegurarte que has seleccionado el proyecto deseado
gcloud config list
Pulumi requiere credenciales de aplicación predeterminadas para interactuar con los recursos de GCP.
gcloud auth application-default login
Desconectate de tu backend actual (si es que estas conectado)
pulumi logout
Conectate a un backend de pulumi, normalmente deberías usar un bucket, puedes consultar las opciones disponibles con pulumi login --help
, en este ejemplo usarás como backend un archivo local:
pulumi login --local
Crea un directorio de trabajo
mkdir pulumi-get-started && cd pulumi-get-started
Crea un nuevo proyecto de pulumi indicando el runtime python
, el nombre del proyecto, la descripción y el stack dev
pulumi new python --name "my-python-project" --description "Mi proyecto de pulumi y python" --stack dev
Se creará la siguiente estructura de ficheros:
.
├── __main__.py
├── .gitignore
├── Pulumi.dev.yaml
├── Pulumi.yaml
└── requirements.txt
Agrega el paquete de GCP a las dependencias del proyecto y realiza la instalación:
echo "pulumi_gcp>8.0.0,<9.0.0" >> requirements.txt
pulumi install
Agrega a la configuración del proyecto de pulumi el proyecto de GCP donde quieres crear los recursos:
pulumi config set gcp:project <PROJECT_ID>
Modifica el archivo __main__.py
con el siguiente contenido para crear un bucket y exportar como un output la url del bucket:
"""A Python Pulumi program"""
import pulumi
from pulumi_gcp import storage
# Create a GCP resource (Storage Bucket)
bucket = storage.Bucket('my-bucket', location="EU")
# Export the DNS name of the bucket
pulumi.export('bucket_name', bucket.url)
Crea los recursos y muestra el output que contiene la url del bucket:
# Ejecuta el script de pulumi
pulumi up
# Muestra la url del bucket creado
pulumi stack output bucket_name
Crea un fichero index.html
en el directorio de trabajo con el siguiente contenido:
<html>
<body>
<h1>Hello, Pulumi!</h1>
</body>
</html>
Modifica __main__.py
para que agregue index.html
al bucket y asigna permisos al bucket para hacerlo público:
bucket_object = storage.BucketObject(
"my-index.html",
bucket=bucket.name,
source=pulumi.FileAsset("index.html")
)
bucket_iam_binding = storage.BucketIAMBinding(
"my-bucket-binding",
bucket=bucket.name,
role="roles/storage.objectViewer",
members=["allUsers"],
)
Actualiza los recursos en GCP ejecutando el script de pulumi y muestra los ficheros del bucket con gsutil
para asegurarte de que el fichero se cargo correctamente:
# Actualiza los recursos ejecutando el script de pulumi
pulumi up
# Comprueba que se ha cargado el fichero correctamente
gsutil ls $(pulumi stack output bucket_name)
Agrega al programa la directiva website
y uniform_bucket_level_access
en la definición del bucket y exporta la url de acceso a la web:
bucket = storage.Bucket(
'test-bucket',
location="EU",
website={
"main_page_suffix": "index.html"
},
uniform_bucket_level_access=True,
)
pulumi.export(
"bucket_endpoint",
pulumi.Output.concat(
"http://storage.googleapis.com/", bucket.id, "/", bucket_object.name
),
)
Actualiza los recursos ejecutando el script de pulumi:
pulumi up
Accede al sitio web que se ha creado en el bucket de GCP:
curl $(pulumi stack output bucket_endpoint)
Obtendrás el contenido de index.html
<html>
<body>
<h1>Hello, Pulumi!</h1>
</body>
</html>
Elimina el stack y los recursos generados en la prática.
pulumi destroy
📌Obtener el nombre del stack actual dentro del script
import pulumi
current_stack = pulumi.get_stack()
pulumi.export("current_stack", current_stack)
📌Obtener el backend al que estoy conectado actualmente
pulumi whoami --verbose
📌Usa la configuración con diferentes namespaces
Definir las diferentes configuraciones
# Configurar variables para GCP
pulumi config set gcp:project <PROJECT_NAME>
# Configurar variables en el namespace por defecto
pulumi config set <CONFIG_NAME> <CONFIG_VALUE>
Acceder a las configuraciones dentro del script
from pulumi import Config
config = Config()
config_gcp = Config("gcp")
📌Listar imágenes públicas de GCP para seleccionar sistema operativo
# Ver imágenes disponibles (listado completo)
gcloud compute images list
# Filtrar imágenes de ubuntu
gcloud compute images list --project=ubuntu-os-cloud --no-standard-images
# Filtrar imágenes de ubuntu 24
gcloud compute images list --project=ubuntu-os-cloud --no-standard-images --filter="name:ubuntu-2404*"
📌Importar recursos al estado
En la documentación del propio recurso en el registry de pulumi te indica como importar cada uno de los recursos.
Sintaxis básica:
pulumi import <TYPE> <RESOURCE_NAME> <ID>
Ejemplos:
# Importar un Project
pulumi import -y gcp:organizations/project:Project project-my-project my-project
# Importar un MonitoredProject
pulumi import -y gcp:monitoring/monitoredProject:MonitoredProject monitoredProject-my-project v1/locations/global/metricsScopes/project-global-monitoring/projects/my-project
# Importar un Service (API)
pulumi import -y gcp:projects/service:Service service-my-proyect-iam my-project/iam.googleapis.com
# Importar un IAMMember
pulumi import -y gcp:projects/iAMMember:IAMMember iamMember-my-project-viewer-user@mail.com "projects/my-project roles/viewer user:user@mail.com"
📌Comparar diferencias de recursos (pulumi state vs. gcp)
Muestrará las diferencias en detalle encontradas entre el estado actual de pulumi y los recursos de GCP.
pulumi preview --diff
📌Comparar la configuración de un bucket de GCP con el estado actual de pulumi
# Descargar a un fichero el estado de pulumi
pulumi stack export > pulumi-state.json
# Descargar la configuración de GCP
gcloud storage buckets describe gs://nexe_filestore --format=json > gcp-bucket.json
📌Stack outputs
Son las salidas de información que indicamos en los scripts con la función de pulumi.export().
Estos son los comandos principales para administrar los outputs:
pulumi stack output
pulumi stack output --show-secrets
📌Secrets
Crea un secret
pulumi config set --secret <SECRET_NAME> "<VALUE>"
Usa el secret en el script
variable = config.require_secret("<SECRET_NAME>")
Elimina un sercret
pulumi config rm <SECRET_NAME>
📌Guardar el estado de pulumi e importarlo (copia de seguridad)
- Guardar el estado
pulumi stack export --file pulumi-backup.json
- Restaurar el estado
pulumi stack import --file pulumi-backup.json
📌Logging
pulumi.info("message")
pulumi.info("message", resource)
pulumi.debug("hidden by default")
pulumi.warn("warning")
pulumi.error("fatal error")
📌Pulumi outputs
No confundas con los pulumi stack outputs, estos outputs se refieren a cuando generamos recursos, a las propiedades de esos recursos creados en el script.
Dentro del script debes utilizar las funciones:
apply()
(Para un ouput)
all()
(Para varios ouputs)
📌Migración de un estado en pulumi cloud backend a un bucket en GCP
- Conectate a pulumi cloud backend
pulumi login
- Inicia el stack
pulumi new gcp-python
- Modifica el secret provider a modo contraseña para no tener problemas a la hora de importarlo en GCP
pulumi stack change-secrets-provider "passphrase"
# Pedirá que introduzcas la contraseña (Ej, 123)
- Despliega los recursos desde el estado de pulumi backend cloud
pulumi up
- Exporta el estado del stack a un fichero
pulumi stack export --file state.json
- Desconectate de pulumi backend cloud
pulumi logout
- Crea el bucket <BUCKET_NAME> en GCP
- Conectate al backend indicando la url del bucket en GCP
pulumi login gs://<BUCKET_NAME>
- Configura las variables de entorno con la contraseña (Ej, 123) y el secret provider
export PULUMI_CONFIG_PASSPHRASE=<PASSWORD>
export PULUMI_CONFIG_SECRETS_PROVIDER=passphrase
- Incia un stack en el nuevo backend con el mismo nombre que tenia en pulumi cloud backend
pulumi stack init
- Indica el ID del proyecto en GCP en la configuración del stack
pulumi config set gcp:project <YOUR_GCP_PROJECT_ID>
- Importa el estado del stack
pulumi stack import --file state.json
- Refresca el estado
pulumi refresh
-
Si realizas cambios en los recursos a modo de prueba, ejecuta
pulumi refresh
antes de ejecutar el plan para que identifique el cambio correctamente. -
ELimina los recursos
pulumi destroy
📌Comandos de utilidad
- Trabajar con los stacks
pulumi stack ls
pulumi stack select <STACK_NAME>
pulumi stack rm <STACK_NAME>
- Importar recursos al estado del stack
pulumi import gcp:storage/bucket:Bucket <NOMBRE_DEL_RECURSO_EN_PULUMI> <NOMBRE_DEL_BUCKET_EN_GCP>
# pulumi import gcp:storage/bucket:Bucket test-bucket test-company-bucket
- Eliminar recurso del estado
# Obtener el urn del recurso
pulumi stack export | jq '.deployment.resources[].urn'
# Eliminar recurso
pulumi state delete <URN>
# pulumi state delete urn:pulumi:dev::myproject::gcp:storage/bucket:Bucket::myBucket
# pulumi state delete urn:pulumi:dev::myproject::gcp:storage/bucket:Bucket::myBucket --force