Skip to content

Pulumi

Pulumi es una herramienta para Infrastructure as Code (IaC), del mismo modo que Terraform pero diferente en su estilo de implementación. Con Pulumi:

  • Usas lenguajes reales (Python, TypeScript, Go, etc.), por lo que puedes incluir bucles, funciones, condiciones, clases…
  • Tienes preview antes de deploy, para mostrar exactamente lo que va a cambiar.
  • Soporta todos los servicios de AWS (así como otros servicios cloud).

Si te preguntas para qué usar un lenguaje y especificamente Python, ello hace parte de decisiones que tienen que ver con la naturaleza del proyecto, incluso el equipo, pero actualmente con el uso de la IA (Inteligencia Artificial) tienes un motivo para mantener una tecnología. Es decir, si ya estas usando Python en tu proyecto, implica que se tiene ese conocimiento, es probable que sea innecesario agregar otra tencología como Terraform.

Instalación de Pulumi CLI

Prerrequisitos

  • Cuenta AWS (usa la de Free Tier)
  • AWS CLI instalado (sudo snap install aws-cli --classic para Linux y brew install awscli para macOS) y luego configurado:
    bash
    aws configure
    (ingresa Access Key, Secret Key, región us-east-1, output json)
  • Python 3.8+
  • Editor (VS Code o Zed recomendado)

Instalación desde macOS o Linux

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

Instalación para Windows (PowerShell como administrador)

bash
@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; iex ((New-Object System.Net.WebClient).DownloadString('https://get.pulumi.com/install.ps1'))" && SET "PATH=%PATH%;%USERPROFILE%\.pulumi\bin"

Verifcando versión instalada

bash
pulumi version   # Debe mostrar ~3.224.x o superior

Flujo de trabajo clásico de Pulumi

En Pulumi se usan Pilas (Stacks) por entorno y se puede conectarse a la nube de Pulumi para ello. Veamos:

bash
pulumi login
pulumi new aws-python --name mi-primer-stack
cd mi-primer-stack

Esto crea:

  • __main__.py (donde escribes tu código)
  • Pulumi.yaml
  • requirements.txt

Como comandos clave encuentras:

ComandoQué hace
pulumi previewMuestra exactamente qué se creará/cambiará
pulumi upPreview + confirma + deploy
pulumi stack outputVer outputs (IPs, URLs, etc.)
pulumi destroyElimina todo
pulumi stack rmBorra el stack (después de destroy)

Ejemplo con Bucket S3 simple

Reemplaza todo el contenido de __main__.py con el siguiente código:

python
import pulumi
import pulumi_aws as aws

# Crear bucket S3 privado
bucket = aws.s3.Bucket("mi-bucket-libros",
    bucket="mi-bucket-libros-2026",
    tags={
        "Environment": "Dev",
        "Owner": "Cesar"
    },
    acl="private"
)

# Exportar datos útiles
pulumi.export("bucket_name", bucket.bucket)
pulumi.export("bucket_arn", bucket.arn)

Se importan librerías y se define el recurso en una variable.
Con pulumi.export se deja a disposición datos útiles para ser usados.

Ejecuta luego:

bash
pulumi preview
pulumi up
pulumi stack output

Confirmas y podrás contar con tu primer recurso en AWS.

Ejemplo de Instancia EC2 con servidor web

Crea un nuevo proyecto o reemplaza __main__.py:

python
import pulumi
import pulumi_aws as aws

size = "t2.micro"   # Free Tier

# AMI Amazon Linux 2 (la más reciente)
ami = aws.ec2.get_ami(most_recent=True,
    owners=["137112412989"],
    filters=[{"name": "name", "values": ["amzn2-ami-hvm-*"]}])

# Security Group (puertos 22 y 80)
group = aws.ec2.SecurityGroup("web-secgrp",
    description="Permitir SSH y HTTP",
    ingress=[
        {"protocol": "tcp", "from_port": 22, "to_port": 22, "cidr_blocks": ["0.0.0.0/0"]},
        {"protocol": "tcp", "from_port": 80, "to_port": 80, "cidr_blocks": ["0.0.0.0/0"]},
    ])

# Script de inicio (servidor web simple)
user_data = """#!/bin/bash
echo "¡Hi from Pulumi + EC2!" > index.html
nohup python3 -m http.server 80 &
"""

server = aws.ec2.Instance("mi-ec2-web",
    instance_type=size,
    vpc_security_group_ids=[group.id],
    ami=ami.id,
    user_data=user_data)

pulumi.export("public_ip", server.public_ip)
pulumi.export("public_dns", server.public_dns)
bash
pulumi up
pulumi stack output public_dns   # Abre esa URL en el navegador

Si abres la url en el navegador, verás el mensaje: ¡Hi from Pulumi + EC2!

Ejemplo de Función Lambda (Serverless)

Crea una carpeta lambda y dentro un archivo lambda_function.py:

python
def handler(event, context):
    return {
        "statusCode": 200,
        "body": "¡Hola desde Lambda con Pulumi!"
    }

Comprimelo con formato zip:

bash
cd lambda
zip -r ../lambda.zip .
cd ..

Ahora en __main__.py (nuevo proyecto o reemplaza):

python
import pulumi
import pulumi_aws as aws

# Rol IAM para Lambda
role = aws.iam.Role("lambda-role",
    assume_role_policy="""{
        "Version": "2012-10-17",
        "Statement": [{
            "Action": "sts:AssumeRole",
            "Principal": {"Service": "lambda.amazonaws.com"},
            "Effect": "Allow"
        }]
    }""")

aws.iam.RolePolicyAttachment("lambda-basic",
    role=role.name,
    policy_arn="arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole")

# Función Lambda
lambda_func = aws.lambda_.Function("mi-lambda",
    code=pulumi.FileArchive("lambda.zip"),
    role=role.arn,
    handler="lambda_function.handler",
    runtime="python3.12",
    timeout=10)

pulumi.export("lambda_name", lambda_func.name)
pulumi.export("lambda_arn", lambda_func.arn)
bash
pulumi up

Para probar, ve a la consola AWS - Lambda - Test

Resumen del flujo Pulumi

Básicamente, se dispone de los siguientes hitos:

  1. pulumi preview: para ver el plan
  2. pulumi up: para confirmar y despliegar
  3. pulumi destroy: para limpiar todo (si es una prueba)

Pulumi es legible para desarrollaores si usas lenguajes de programación como Python