Pular para o conteúdo principal

Fundamentação Teórica: Modify an existing Azure Resource Manager template


1. Intuição Inicial​

No módulo anterior, aprendemos a ler e interpretar ARM Templates e arquivos Bicep. Agora o objetivo é diferente: dado um template existente, como você o modifica de forma segura e eficaz para atender a novos requisitos?

Pense na analogia da planta do escritório novamente. Você tem uma planta que funcionou perfeitamente para criar escritórios em dez cidades. Agora a empresa quer adicionar uma sala de servidor dedicada, mudar o mobiliário de madeira para metal, e tornar o tamanho da sala de reuniões configurável por cidade. Você não refaz a planta do zero: você modifica a existente de forma controlada, testando as mudanças antes de aplicar em todas as cidades.

Modificar um template existente envolve três habilidades complementares: entender o que o template faz hoje, saber quais tipos de mudança têm que impacto nos recursos existentes, e aplicar as mudanças de forma que o deploy seja seguro e idempotente.


2. Contexto​

A modificação de templates é a atividade mais comum no ciclo de vida de infraestrutura como código. Templates raramente são criados e nunca mais tocados. À medida que os requisitos evoluem, o template precisa evoluir com eles. A diferença entre um time maduro e um time iniciante em IaC está justamente em como gerenciam essas mudanças:

100%
Scroll para zoom · Arraste para mover · 📱 Pinch para zoom no celular

O AZ-104 avalia especificamente a capacidade de pegar um template existente e modificá-lo para adicionar recursos, tornar valores parametrizáveis, adicionar outputs ou corrigir configurações.


3. Construção dos Conceitos​

3.1 Tipos de Modificação e Seu Impacto​

Nem toda modificação tem o mesmo impacto. Entender a categoria da mudança é o primeiro passo para avaliar o risco:

100%
Scroll para zoom · Arraste para mover · 📱 Pinch para zoom no celular

Ponto crítico sobre remoção: remover um recurso do template em modo Incremental (padrão) não exclui o recurso no Azure. Em modo Complete, o recurso é excluído. Isso significa que o comportamento de uma remoção é completamente diferente dependendo do modo de deploy.

3.2 Propriedades que Causam Recriação de Recursos​

Algumas propriedades de recursos Azure são imutáveis: se você as alterar, o Azure precisa excluir e recriar o recurso. O --what-if mostrará essas mudanças como - Delete seguido de + Create.

Exemplos de propriedades imutáveis que causam recriação:

RecursoPropriedade imutávelImpacto
Storage Accountkind (ex: Storage para StorageV2)Recriação com perda de dados
Cosmos DBkind, apiProperties.serverVersionRecriação
AKSagentPoolProfiles[].vmSizeRecriação do node pool
VNetaddressSpace (redução)Bloqueado se há subnets conflitantes
App Service Plankind (Windows para Linux)Recriação
SQL ServerNão pode alterar nomeNovo recurso

Antes de modificar propriedades estruturais de recursos críticos, sempre execute --what-if para verificar se o ARM sinalizará uma recriação.

3.3 Modificações Comuns: Padrões e Técnicas​

Converter valor hardcoded em parâmetro​

Antes (ARM Template com localização hardcoded):

"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2023-01-01",
"name": "minhaconta",
"location": "brazilsouth",
...
}
]

Depois (com localização parametrizada):

"parameters": {
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Regiao para deploy dos recursos"
}
}
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2023-01-01",
"name": "minhaconta",
"location": "[parameters('location')]",
...
}
]

O valor padrão [resourceGroup().location] usa a localização do resource group onde o template é deployado, o que é uma boa prática para evitar problemas de região.

Adicionar um novo recurso ao template​

O cenário mais comum: um template que cria uma storage account precisa ser modificado para também criar um container de blob dentro dela.

Antes:

"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2023-01-01",
"name": "[parameters('storageAccountName')]",
"location": "[parameters('location')]",
"sku": { "name": "Standard_LRS" },
"kind": "StorageV2",
"properties": {}
}
]

Depois (com blob service e container adicionados):

"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2023-01-01",
"name": "[parameters('storageAccountName')]",
"location": "[parameters('location')]",
"sku": { "name": "Standard_LRS" },
"kind": "StorageV2",
"properties": {}
},
{
"type": "Microsoft.Storage/storageAccounts/blobServices",
"apiVersion": "2023-01-01",
"name": "[concat(parameters('storageAccountName'), '/default')]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]"
],
"properties": {
"deleteRetentionPolicy": {
"enabled": true,
"days": 7
}
}
},
{
"type": "Microsoft.Storage/storageAccounts/blobServices/containers",
"apiVersion": "2023-01-01",
"name": "[concat(parameters('storageAccountName'), '/default/', parameters('containerName'))]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('storageAccountName'), 'default')]"
],
"properties": {
"publicAccess": "None"
}
}
]

O padrão de nomes para recursos filhos em ARM usa / como separador: storageAccountName/default/containerName.

A mesma modificação em Bicep​

O Bicep torna esse padrão hierárquico muito mais legível com parent:

// Adicionar ao Bicep existente
resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2023-01-01' = {
parent: storageAccount // referência simbólica ao recurso pai
name: 'default'
properties: {
deleteRetentionPolicy: {
enabled: true
days: 7
}
}
}

param containerName string = 'meu-container'

resource blobContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = {
parent: blobService
name: containerName
properties: {
publicAccess: 'None'
}
}

Adicionar um output​

Um template existente que cria uma storage account pode precisar retornar o endpoint para uso em um pipeline:

// ARM: adicionar à seção outputs
"outputs": {
"blobEndpoint": {
"type": "string",
"value": "[reference(parameters('storageAccountName')).primaryEndpoints.blob]"
},
"storageAccountId": {
"type": "string",
"value": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]"
}
}
// Bicep: adicionar ao final do arquivo
output blobEndpoint string = storageAccount.properties.primaryEndpoints.blob
output storageAccountId string = storageAccount.id

Adicionar tags a todos os recursos​

Tags são importantes para organização e custo. Adicionar um parâmetro de tags e aplicar a todos os recursos:

// ARM: adicionar parâmetro
"parameters": {
"tags": {
"type": "object",
"defaultValue": {
"ambiente": "producao",
"projeto": "sistema-x",
"gerenciado-por": "iac"
}
}
},
// Em cada recurso:
"tags": "[parameters('tags')]"
// Bicep: parâmetro com valor padrão
param tags object = {
ambiente: 'producao'
projeto: 'sistema-x'
geradoPor: 'iac'
}

// Em cada recurso, adicionar:
tags: tags

Usar a função if() para recursos condicionais​

Um cenário comum é querer criar um recurso apenas em produção, não em desenvolvimento:

// ARM: condition no recurso
{
"type": "Microsoft.Insights/diagnosticSettings",
"condition": "[equals(parameters('ambiente'), 'prod')]",
"name": "diagnostic-settings",
"apiVersion": "2021-05-01-preview",
"scope": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]",
"properties": { ... }
}
// Bicep: condition no recurso
param ambiente string = 'dev'

resource diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (ambiente == 'prod') {
name: 'diag-storage'
scope: storageAccount
properties: { ... }
}

Usar copy para criar múltiplas instâncias (ARM) ou for (Bicep)​

Criar múltiplas subnets em um VNet:

// ARM: copy element
{
"type": "Microsoft.Network/virtualNetworks/subnets",
"apiVersion": "2023-05-01",
"name": "[concat(parameters('vnetName'), '/', parameters('subnets')[copyIndex()].name)]",
"copy": {
"name": "subnetCopy",
"count": "[length(parameters('subnets'))]"
},
"dependsOn": [
"[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
],
"properties": {
"addressPrefix": "[parameters('subnets')[copyIndex()].prefix]"
}
}
// Bicep: for loop (muito mais legível)
param subnets array = [
{ name: 'subnet-frontend', prefix: '10.0.1.0/24' }
{ name: 'subnet-backend', prefix: '10.0.2.0/24' }
{ name: 'subnet-database', prefix: '10.0.3.0/24' }
]

resource subnetResources 'Microsoft.Network/virtualNetworks/subnets@2023-05-01' = [for subnet in subnets: {
parent: vnet
name: subnet.name
properties: {
addressPrefix: subnet.prefix
}
}]

4. Visão Estrutural​

O Processo de Modificação Segura​

100%
Scroll para zoom · Arraste para mover · 📱 Pinch para zoom no celular

5. Funcionamento na Prática​

Obter o ARM Template de um Recurso Existente​

Uma das fontes mais práticas de templates para modificar é exportar o template de recursos já existentes no Azure:

Via portal: Resource Group > Export template (exporta todos os recursos do RG) ou [Recurso específico] > Export template

Via CLI:

# Exportar template de um resource group completo
az group export \
--name rg-producao \
--output-format json > template-exportado.json

# Exportar template de um recurso específico
az resource show \
--ids /subscriptions/<sub-id>/resourceGroups/rg-producao/providers/Microsoft.Storage/storageAccounts/minhaconta \
--output json > recurso-exportado.json

Atenção: templates exportados pelo portal frequentemente contêm valores hardcoded, senhas omitidas (substituídas por null), e algumas propriedades de runtime que não devem estar no template. Sempre revise e limpe o template exportado antes de usá-lo como base.

Decompilação: De ARM para Bicep​

Quando você tem um ARM Template existente e quer trabalhar em Bicep:

az bicep decompile --file template-exportado.json

Isso gera um arquivo .bicep equivalente. A qualidade da decompilação é boa mas não perfeita: o arquivo gerado pode precisar de ajustes manuais, especialmente em templates com copy loops, reference() complexos ou propriedades de runtime.

Verificar Mudanças em Propriedades com --what-if​

Após modificar um template que afeta um recurso existente, o --what-if é indispensável:

az deployment group what-if \
--resource-group rg-producao \
--template-file main.bicep \
--parameters @parameters.prod.json \
--result-format FullResourcePayloads

O parâmetro --result-format FullResourcePayloads mostra o payload completo do recurso após as mudanças, útil para verificar exatamente quais propriedades serão alteradas.

O resultado codificado por cores:

  • + Create: recurso novo
  • ~ Modify: recurso existente que será alterado
  • - Delete: recurso que será excluído (apenas em modo Complete)
  • = Nochange: recurso sem alterações
  • x Ignore: recurso ignorado pelo ARM (gerenciado externamente)

6. Formas de Implementação​

6.1 VS Code com Extensões​

Quando usar: edição do dia a dia. O VS Code com as extensões corretas transforma a experiência de editar templates.

Extensões essenciais:

  • Bicep (Microsoft): autocomplete, validação em tempo real, visualização de recursos para Bicep
  • ARM Tools (Microsoft): schema validation, snippets e IntelliSense para ARM JSON
  • Azure Resource Manager (ARM) Template Viewer: visualização gráfica do template

Vantagens: feedback imediato de erros, autocomplete de tipos de recurso, propriedades e valores, linting integrado.

6.2 Azure CLI: Validação e Deploy​

# Validar o template modificado ANTES de deployar
az deployment group validate \
--resource-group rg-producao \
--template-file main.bicep \
--parameters @parameters.prod.json

# What-If para ver o impacto ANTES de aplicar
az deployment group what-if \
--resource-group rg-producao \
--template-file main.bicep \
--parameters @parameters.prod.json

# Deploy com confirmação interativa do what-if
az deployment group create \
--resource-group rg-producao \
--template-file main.bicep \
--parameters @parameters.prod.json \
--confirm-with-what-if

O flag --confirm-with-what-if executa o what-if primeiro e pede confirmação antes de prosseguir com o deploy. É o fluxo mais seguro para deploys em produção interativos.

6.3 Portal do Azure: Editor de Templates​

O portal tem um editor integrado em Create a resource > Deploy a custom template > Build your own template in the editor. Útil para edições menores ou para testes rápidos, mas não substitui o fluxo de controle de versão para mudanças em produção.


7. Controle e Segurança​

Controle de Versão para Templates​

Templates devem estar em Git com o mesmo rigor que código de aplicação:

  • Cada modificação em branch separada
  • Pull Request com revisão antes de merge
  • CI/CD pipeline que valida e deploya automaticamente após merge
  • Tags para versões estáveis dos templates

Nunca modifique um template de produção diretamente sem passar pelo processo de PR.

Manter o Princípio da Idempotência​

Um template modificado deve continuar sendo idempotente: deployar o mesmo template múltiplas vezes deve sempre resultar no mesmo estado, sem erros no segundo deploy.

Problemas comuns de idempotência após modificações:

  • Usar newGuid() em um nome de recurso: gera nome diferente a cada deploy, criando recursos duplicados
  • Verificar condições em recursos que devem ser únicos: se a condição muda entre deploys, o recurso pode ser criado ou excluído inesperadamente
  • Propriedades que o ARM não consegue comparar corretamente, gerando "updates" desnecessários

Testar a Modificação em Ambiente Isolado​

Antes de aplicar uma modificação em produção, sempre deploy em um resource group de teste dedicado:

# Criar RG de teste temporário
az group create \
--name rg-teste-modificacao \
--location brazilsouth

# Deploy do template modificado no RG de teste
az deployment group create \
--resource-group rg-teste-modificacao \
--template-file main-modificado.bicep \
--parameters @parameters.dev.json

# Verificar resultado
az resource list \
--resource-group rg-teste-modificacao \
--output table

# Limpar após teste
az group delete --name rg-teste-modificacao --yes --no-wait

8. Tomada de Decisão​

Quando modificar o template vs. criar um novo?​

SituaçãoDecisãoMotivo
Adicionar configuração a recurso existenteModificarMenor impacto, evolução incremental
Adicionar novos recursos ao ambienteModificarManter tudo no mesmo template para coerência
Mudança arquitetural radical (ex: trocar LB por App Gateway)Novo template ou branch experimentalRisco de impacto em recursos existentes
Refatoração para usar módulosNova estrutura com módulos + testesMudança estrutural que não afeta recursos finais
Template de terceiro que não pode ser editadoTemplate Spec ou módulo wrapperEncapsular o template externo

Modificar ARM JSON vs. converter para Bicep?​

SituaçãoDecisãoMotivo
Template simples, time usa ARMModificar o ARM JSONMenor esforço
Template complexo com muita duplicaçãoConverter para BicepBicep reduz drasticamente a verbosidade
Time novo em IaCConverter para BicepMelhor DX e curva de aprendizado
Integração que só aceita ARM JSONManter ARM, usar Bicep como fonteBicep compila para ARM; usar Bicep como fonte e compilar

9. Boas Práticas​

Use --confirm-with-what-if em todos os deploys de produção: o custo de revisar o what-if é segundos; o custo de uma modificação inesperada pode ser horas de recovery.

Mantenha um arquivo de parâmetros por ambiente, nunca misture valores: parameters.dev.json, parameters.staging.json, parameters.prod.json. Nunca use o mesmo arquivo para ambientes diferentes, mesmo que apenas um valor mude.

Ao parametrizar um valor hardcoded, preserve o valor original como default: se o template tinha "location": "brazilsouth" hardcoded e você vai parametrizar, faça "defaultValue": "brazilsouth". Isso garante que um deploy sem passar o parâmetro novo continue funcionando com o mesmo comportamento do template original.

Documente cada modificação com comentários (em Bicep, use //):

// Adicionado em 2024-03: configuração de soft delete para blobs
// Issue #123: requisito de compliance para retenção de 7 dias
resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2023-01-01' = {
parent: storageAccount
name: 'default'
properties: {
deleteRetentionPolicy: {
enabled: true
days: 7
}
}
}

Verifique a apiVersion ao adicionar novas propriedades: uma propriedade nova pode exigir uma apiVersion mais recente. Se você adicionar uma propriedade e o deploy falhar com erro de propriedade não reconhecida, verifique se a apiVersion do recurso suporta essa propriedade.


10. Erros Comuns​

Adicionar parâmetro sem valor default e quebrar deploys de CI/CD existentes

Um novo parâmetro newFeatureEnabled é adicionado ao template sem defaultValue. O pipeline de CI/CD não passa esse parâmetro e o deploy falha com erro de parâmetro obrigatório não fornecido. Sempre adicione defaultValue em novos parâmetros que não são fornecidos por todos os pipelines existentes.

Alterar o nome de um recurso existente sem entender o impacto

O template tinha "name": "minha-storage" e foi modificado para "name": "[parameters('storageName')]", com o parâmetro recebendo "nova-storage". No próximo deploy, o ARM cria nova-storage como novo recurso e o recurso minha-storage permanece (modo Incremental) ou é excluído (modo Complete). Não é uma "renomeação": é uma criação de novo recurso. Recursos Azure não podem ser renomeados.

Modificar a apiVersion de um recurso sem verificar breaking changes

Atualizar apiVersion de 2020-01-01 para 2023-05-01 pode mudar o comportamento de algumas propriedades ou tornar propriedades obrigatórias que antes eram opcionais. Consulte sempre o changelog da API do resource provider ao atualizar a versão.

Remover uma propriedade esperando que o valor volte ao padrão

Em muitos recursos Azure, remover uma propriedade do template não reseta o valor para o padrão: o ARM mantém o valor atual do recurso. Para resetar uma propriedade para o padrão, você precisa defini-la explicitamente com o valor padrão no template.

Editar diretamente o template exportado do portal sem limpar

Templates exportados pelo portal frequentemente contêm: propriedades de runtime (como etags, ids gerados), propriedades que o ARM não aceita na criação, e valores hardcoded que deveriam ser parâmetros. Usar o template exportado diretamente sem revisão pode causar erros de deploy ou criar dependências acidentais em valores específicos do ambiente de origem.


11. Operação e Manutenção​

Rastrear o Histórico de Modificações​

O histórico de deploy do ARM registra cada deploy com nome, timestamp e status. Para rastrear modificações ao longo do tempo, use nomes de deploy descritivos:

az deployment group create \
--resource-group rg-producao \
--template-file main.bicep \
--parameters @parameters.prod.json \
--name "add-blob-container-issue-123"

Com nomes descritivos, o histórico de deploys se torna uma timeline das modificações:

az deployment group list \
--resource-group rg-producao \
--query "[].{Nome:name, Data:properties.timestamp, Status:properties.provisioningState}" \
--output table

Comparar o Estado Atual com o Template​

Para verificar se o estado atual dos recursos está sincronizado com o template (drift detection):

az deployment group what-if \
--resource-group rg-producao \
--template-file main.bicep \
--parameters @parameters.prod.json \
--mode Complete

Se o what-if em modo Complete mostrar mudanças, significa que o estado atual divergiu do template (alguém modificou recursos manualmente ou via portal). As mudanças indicadas são o que seria necessário para sincronizar o estado real com o template.


12. Integração e Automação​

Pipeline de Validação de Template no PR​

Um pipeline que roda em cada Pull Request para validar modificações:

# .github/workflows/validate-template.yml
name: Validate Template Changes

on:
pull_request:
paths: ['infrastructure/**']

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Azure Login
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}

- name: Lint Bicep
run: az bicep build --file infrastructure/main.bicep

- name: Validate against dev
run: |
az deployment group validate \
--resource-group rg-dev \
--template-file infrastructure/main.bicep \
--parameters @infrastructure/parameters.dev.json

- name: What-If against staging
run: |
az deployment group what-if \
--resource-group rg-staging \
--template-file infrastructure/main.bicep \
--parameters @infrastructure/parameters.staging.json \
--output table

Esse pipeline garante que nenhuma modificação com erros de sintaxe ou que quebre o deploy em staging chegue à branch principal.

Módulos Reutilizáveis para Padronização​

Ao modificar múltiplos templates que compartilham padrões, extraia o padrão para um módulo:

100%
Scroll para zoom · Arraste para mover · 📱 Pinch para zoom no celular

Quando você modifica o módulo storage.bicep, todos os templates que o importam recebem automaticamente a modificação no próximo deploy, garantindo consistência.


13. Resumo Final​

Pontos essenciais:

  • Modificar um template existente requer entender o impacto da mudança: adicionar é baixo risco, alterar propriedades pode causar recriação de recursos, remover tem comportamento diferente em modo Incremental vs. Complete.
  • O --what-if é indispensável antes de qualquer deploy de template modificado em produção. Ele mostra exatamente quais recursos serão criados (+), modificados (~), excluídos (-) ou não alterados (=).
  • Ao parametrizar um valor hardcoded, sempre preserve o valor original como defaultValue para não quebrar deploys existentes que não passam o novo parâmetro.

Diferenças críticas:

  • Renomear um recurso no template não renomeia o recurso Azure: cria um novo com o novo nome e o antigo permanece (modo Incremental) ou é excluído (modo Complete).
  • Remover uma propriedade do template frequentemente não reseta o valor para o padrão: o ARM mantém o valor atual do recurso. Para resetar, defina explicitamente o valor padrão.
  • az deployment group validate verifica sintaxe e schema mas não simula o impacto. --what-if simula o impacto real nos recursos existentes.
  • Template exportado do portal contém propriedades de runtime e valores hardcoded que precisam de limpeza antes do uso.

O que precisa ser lembrado:

  • Ao adicionar um novo recurso filho (ex: blob container dentro de storage account), use parent: em Bicep ou o padrão de nome pai/filho em ARM, e garanta a dependência explícita via dependsOn ou reference().
  • Recursos com propriedades imutáveis (ex: kind em storage accounts) causam recriação quando alterados. O --what-if sinaliza isso como -Delete + +Create.
  • O comando az bicep decompile converte ARM JSON para Bicep como ponto de partida, mas o resultado sempre precisa de revisão manual.
  • Sempre use --confirm-with-what-if em deploys interativos de produção para ter uma última chance de revisar o impacto antes de confirmar.
  • O histórico de deploys tem limite de 800 entradas por resource group. Use nomes de deploy descritivos para rastreabilidade e configure limpeza automática em ambientes com deploys frequentes.