Fundamentação Teórica: Modify an existing Bicep file
1. Intuição Inicial​
Nos dois módulos anteriores, aprendemos a interpretar templates (ler e entender) e a modificar ARM Templates JSON (editar o formato tradicional). Este módulo foca especificamente nas técnicas e particularidades de modificar arquivos Bicep.
Embora o Bicep e o ARM Template descrevam a mesma infraestrutura, eles têm sintaxes muito diferentes, e as técnicas de modificação têm nuances especÃficas de cada linguagem. Modificar um arquivo Bicep se parece muito mais com programar do que com editar um documento de configuração. O Bicep tem tipos, expressões, loops, condicionais e módulos. Isso significa que você pode aplicar princÃpios de engenharia de software diretamente na infraestrutura.
A analogia mais precisa: modificar um ARM JSON é como editar uma planta em formato de tabela de especificações. Modificar um arquivo Bicep é como editar código-fonte de um programa. As ferramentas são melhores, o código é mais legÃvel, e os erros são detectados antes do deploy.
2. Contexto​
O Bicep é a linguagem preferida pela Microsoft para IaC no Azure desde 2021. Ao contrário do ARM JSON que é gerado por ferramentas e raramente escrito à mão, o Bicep é projetado para ser escrito, lido e modificado por humanos.
O Bicep compila para ARM Template antes do deploy. Você pode chamar az deployment group create diretamente com um arquivo .bicep e a CLI compila automaticamente. Isso significa que modificar um Bicep é a forma moderna de modificar infraestrutura Azure.
3. Construção dos Conceitos​
3.1 Anatomia de um Arquivo Bicep: O que Pode Ser Modificado​
Um arquivo Bicep é composto por elementos declarativos que você pode adicionar, alterar ou remover:
3.2 Modificações em Parâmetros: Decorators​
O sistema de decorators do Bicep é muito mais rico que as propriedades equivalentes em ARM JSON. Modificar parâmetros em Bicep significa adicionar, remover ou alterar decorators:
// Parâmetro simples (antes da modificação)
param storageAccountName string
// Parâmetro com validação completa (após modificação)
@description('Nome da storage account. Deve ser globalmente unico, apenas letras minusculas e numeros.')
@minLength(3)
@maxLength(24)
param storageAccountName string
// Adicionar restrições de valores permitidos
@description('SKU da storage account')
@allowed([
'Standard_LRS'
'Standard_GRS'
'Standard_ZRS'
'Premium_LRS'
])
param storageSku string = 'Standard_LRS'
// Tornar parâmetro seguro (não aparece em logs)
@secure()
param adminPassword string
O decorator @secure() é especialmente importante: ele faz o Bicep marcar o parâmetro como securestring no ARM Template compilado. Isso impede que o valor apareça no histórico de deploys ou nos logs do Azure Monitor.
3.3 Modificações em Variáveis: Expressões e Interpolação​
Variáveis no Bicep suportam expressões completas, incluindo o operador ternário ?: (if/else inline):
// Variável simples
var storageSkuName = 'Standard_LRS'
// Variável condicional (modificação para suportar múltiplos ambientes)
param ambiente string = 'dev'
var storageSkuName = ambiente == 'prod' ? 'Standard_GRS' : 'Standard_LRS'
Interpolação de string: diferente do ARM Template que usa concat(), o Bicep usa interpolação com ${}:
// Antes: usando concat (estilo ARM na decompilação)
var storageFullName = concat(storageAccountName, uniqueString(resourceGroup().id))
// Depois: interpolação Bicep (mais legÃvel)
var storageFullName = '${storageAccountName}${uniqueString(resourceGroup().id)}'
3.4 Modificações em Resources: Propriedades e Hierarquia​
Adicionar propriedades a um recurso existente​
// Antes: storage account básica
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: storageAccountName
location: location
sku: {
name: storageSku
}
kind: 'StorageV2'
properties: {}
}
// Depois: com propriedades de segurança adicionadas
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: storageAccountName
location: location
sku: {
name: storageSku
}
kind: 'StorageV2'
properties: {
minimumTlsVersion: 'TLS1_2'
allowSharedKeyAccess: false
supportsHttpsTrafficOnly: true
networkAcls: {
defaultAction: 'Deny'
bypass: 'AzureServices'
}
}
tags: tags // adicionando tags ao recurso
}
Adicionar recurso filho com parent​
O parent é a forma preferida no Bicep para expressar hierarquia. Ele é mais seguro que construir o nome manualmente porque o Bicep verifica que o recurso pai existe:
// Adicionar blob service à storage account existente no template
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
}
containerDeleteRetentionPolicy: {
enabled: true
days: 7
}
}
}
// Adicionar container dentro do blob service
param containerName string = 'dados'
resource blobContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = {
parent: blobService // encadeia com o blob service acima
name: containerName
properties: {
publicAccess: 'None'
}
}
Adicionar resource lock ao recurso existente​
// Adicionar lock ao recurso crÃtico
resource storageLock 'Microsoft.Authorization/locks@2020-05-01' = {
name: '${storageAccount.name}-lock'
scope: storageAccount // scope aponta para o recurso a ser protegido
properties: {
level: 'CanNotDelete'
notes: 'Storage de producao protegido contra exclusao acidental'
}
}
3.5 Modificações Condicionais com if​
Adicionar um recurso que só deve existir em certos cenários:
// Adicionar parâmetro de controle
param enableDiagnostics bool = false
param logAnalyticsWorkspaceId string = ''
// Recurso condicional: só criado se enableDiagnostics for true
resource diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (enableDiagnostics) {
name: 'diag-${storageAccountName}'
scope: storageAccount
properties: {
workspaceId: logAnalyticsWorkspaceId
logs: []
metrics: [
{
category: 'Transaction'
enabled: true
}
]
}
}
3.6 Loops: for para Múltiplas Instâncias​
Substituir múltiplos recursos idênticos por um loop:
// Antes: três subnets declaradas individualmente
resource subnet1 'Microsoft.Network/virtualNetworks/subnets@2023-05-01' = {
parent: vnet
name: 'subnet-frontend'
properties: { addressPrefix: '10.0.1.0/24' }
}
resource subnet2 'Microsoft.Network/virtualNetworks/subnets@2023-05-01' = {
parent: vnet
name: 'subnet-backend'
properties: { addressPrefix: '10.0.2.0/24' }
}
// Depois: usando for loop
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
}
}]
Para fazer output de todos os recursos criados pelo loop:
output subnetIds array = [for (subnet, i) in subnets: subnetResources[i].id]
3.7 Atualizar a apiVersion​
Ao adicionar novas propriedades que requerem uma versão mais recente da API:
// Antes
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-02-01' = {
// Depois (versão mais recente com suporte a novas propriedades)
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
Para descobrir as versões disponÃveis:
az provider show \
--namespace Microsoft.Storage \
--query "resourceTypes[?resourceType=='storageAccounts'].apiVersions" \
--output table
3.8 Adicionar e Usar Módulos​
Extrair parte do arquivo em módulo para reutilização:
// Antes: tudo no main.bicep
resource vnet 'Microsoft.Network/virtualNetworks@2023-05-01' = {
name: 'vnet-producao'
location: location
properties: {
addressSpace: {
addressPrefixes: ['10.0.0.0/16']
}
}
}
// Depois: extraÃdo para módulo
// modules/vnet.bicep (arquivo separado)
// main.bicep usa o módulo:
module vnetModule './modules/vnet.bicep' = {
name: 'vnet-deploy'
params: {
vnetName: 'vnet-producao'
addressPrefix: '10.0.0.0/16'
location: location
}
}
// Usar output do módulo em outro recurso
resource subnet 'Microsoft.Network/virtualNetworks/subnets@2023-05-01' = {
name: '${vnetModule.outputs.vnetName}/subnet-app'
...
}
4. Visão Estrutural​
5. Funcionamento na Prática​
Compilar e Verificar o Arquivo Modificado​
Após cada modificação significativa, compile para verificar erros antes de tentar deployar:
# Compilar Bicep para ARM JSON (verifica erros de sintaxe)
az bicep build --file main.bicep
# Com saÃda para arquivo especÃfico (para inspeção do ARM gerado)
az bicep build --file main.bicep --outfile main-compiled.json
# Verificar a versão do compilador Bicep
az bicep version
# Atualizar o compilador Bicep
az bicep upgrade
Linting com Regras de Qualidade​
O compilador Bicep tem regras de linting embutidas que avisam sobre boas práticas:
# Linting é feito automaticamente no build
# Para ver todas as regras disponÃveis:
az bicep build --file main.bicep --stdout 2>&1 | grep "Warning"
O VS Code com a extensão Bicep mostra os avisos de linting em tempo real com sublinhados amarelos. Avisos comuns:
use-stable-vm-image: a imagem de VM pode não estar disponÃvel em todas as regiõesno-unused-params: parâmetro declarado mas nunca usado no templateno-unused-vars: variável declarada mas nunca referenciadaprefer-interpolation: use'${var}'em vez deconcat(var, '')
Comportamentos Importantes e Não Óbvios​
Nomes simbólicos vs. nomes de recurso: em Bicep, o nome simbólico (como storageAccount ou vnet) é usado para referências dentro do arquivo. O nome do recurso Azure (como minhaconta) é o valor da propriedade name. São coisas diferentes.
// 'storageAccount' é o nome simbólico (usado no Bicep)
// 'minhaconta' é o nome real do recurso Azure
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: 'minhaconta' // nome Azure
...
}
// Referenciar pelo nome simbólico (não pelo nome Azure)
output storageId string = storageAccount.id // correto
// output storageId string = 'minhaconta'.id // erro: isso não funciona
dependsOn no Bicep é raramente necessário: quando você referencia um recurso pelo nome simbólico, o Bicep automaticamente infere a dependência. Use dependsOn apenas quando a dependência existe mas não pode ser expressa via referência direta.
// Dependência implÃcita: blobService depende de storageAccount
// porque usa 'parent: storageAccount'
resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2023-01-01' = {
parent: storageAccount // cria dependência automaticamente
name: 'default'
properties: {}
}
// dependsOn explÃcito (raramente necessário)
resource algoQueDepende 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
name: 'minha-identidade'
location: location
dependsOn: [
storageAccount // necessário apenas se não há referência direta
]
}
6. Formas de Implementação​
6.1 VS Code com Extensão Bicep​
Quando usar: todo o desenvolvimento e modificação de arquivos Bicep.
A extensão Bicep para VS Code oferece:
- IntelliSense: autocomplete de tipos de recurso, propriedades e valores
- Verificação em tempo real: erros de sintaxe e avisos de linting sem precisar compilar
- Hover documentation: ao passar o mouse sobre uma propriedade, mostra a documentação
- Code actions: sugestões de correção automática para problemas detectados
- Visualizer:
Ctrl+Shift+P> "Bicep: Open Bicep Visualizer" mostra um grafo dos recursos e dependências
Para instalar:
# Via extensões do VS Code
code --install-extension ms-azuretools.vscode-bicep
6.2 Azure CLI para Validação e Deploy​
# Após modificar o arquivo Bicep:
# 1. Compilar para verificar erros de sintaxe
az bicep build --file main.bicep
# 2. Validar contra o Azure (verifica se o deploy seria aceito)
az deployment group validate \
--resource-group rg-producao \
--template-file main.bicep \
--parameters @parameters.prod.json
# 3. What-If para ver impacto
az deployment group what-if \
--resource-group rg-producao \
--template-file main.bicep \
--parameters @parameters.prod.json
# 4. Deploy com confirmação interativa
az deployment group create \
--resource-group rg-producao \
--template-file main.bicep \
--parameters @parameters.prod.json \
--confirm-with-what-if
6.3 Bicep Playground (Online)​
Para modificações exploratórias ou aprendizado: https://aka.ms/bicepdemo
O Bicep Playground permite editar Bicep e ver o ARM JSON compilado lado a lado em tempo real, sem necessidade de instalar nada localmente.
7. Controle e Segurança​
Verificar o ARM JSON Gerado​
Após modificar um Bicep, é uma boa prática verificar o ARM JSON compilado para confirmar que a modificação produziu exatamente o que você esperava:
az bicep build --file main.bicep --outfile main-check.json
Compare o JSON antes e depois da modificação. Diferenças no JSON compilado refletem exatamente o que mudará no deploy.
Parâmetros Seguros: Nunca em Texto Claro​
Ao modificar um arquivo Bicep para aceitar senhas ou chaves:
// INCORRETO: parâmetro de senha como string comum
param dbPassword string // valor aparecerá em logs!
// CORRETO: usar @secure()
@secure()
param dbPassword string // valor não aparece em logs
// MELHOR: referência ao Key Vault no arquivo de parâmetros
// (sem precisar passar a senha como string em nenhum lugar)
O arquivo de parâmetros correspondente com referência ao Key Vault:
{
"parameters": {
"dbPassword": {
"reference": {
"keyVault": {
"id": "/subscriptions/<sub>/resourceGroups/rg-secrets/providers/Microsoft.KeyVault/vaults/meu-kv"
},
"secretName": "db-admin-password"
}
}
}
}
8. Tomada de Decisão​
Quando usar parent vs. construir o nome manualmente?​
| Situação | Abordagem | Motivo |
|---|---|---|
| Recurso filho de um recurso criado no mesmo arquivo | parent: | Inferência de dependência automática, mais seguro |
| Recurso filho de um recurso existente fora do template | Nome construÃdo com 'recurso-pai/filho' | O recurso pai não existe no escopo simbólico do template |
| Múltiplos recursos filhos em loop | parent: + loop | Mais conciso e com dependência inferida |
Quando extrair um módulo?​
| Situação | Decisão | Motivo |
|---|---|---|
| Padrão repetido em 3+ arquivos | Extrair para módulo | DRY: não se repita |
| Arquivo Bicep com > 200 linhas | Considerar módulos | Manutenibilidade |
| Componente com requisito de versão especÃfico | Módulo com versão | Controle independente do ciclo de vida |
| Time compartilhando componentes | Módulo em registro Bicep | Distribuição centralizada |
Quando usar for vs. múltiplos recursos?​
| Situação | Abordagem | Motivo |
|---|---|---|
| Recursos com estrutura idêntica, quantidade variável | for loop | Parametrizável, sem repetição |
| Dois recursos com configurações muito diferentes | Recursos separados | Clareza sobre cada um |
| Recursos que precisam de referência individual depois | for + Ãndice [i] nos outputs | Permite referenciar cada item |
| Quantidade fixa conhecida em design time | Recursos separados ou for | Depende da preferência de legibilidade |
9. Boas Práticas​
Organize o arquivo Bicep em ordem lógica: parâmetros primeiro, depois variáveis, depois recursos na ordem de dependência (pai antes do filho), depois outputs. Isso facilita a leitura de cima para baixo.
Use nomes simbólicos descritivos: o nome simbólico storageAccount é muito melhor que sa1 ou resource1. Quem ler o arquivo entende imediatamente o que aquele sÃmbolo representa.
Sempre especifique @description(): parâmetros sem descrição são difÃceis de entender fora de contexto. Mesmo que pareça óbvio para quem criou, adicionar descrição é um investimento em legibilidade futura.
Use o operador ternário para lógica simples, if para recursos condicionais: para variar um valor com base em condição, use condition ? value1 : value2. Para criar ou não um recurso, use resource ... = if (condition) { ... }.
Prefira uniqueString(resourceGroup().id) a sufixos aleatórios: uniqueString() é determinÃstico para o mesmo resource group. O mesmo sufixo gerado a cada deploy permite que o ARM atualize o recurso existente em vez de criar um novo.
10. Erros Comuns​
Modificar o nome simbólico e quebrar todas as referências
O recurso era chamado storageAcct e foi renomeado para storageAccount para ficar mais legÃvel. Mas há outras referências no arquivo como storageAcct.id, parent: storageAcct, outputs que usam storageAcct. Todos esses precisam ser atualizados. O compilador detecta esses erros, mas é importante fazer o rename de forma consistente. O VS Code com a extensão Bicep tem rename symbol (F2) que atualiza todas as referências automaticamente.
Adicionar propriedade incompatÃvel com a apiVersion atual
Uma nova propriedade de segurança é adicionada ao recurso: allowBlobPublicAccess: false. O deploy falha com erro "Unknown property 'allowBlobPublicAccess'". A causa é que a apiVersion atual do recurso é 2019-04-01, que não suporta essa propriedade. A solução é atualizar a apiVersion para uma versão mais recente que suporte a propriedade.
Usar concat() em vez de interpolação
O Bicep tem o linter configurado para avisar sobre isso: prefer-interpolation. Embora concat(storageAccountName, uniqueString(resourceGroup().id)) funcione, '${storageAccountName}${uniqueString(resourceGroup().id)}' é a forma idiomática do Bicep e deve ser preferida.
Referenciar output de módulo antes do módulo ser "criado"
Em Bicep, um módulo só está disponÃvel para referência após sua declaração. Se você tenta usar moduleA.outputs.id antes de declarar module moduleA, o Bicep retorna erro de referência inválida. A ordem das declarações no arquivo Bicep importa para legibilidade, mesmo que o ARM processe em paralelo.
Esquecer de atualizar o arquivo de parâmetros ao adicionar novo parâmetro obrigatório
Um novo parâmetro containerName é adicionado ao arquivo Bicep sem defaultValue. O arquivo parameters.prod.json não é atualizado. O deploy falha com erro de parâmetro obrigatório não fornecido. Sempre verifique se todos os arquivos de parâmetros de todos os ambientes precisam ser atualizados ao adicionar parâmetros sem defaultValue.
11. Operação e Manutenção​
Verificar Consistência do Template com o Estado Real​
# What-If em modo Complete mostra diferenças entre template e estado atual
az deployment group what-if \
--resource-group rg-producao \
--template-file main.bicep \
--parameters @parameters.prod.json \
--mode Complete
Se o what-if mostra recursos a serem criados que deveriam já existir, ou recursos a serem deletados que não estão no template, há drift: o estado real divergiu do template.
Verificar Avisos de Linting no Pipeline​
Configure o pipeline para falhar se houver avisos de linting:
# Em um pipeline, tratar avisos como erros
az bicep build --file main.bicep 2>&1 | \
grep -i "warning" && \
echo "Avisos de linting encontrados" && exit 1 || \
echo "Sem avisos de linting"
Limites do Bicep​
| Item | Limite |
|---|---|
| Tamanho máximo do arquivo Bicep | 4 MB |
| Módulos aninhados (profundidade) | 5 nÃveis |
| Parâmetros por template | 256 |
| Variáveis por template | 256 |
| Outputs por template | 64 |
| Recursos por template | 800 |
12. Integração e Automação​
Bicep Registry: Módulos Compartilhados​
Para organizações com múltiplos times usando Bicep, o Bicep Registry (baseado no Azure Container Registry) permite publicar módulos versionados:
// Referenciar módulo de um registry privado
module vnet 'br:meuacr.azurecr.io/bicep/vnet:v1.2' = {
name: 'vnet-deploy'
params: {
vnetName: 'vnet-producao'
location: location
}
}
Publicar um módulo no registry:
az bicep publish \
--file modules/vnet.bicep \
--target br:meuacr.azurecr.io/bicep/vnet:v1.2
Pipeline de CI com Validação de Bicep​
# .github/workflows/bicep-validate.yml
name: Validate Bicep Changes
on:
pull_request:
paths: ['**/*.bicep', '**/parameters*.json']
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Azure Login
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Install Bicep
run: az bicep install
- name: Build Bicep (Lint + Compile)
run: |
for f in $(find . -name "*.bicep"); do
echo "Building $f"
az bicep build --file "$f"
done
- name: Validate against test environment
run: |
az deployment group validate \
--resource-group rg-bicep-validation \
--template-file main.bicep \
--parameters @parameters.dev.json
- name: What-If Report
run: |
az deployment group what-if \
--resource-group rg-producao \
--template-file main.bicep \
--parameters @parameters.prod.json \
--output table
13. Resumo Final​
Pontos essenciais:
- Modificar um arquivo Bicep é mais próximo de programar do que de editar configuração. O compilador detecta erros antes do deploy, e o VS Code com a extensão Bicep fornece feedback em tempo real.
- Os principais tipos de modificação: adicionar/alterar parâmetros com decorators, adicionar/modificar variáveis com expressões, adicionar/alterar recursos e suas propriedades, adicionar recursos condicionais e em loop, adicionar outputs e módulos.
- O
parent:é a forma preferida para expressar hierarquia de recursos em Bicep, criando dependências implÃcitas automaticamente.
Diferenças crÃticas:
- Nome simbólico (ex:
storageAccount) vs. nome do recurso Azure (ex:'minhaconta'): o simbólico é usado dentro do Bicep para referências; o nome Azure é o que aparece no portal. Mudar um não muda o outro. @secure()no parâmetro vs. parâmetro sem decorator: com@secure(), o valor não aparece em logs do ARM; sem ele, o valor é exposto. Qualquer senha ou chave de API deve ter@secure().parent:implÃcito vs.dependsOn:explÃcito:parent:cria a dependência automaticamente;dependsOn:é necessário apenas quando a dependência existe mas não pode ser expressa via referência direta.if (condition)em resource vs. ternário? :: oifdecide se o recurso é criado ou não; o ternário seleciona entre dois valores para uma propriedade.
O que precisa ser lembrado:
az bicep build --file main.bicepdetecta erros de sintaxe e avisos de linting antes de qualquer deploy.az deployment group validateverifica se o template seria aceito pelo Azure, além de verificar sintaxe.az deployment group what-ifmostra o impacto real nos recursos existentes antes de aplicar.- Ao adicionar novo parâmetro sem
defaultValue, todos os arquivos de parâmetros de todos os ambientes precisam ser atualizados. - O VS Code com a extensão Bicep oferece
F2(rename symbol) para renomear nomes simbólicos e atualizar todas as referências automaticamente. - Use
uniqueString(resourceGroup().id)para sufixos de nomes únicos mas determinÃsticos, não timestamps ou GUIDs aleatórios.