Simplificando a Implantação de Projetos no Azure com JSON e ARM Templates
A administração de ambientes Azure envolve frequentemente a criação e configuração de recursos. Para otimizar e padronizar essas operações, os Azure Resource Manager (ARM) templates, escritos em JSON, são ferramentas indispensáveis. Eles permitem que você defina a infraestrutura como código, garantindo consistência, repetibilidade e minimizando erros manuais.
Neste artigo, exploraremos um JSON modelo que simplifica a implantação de um novo projeto no Azure, criando um Grupo de Recursos, uma Rede Virtual (VNet) e uma Máquina Virtual (VM) Linux básica. O objetivo é fornecer um template flexível que possa ser facilmente adaptado por outros administradores, alterando apenas alguns parâmetros-chave.
JSON 1: Implantação de um Novo Projeto no Azure (Grupo de Recursos, VNet e VM)
Este template é projetado para criar o ambiente básico para um novo projeto no Azure. Ele engloba a criação de um grupo de recursos para organizar seus recursos, uma rede virtual para isolamento de rede e uma máquina virtual Linux para hospedar suas aplicações.
Objetivo: Automatizar a criação de um ambiente de projeto inicial no Azure, incluindo:
- Um novo Grupo de Recursos.
- Uma Rede Virtual (VNet) com uma sub-rede.
- Uma Máquina Virtual (VM) Linux com IP público e regras de NSG para SSH.
Serviços do Azure Envolvidos: Azure Resource Manager, Azure Virtual Networks, Azure Virtual Machines, Azure Network Security Groups.
O JSON Modelo:
JSON
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"resourceGroupName": {
"type": "string",
"metadata": {
"description": "Nome do novo Grupo de Recursos para o projeto."
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Localização (região do Azure) para os recursos."
}
},
"vnetName": {
"type": "string",
"defaultValue": "[concat('vnet-', parameters('resourceGroupName'))]",
"metadata": {
"description": "Nome da Rede Virtual."
}
},
"subnetName": {
"type": "string",
"defaultValue": "default",
"metadata": {
"description": "Nome da Sub-rede dentro da Rede Virtual."
}
},
"vnetAddressPrefix": {
"type": "string",
"defaultValue": "10.0.0.0/16",
"metadata": {
"description": "Prefixo de endereço para a VNet."
}
},
"subnetAddressPrefix": {
"type": "string",
"defaultValue": "10.0.0.0/24",
"metadata": {
"description": "Prefixo de endereço para a Sub-rede."
}
},
"vmName": {
"type": "string",
"defaultValue": "[concat('vm-', parameters('resourceGroupName'))]",
"metadata": {
"description": "Nome da Máquina Virtual."
}
},
"vmSize": {
"type": "string",
"defaultValue": "Standard_DS1_v2",
"metadata": {
"description": "Tamanho da Máquina Virtual (ex: Standard_DS1_v2, Standard_B2s)."
}
},
"adminUsername": {
"type": "string",
"metadata": {
"description": "Nome de usuário administrador para a VM Linux."
}
},
"adminPassword": {
"type": "securestring",
"metadata": {
"description": "Senha administrador para a VM Linux. Use senhas fortes!"
}
},
"ubuntuOSVersion": {
"type": "string",
"defaultValue": "latest",
"allowedValues": [
"18.04-LTS",
"20.04-LTS",
"22.04-LTS",
"latest"
],
"metadata": {
"description": "Versão do Ubuntu Server para a VM."
}
},
"sshPortNumber": {
"type": "int",
"defaultValue": 22,
"metadata": {
"description": "Número da porta para SSH. Mude se não for 22."
}
}
},
"variables": {
"publicIpAddressName": "[concat(parameters('vmName'), '-ip')]",
"networkSecurityGroupName": "[concat(parameters('vmName'), '-nsg')]",
"networkInterfaceName": "[concat(parameters('vmName'), '-nic')]"
},
"resources": [
{
"type": "Microsoft.Resources/resourceGroups",
"apiVersion": "2021-04-01",
"name": "[parameters('resourceGroupName')]",
"location": "[parameters('location')]",
"properties": {}
},
{
"type": "Microsoft.Network/virtualNetworks",
"apiVersion": "2023-09-01",
"name": "[parameters('vnetName')]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]"
],
"properties": {
"addressSpace": {
"addressPrefixes": [
"[parameters('vnetAddressPrefix')]"
]
},
"subnets": [
{
"name": "[parameters('subnetName')]",
"properties": {
"addressPrefix": "[parameters('subnetAddressPrefix')]"
}
}
]
}
},
{
"type": "Microsoft.Network/networkSecurityGroups",
"apiVersion": "2023-09-01",
"name": "[variables('networkSecurityGroupName')]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
],
"properties": {
"securityRules": [
{
"name": "AllowSSH",
"properties": {
"description": "Allow SSH access",
"protocol": "Tcp",
"sourcePortRange": "*",
"destinationPortRange": "[parameters('sshPortNumber')]",
"sourceAddressPrefix": "Internet",
"destinationAddressPrefix": "*",
"access": "Allow",
"priority": 100,
"direction": "Inbound"
}
}
]
}
},
{
"type": "Microsoft.Network/publicIPAddresses",
"apiVersion": "2023-09-01",
"name": "[variables('publicIpAddressName')]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]"
],
"properties": {
"publicIPAllocationMethod": "Dynamic",
"dnsSettings": {
"domainNameLabel": "[concat(parameters('vmName'), '-', uniqueString(resourceGroup().id))]"
}
}
},
{
"type": "Microsoft.Network/networkInterfaces",
"apiVersion": "2023-09-01",
"name": "[variables('networkInterfaceName')]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIpAddressName'))]",
"[resourceId('Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName'))]",
"[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
],
"properties": {
"ipConfigurations": [
{
"name": "ipconfig1",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIpAddressName'))]"
},
"subnet": {
"id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('subnetName'))]"
}
}
}
],
"networkSecurityGroup": {
"id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName'))]"
}
}
},
{
"type": "Microsoft.Compute/virtualMachines",
"apiVersion": "2023-09-01",
"name": "[parameters('vmName')]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Network/networkInterfaces', variables('networkInterfaceName'))]"
],
"properties": {
"hardwareProfile": {
"vmSize": "[parameters('vmSize')]"
},
"osProfile": {
"computerName": "[parameters('vmName')]",
"adminUsername": "[parameters('adminUsername')]",
"adminPassword": "[parameters('adminPassword')]"
},
"storageProfile": {
"imageReference": {
"publisher": "Canonical",
"offer": "0001-com-ubuntu-server-jammy",
"sku": "[parameters('ubuntuOSVersion') == '18.04-LTS' ? '18_04-lts-gen2' : parameters('ubuntuOSVersion') == '20.04-LTS' ? '20_04-lts-gen2' : '22_04-lts-gen2']",
"version": "latest"
},
"osDisk": {
"createOption": "FromImage",
"managedDisk": {
"storageAccountType": "Standard_LRS"
}
}
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('networkInterfaceName'))]"
}
]
}
}
}
],
"outputs": {
"resourceGroupNameOutput": {
"type": "string",
"value": "[parameters('resourceGroupName')]",
"metadata": {
"description": "Nome do Grupo de Recursos criado."
}
},
"publicIpAddress": {
"type": "string",
"value": "[reference(resourceId('Microsoft.Network/publicIPAddresses', variables('publicIpAddressName'))).ipAddress]",
"metadata": {
"description": "Endereço IP público da VM."
}
},
"sshCommand": {
"type": "string",
"value": "[concat('ssh ', parameters('adminUsername'), '@', reference(resourceId('Microsoft.Network/publicIPAddresses', variables('publicIpAddressName'))).ipAddress)]",
"metadata": {
"description": "Comando SSH para conectar à VM."
}
}
}
}
Explicação Detalhada do JSON:
- $schema e contentVersion: Define o tipo de documento e a versão do template ARM.
- parameters: Seção crucial para a flexibilidade. Aqui você define as entradas que os administradores podem personalizar.
- resourceGroupName, location, vnetName, vmName, adminUsername, adminPassword, vmSize: Parâmetros essenciais que garantem a unicidade e configurabilidade do projeto.
- defaultValue: Muitos parâmetros têm valores padrão, facilitando o uso rápido sem necessidade de especificar todos eles.
- allowedValues: Restringe as opções para alguns parâmetros (ex: ubuntuOSVersion), evitando erros de digitação.
- securestring: Para senhas, garante que o valor não seja logado abertamente.
- variables: Define valores que são computados durante a implantação, geralmente para construir nomes de recursos derivados dos parâmetros. Isso mantém os nomes consistentes.
- resources: A parte principal onde cada recurso do Azure é definido. Cada recurso tem:
- type: O tipo de recurso (ex: Microsoft.Network/virtualNetworks).
- apiVersion: A versão da API do Azure Resource Provider para o recurso.
- name: O nome do recurso.
- location: A região do Azure onde o recurso será implantado.
- dependsOn: Garante a ordem correta de implantação. Por exemplo, a VM depende da NIC, que depende da Public IP e da NSG, que dependem da VNet.
- properties: As configurações específicas do recurso (endereços IP, tamanho da VM, nome de usuário, etc.).
- outputs: Retorna informações úteis após a implantação, como o nome do Grupo de Recursos, o IP público da VM e até mesmo um comando SSH formatado para acesso rápido.
Demonstração com Azure CLI:
Para usar este template, salve-o em um arquivo chamado deploy_project.json.
- Prepare o arquivo de parâmetros (opcional, mas recomendado para reutilização): Crie um arquivo chamado parameters.json para definir os valores dos parâmetros. Isso é ideal para que outros administradores apenas alterem este arquivo.
JSON
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"resourceGroupName": {
"value": "meuNovoProjetoRG" // Substitua pelo nome do seu Grupo de Recursos
},
"location": {
"value": "eastus" // Substitua pela região desejada
},
"vmName": {
"value": "meuServidorWeb" // Substitua pelo nome da sua VM
},
"vmSize": {
"value": "Standard_B2s" // Opcional: mude o tamanho da VM
},
"adminUsername": {
"value": "azureuser" // Seu nome de usuário admin
},
"adminPassword": {
"value": "SuaSenhaMuitoSegura123!" // Sua senha forte
},
"sshPortNumber": {
"value": 22 // Opcional: Mude a porta SSH se necessário
}
}
}
Importante: Nunca armazene senhas em texto puro em arquivos de controle de versão. Para ambientes de produção, use o Azure Key Vault para armazenar segredos e referencie-os nos templates.
- Execute a implantação via Azure CLI:
Abra seu terminal ou Azure Cloud Shell e execute o seguinte comando:
Bash
az deployment group create \
--name "ProjetoNovoDeploy" \
--template-file deploy_project.json \
--parameters @parameters.json
- --name "ProjetoNovoDeploy": Um nome para sua implantação.
- --template-file deploy_project.json: O caminho para o arquivo JSON do seu template.
- --parameters @parameters.json: O caminho para o arquivo JSON com os valores dos parâmetros. O @ indica que é um arquivo.
Alternativamente, você pode passar os parâmetros diretamente na linha de comando, sem um arquivo de parâmetros:
Bash
az deployment group create \
--name "ProjetoNovoDeploy" \
--template-file deploy_project.json \
--parameters resourceGroupName=meuNovoProjetoRG location=eastus vmName=meuServidorWeb adminUsername=azureuser adminPassword="SuaSenhaMuitoSegura123!"
Cuidado: Ao passar senhas diretamente na linha de comando, elas podem aparecer no histórico do seu shell. O uso de um arquivo de parâmetros ou Key Vault é mais seguro.
Particularidades e Dicas Importantes:
- Reutilização: Este template é um ponto de partida excelente. Você pode estendê-lo para incluir outros recursos como contas de armazenamento, bancos de dados, App Services, etc.
- Parâmetros: Utilize os parâmetros para tornar seu template dinâmico e reutilizável. Evite "hard-coding" valores dentro da seção resources.
- dependsOn: Essencial para garantir que os recursos sejam criados na ordem correta. O Azure não tentará criar uma NIC antes que a VNet exista, por exemplo.
- Versões da API (apiVersion): Sempre use as versões mais recentes das APIs para os tipos de recurso que você está implantando. Consulte a documentação do Azure para as versões mais atuais.
- Saídas (outputs): Use as saídas para recuperar informações importantes sobre os recursos implantados, como IPs públicos ou URLs, que podem ser úteis para scripts subsequentes ou para o usuário final.
- Validação: Antes de implantar, você pode validar o template usando az deployment group validate --resource-group <nome-do-grupo> --template-file deploy_project.json --parameters @parameters.json. Isso verifica a sintaxe e a lógica sem realmente criar recursos.
- What-if: Use az deployment group what-if --resource-group <nome-do-grupo> --template-file deploy_project.json --parameters @parameters.json para ver as mudanças que a implantação faria no seu ambiente sem aplicá-las.
Integrando o Azure Key Vault para Segurança de Credenciais
Conforme mencionado, armazenar senhas diretamente nos arquivos de parâmetros não é uma prática segura. O Azure Key Vault é o serviço ideal para gerenciar e proteger chaves criptográficas, certificados e, crucialmente, segredos (como senhas).
Para utilizar senhas armazenadas no Key Vault em seu ARM template, você precisará de algumas etapas:
- Criar e configurar um Azure Key Vault: O Key Vault deve existir antes da implantação do template. Você pode criá-lo via portal, CLI ou até mesmo outro ARM template. Certifique-se de que a identidade que realiza a implantação (usuário, Service Principal ou Managed Identity) tenha permissões de Get para os segredos no Key Vault.
Exemplo de criação de Key Vault via CLI:
Bash
# Crie um grupo de recursos para o Key Vault (se ainda não tiver um)
az group create --name "myKeyVaultRG" --location "eastus"
# Crie o Key Vault
az keyvault create \
--name "mySecureKeyVault123" \
--resource-group "myKeyVaultRG" \
--location "eastus" \
--sku "Standard"
# Adicione um segredo (a senha da VM)
az keyvault secret set \
--vault-name "mySecureKeyVault123" \
--name "vmAdminPassword" \
--value "SuaSenhaUltraSecretaParaVM!"
Importante: Conceda permissões de acesso ao Key Vault para a entidade que fará a implantação. Por exemplo, se for você com sua conta de usuário:
Bash
az keyvault set-policy \
--name "mySecureKeyVault123" \
--resource-group "myKeyVaultRG" \
--object-id $(az ad signed-in-user show --query id -o tsv) \
--secret-permissions get list
(Substitua $(az ad signed-in-user show --query id -o tsv) pelo Object ID da Service Principal ou Managed Identity, se aplicável).
- Modificar o parâmetro da senha no ARM Template: No seu arquivo deploy_project.json, você manterá o parâmetro adminPassword como securestring. Não há alteração neste arquivo.
- Modificar o arquivo de parâmetros para referenciar o Key Vault: Esta é a mudança chave. Em vez de fornecer a senha diretamente, você referencia o segredo no Key Vault. Crie ou modifique seu parameters.json da seguinte forma:
JSON
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"resourceGroupName": {
"value": "meuNovoProjetoRG"
},
"location": {
"value": "eastus"
},
"vmName": {
"value": "meuServidorWeb"
},
"vmSize": {
"value": "Standard_B2s"
},
"adminUsername": {
"value": "azureuser"
},
"adminPassword": {
"reference": {
"keyVault": {
"id": "/subscriptions/<SUA_SUBSCRIPTION_ID>/resourceGroups/myKeyVaultRG/providers/Microsoft.KeyVault/vaults/mySecureKeyVault123"
},
"secretName": "vmAdminPassword"
// "secretVersion": "OptionalVersion" // Opcional: use uma versão específica do segredo
}
},
"sshPortNumber": {
"value": 22
}
}
}
Lembre-se de substituir <SUA_SUBSCRIPTION_ID> pelo ID da sua assinatura do Azure. Você pode obter seu ID de assinatura com az account show --query id -o tsv.
Como Funciona:
Quando você executa a implantação do ARM template com este arquivo de parâmetros, o Azure Resource Manager faz uma chamada segura ao Key Vault para recuperar o segredo (vmAdminPassword) antes de provisionar a VM. Dessa forma, a senha nunca é exposta em texto simples no template, nos arquivos de parâmetros ou nos logs de implantação.
Demonstração de Implantação com Key Vault:
A execução do comando no Azure CLI permanece a mesma:
Bash
az deployment group create \
--name "ProjetoNovoDeployComKV" \
--template-file deploy_project.json \
--parameters @parameters.json
Com esta abordagem, você eleva significativamente a segurança de suas implantações no Azure, gerenciando credenciais de forma centralizada e segura com o Key Vault.
Este template oferece uma base sólida para a automação de novas implantações de projetos no Azure. Ao dominá-lo, os administradores podem garantir que os ambientes sejam configurados de forma padronizada e eficiente, liberando tempo para tarefas mais estratégicas.