
Infraestrutura como Código no Azure com Pulumi: deploy de VMs com NGINX e MySQL
Introdução
O Pulumi é uma alternativa moderna ao ARM, Bicep ou Terraform para provisionar infraestrutura no Azure. A grande vantagem é que você pode escrever sua infraestrutura com linguagens como TypeScript, Python ou C#, aproveitando:
- Intellisense, testes, controle de fluxo
- Reutilização de código
- Integração com bibliotecas do ecossistema
- Deploy automatizado com CI/CD
Neste exemplo prático, vamos criar:
- Um Resource Group
- Duas VMs Linux no Azure:
- Uma rodando o NGINX
- Outra com o MySQL Server
- Configuração de networking (VNet + Subnet + IP público + NSG)
- E tudo usando Pulumi com TypeScript
Pré-requisitos
- Conta no Azure com permissão de contributor
- Node.js instalado
- Pulumi CLI instalado:
curl -fsSL https://get.pulumi.com | sh
- Login no Azure via CLI:
az login
Estrutura do projeto
azure-pulumi-infra/
├── Pulumi.yaml
├── Pulumi.dev.yaml
├── index.ts
├── package.json
└── tsconfig.json
Inicializando o projeto
pulumi new azure-native-typescript
Escolha um nome para o projeto e a stack (ex: dev)
Código completo (index.ts)
import * as azure from "@pulumi/azure-native";
// 1. Resource Group
const rg = new azure.resources.ResourceGroup("app-rg", {
location: "brazilsouth",
});
// 2. Virtual Network + Subnet
const vnet = new azure.network.VirtualNetwork("vnet", {
resourceGroupName: rg.name,
location: rg.location,
addressSpace: { addressPrefixes: ["10.0.0.0/16"] },
subnets: [{ name: "default", addressPrefix: "10.0.1.0/24" }],
});
// 3. Public IPs
const publicIpNginx = new azure.network.PublicIPAddress("nginx-ip", {
resourceGroupName: rg.name,
location: rg.location,
publicIPAllocationMethod: "Dynamic",
});
const publicIpMySQL = new azure.network.PublicIPAddress("mysql-ip", {
resourceGroupName: rg.name,
location: rg.location,
publicIPAllocationMethod: "Dynamic",
});
// 4. Network Interface (NIC)
const nicNginx = new azure.network.NetworkInterface("nic-nginx", {
resourceGroupName: rg.name,
location: rg.location,
ipConfigurations: [{
name: "ipcfg",
subnet: { id: vnet.subnets.apply(s => s[0].id!) },
privateIPAllocationMethod: "Dynamic",
publicIPAddress: { id: publicIpNginx.id },
}],
});
const nicMySQL = new azure.network.NetworkInterface("nic-mysql", {
resourceGroupName: rg.name,
location: rg.location,
ipConfigurations: [{
name: "ipcfg",
subnet: { id: vnet.subnets.apply(s => s[0].id!) },
privateIPAllocationMethod: "Dynamic",
publicIPAddress: { id: publicIpMySQL.id },
}],
});
// 5. VM com NGINX
const vmNginx = new azure.compute.VirtualMachine("vm-nginx", {
resourceGroupName: rg.name,
location: rg.location,
networkProfile: { networkInterfaces: [{ id: nicNginx.id }] },
hardwareProfile: { vmSize: "Standard_B1s" },
osProfile: {
computerName: "nginxvm",
adminUsername: "azureuser",
adminPassword: "SenhaForte@123", // evite isso em prod, use secrets!
customData: Buffer.from(`#!/bin/bash
sudo apt-get update
sudo apt-get install -y nginx
sudo systemctl start nginx`).toString("base64"),
},
storageProfile: {
osDisk: {
name: "osdisk-nginx",
createOption: "FromImage",
},
imageReference: {
publisher: "Canonical",
offer: "UbuntuServer",
sku: "18.04-LTS",
version: "latest",
},
},
});
// 6. VM com MySQL
const vmMySQL = new azure.compute.VirtualMachine("vm-mysql", {
resourceGroupName: rg.name,
location: rg.location,
networkProfile: { networkInterfaces: [{ id: nicMySQL.id }] },
hardwareProfile: { vmSize: "Standard_B1s" },
osProfile: {
computerName: "mysqlvm",
adminUsername: "azureuser",
adminPassword: "SenhaForte@123",
customData: Buffer.from(`#!/bin/bash
sudo apt-get update
sudo apt-get install -y mysql-server
sudo systemctl start mysql`).toString("base64"),
},
storageProfile: {
osDisk: {
name: "osdisk-mysql",
createOption: "FromImage",
},
imageReference: {
publisher: "Canonical",
offer: "UbuntuServer",
sku: "18.04-LTS",
version: "latest",
},
},
});
// 7. Outputs
export const ipNginx = publicIpNginx.ipAddress;
export const ipMySQL = publicIpMySQL.ipAddress;
Deploy da infraestrutura
npm install
pulumi up
Para destruir o ambiente:
pulumi destroy
Boas práticas
Prática | Justificativa |
---|---|
Use Pulumi Config para senhas e valores sensíveis | Evita hardcoded |
Separe lógica em arquivos por recurso | Melhor manutenção |
Use autoTags , stacks , e environments | Para ambientes dev/prod/test |
Integre com GitHub Actions ou Azure Pipelines | CI/CD de IaC completo |
Combine com Azure Policy e Key Vault | Governança e segurança |
Referências
Conclusão
Pulumi traz agilidade e flexibilidade para equipes de infraestrutura modernas que desejam integrar IaC com as práticas de desenvolvimento. Ao usar TypeScript ou Python, a curva de aprendizado se torna mais acessível e os recursos do Azure podem ser definidos de forma programática e escalável.