Pulumi: Infraestrutura como Código Moderna com Python, TypeScript e Go

O que é o Pulumi?
Pulumi é uma plataforma de Infrastructure as Code (IaC) que permite gerenciar recursos de nuvem usando linguagens de programação reais como Python, TypeScript, Go, C# e Java. Diferente do Terraform que usa HCL (HashiCorp Configuration Language), o Pulumi oferece loops, condicionais, funções e bibliotecas padrão — tudo que você espera de uma linguagem de programação moderna.
Fundado em 2017 por Joe Duffy (ex-engenheiro da Microsoft e criador do Midori), o Pulumi rapidamente se tornou uma alternativa popular ao Terraform, especialmente entre times que já trabalham com TypeScript ou Python no dia a dia.
Por que Pulumi em vez de Terraform?
Enquanto o Terraform é maduro e amplamente adotado, o Pulumi resolve várias limitações fundamentais do HCL:
- Linguagem real: Use loops
for, condicionaisif/else, funções auxiliares e bibliotecas npm/pip — sem aprender HCL - TypeScript nativo: Autocomplete, type checking e refactoring no seu editor favorito
- Reuso de código: Publique componentes como pacotes npm, Python wheels ou Go modules
- Testabilidade: Escreva testes unitários e de integração com Jest, Pytest ou Go test
- Multi-cloud consistente: Mesma linguagem e ferramentas para AWS, Azure, GCP, Kubernetes e mais
Primeiros Passos com Python
Vamos criar nossa primeira infraestrutura com Pulumi usando Python:
# Instalar o Pulumi CLI
curl -fsSL https://get.pulumi.com | sh
# Verificar instalação
pulumi version
# Criar um novo projeto Python
mkdir meu-projeto-pulumi
cd meu-projeto-pulumi
pulumi new aws-python
O comando pulumi new gera a estrutura do projeto com __main__.py, Pulumi.yaml e requirements.txt.
Criando Recursos AWS
Exemplo completo de um bucket S3 com versionamento e um bucket público para site estático:
"""
import pulumi
import pulumi_aws as aws
from pulumi_aws import s3
# Bucket privado com versionamento
bucket_dados = s3.Bucket("bucket-dados",
bucket=f"dados-{pulumi.get_stack()}",
versioning=s3.BucketVersioningArgs(
enabled=True
),
tags={
"Ambiente": pulumi.get_stack(),
"GerenciadoPor": "Pulumi"
}
)
# Bucket público para site estático
bucket_site = s3.Bucket("bucket-site",
bucket=f"site-{pulumi.get_stack()}",
website=s3.BucketWebsiteArgs(
index_document="index.html",
error_document="error.html"
),
)
# Política para tornar o bucket público
bucket_policy = s3.BucketPolicy("bucket-policy",
bucket=bucket_site.id,
policy=bucket_site.arn.apply(lambda arn: json.dumps({
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": "*",
"Action": ["s3:GetObject"],
"Resource": f"{arn}/*"
}]
}))
)
# Exportar outputs
pulumi.export("bucket_dados_nome", bucket_dados.id)
pulumi.export("url_site", bucket_site.website_endpoint)
Gerenciando Kubernetes com Pulumi
O Pulumi também gerencia clusters Kubernetes com a mesma linguagem. Exemplo de deploy de um deployment Nginx com service LoadBalancer:
import pulumi
from pulumi_kubernetes import Provider
from pulumi_kubernetes.apps.v1 import Deployment
from pulumi_kubernetes.core.v1 import Service
# Provider Kubernetes (pode ser local, EKS, AKS, GKE)
k8s_provider = Provider("k8s")
# Deployment
deployment = Deployment("nginx-deploy",
spec={
"selector": {"match_labels": {"app": "nginx"}},
"replicas": 3,
"template": {
"metadata": {"labels": {"app": "nginx"}},
"spec": {
"containers": [{
"name": "nginx",
"image": "nginx:1.25",
"ports": [{"container_port": 80}]
}]
}
}
},
opts=pulumi.ResourceOptions(provider=k8s_provider)
)
# Service
service = Service("nginx-service",
spec={
"selector": {"app": "nginx"},
"ports": [{
"port": 80,
"target_port": 80,
"protocol": "TCP"
}],
"type": "LoadBalancer"
},
opts=pulumi.ResourceOptions(provider=k8s_provider)
)
pulumi.export("nginx_ip", service.status.apply(
lambda status: status.load_balancer.ingress[0].ip if status.load_balancer.ingress else None
))
Pulumi com TypeScript: Type Safety Total
TypeScript é a linguagem mais popular do Pulumi, oferecendo type checking completo para todos os recursos cloud:
import * as aws from "@pulumi/aws";
import * as pulumi from "@pulumi/pulumi";
// VPC com subnets
const vpc = new aws.ec2.Vpc("main-vpc", {
cidrBlock: "10.0.0.0/16",
enableDnsSupport: true,
enableDnsHostnames: true,
tags: { Name: "main-vpc" },
});
const publicSubnet = new aws.ec2.Subnet("public-subnet", {
vpcId: vpc.id,
cidrBlock: "10.0.1.0/24",
mapPublicIpOnLaunch: true,
availabilityZone: "us-east-1a",
tags: { Name: "public-subnet" },
});
// Security Group
const webSg = new aws.ec2.SecurityGroup("web-sg", {
vpcId: vpc.id,
ingress: [
{ protocol: "tcp", fromPort: 80, toPort: 80, cidrBlocks: ["0.0.0.0/0"] },
{ protocol: "tcp", fromPort: 443, toPort: 443, cidrBlocks: ["0.0.0.0/0"] },
],
egress: [
{ protocol: "-1", fromPort: 0, toPort: 0, cidrBlocks: ["0.0.0.0/0"] },
],
});
export const vpcId = vpc.id;
export const subnetId = publicSubnet.id;
Pulumi Automation API: Infraestrutura Programática
Um dos recursos mais poderosos do Pulumi é a Automation API, que permite gerenciar infraestrutura programaticamente dentro de aplicações — ideal para plataformas internas (IDP) e self-service:
import pulumi
from pulumi.automation import create_stack, LocalWorkspace
def criar_ambiente_dev(projeto: str):
"""
Cria um ambiente Pulumi completo via API.
Útil para plataformas self-service onde devs
solicitam ambientes sob demanda.
"""
workspace = LocalWorkspace(
project_settings={
"name": "infra-dev",
"runtime": "python",
},
secrets_provider="passphrase",
)
stack = create_stack("dev-${projeto}", workspace=workspace)
# Define as configurações do stack
stack.set_config("aws:region", "us-east-1")
stack.set_all_config({
"projeto": pulumi.Config_value(projeto),
})
# Define o programa Pulumi
stack.set_program(lambda: criar_recursos_dev())
# Executa up (equivalente ao pulumi up)
result = stack.up(on_output=print)
return result.outputs
def criar_recursos_dev():
import pulumi_aws as aws
# ... recursos do ambiente dev
bucket = aws.s3.Bucket("dev-bucket")
pulumi.export("bucket_name", bucket.id)
Componentes Reutilizáveis
Com Pulumi, você pode criar componentes reutilizáveis e distribuí-los como pacotes:
# meu-pacote/componente.py
import pulumi
from pulumi import ComponentResource, ResourceOptions
import pulumi_aws as aws
from typing import Optional
class BucketComTags(ComponentResource):
def __init__(self, name: str, bucket_name: str,
versioning: bool = True,
opts: Optional[ResourceOptions] = None):
super().__init__("meupacote:BucketComTags", name, None, opts)
self.bucket = aws.s3.Bucket(f"{name}-bucket",
bucket=bucket_name,
versioning=aws.s3.BucketVersioningArgs(
enabled=versioning
),
tags={
"GerenciadoPor": "Pulumi",
"Projeto": name,
"Versao": "1.0"
},
opts=ResourceOptions(parent=self)
)
self.register_outputs({
"bucket_id": self.bucket.id,
"bucket_arn": self.bucket.arn,
})
# Uso: pip install meu-pacote
bucket = BucketComTags("meu-bucket", "dados-app-prod")
pulumi.export("id", bucket.bucket_id)
Pulumi Cloud: Estado, Segredos e Colaboração
O Pulumi Cloud (anteriormente Pulumi Service) oferece gerenciamento de estado remoto, armazenamento seguro de segredos e colaboração em time:
- State remoto: Mantenha o estado da infraestrutura centralizado e versionado
- Secrets encriptados: Senhas e chaves de API são criptografadas automaticamente
- Webhooks: Notifique Slack, Teams ou webhooks em cada deploy
- Pull Request comments: Veja diff de infra diretamente nos PRs do GitHub/GitLab
- Policy as Code: Aplique regras de compliance com CrossGuard (policies em Python/TypeScript)
Policy as Code com CrossGuard
CrossGuard é o framework de políticas do Pulumi que permite validar infraestrutura antes do deploy:
# policy.py
from pulumi_policy import (
ResourceValidationPolicy,
PolicyPack,
StackValidationPolicy,
)
# Política: todo bucket S3 deve ter versionamento
bucket_versioning_policy = ResourceValidationPolicy(
name="s3-versioning-obrigatorio",
description="Todos os buckets S3 devem ter versionamento ativado",
validate=lambda args, report:
report.assertion(
args.resource_type == "aws:s3/bucket:Bucket"
and args.props.get("versioning", {}).get("enabled"),
"Bucket S3 sem versionamento ativado!",
),
)
# Política: tags obrigatórias em todos os recursos
tag_policy = ResourceValidationPolicy(
name="tags-obrigatorias",
description="Todos os recursos devem ter tag Ambiente",
validate=lambda args, report:
report.assertion(
"Ambiente" in args.props.get("tags", {}),
f"Tag 'Ambiente' ausente em {args.resource_type}",
),
)
PolicyPack(
name="politicas-infra",
policies=[bucket_versioning_policy, tag_policy],
)
Comparativo de Comandos
| Operação | Terraform | Pulumi |
|---|---|---|
| Iniciar projeto | terraform init | pulumi new |
| Ver plano | terraform plan | pulumi preview |
| Aplicar | terraform apply | pulumi up |
| Destruir | terraform destroy | pulumi destroy |
| Ver estado | terraform state list | pulumi stack ls |
Conclusão
Pulumi representa uma evolução natural no mundo de Infrastructure as Code. Ao permitir que desenvolvedores usem linguagens que já conhecem — Python, TypeScript, Go — ele elimina a barreira de aprendizado do HCL e abre possibilidades como testes unitários, reuso via pacotes e automação programática com a Automation API.
Se seu time já trabalha com TypeScript no front-end e Python nos dados, o Pulumi permite unificar a stack: a mesma linguagem para aplicação e infraestrutura. Para novos projetos, especialmente em ambientes multi-cloud ou com times de plataforma, o Pulumi é uma escolha cada vez mais popular — e por bons motivos.







