1. Definir o contrato da mudança
Numa migração fictícia de um serviço de fundos, o projeto precisa de uma rede privada, armazenamento e máquinas preparadas para processamento batch. Antes do código, define o destino, os responsáveis e o resultado observável: que recursos devem existir, que configuração devem ter e como se demonstra que a aplicação funciona? Guarda a versão do template, os parâmetros, as dependências e a identidade de execução. Uma branch chamada release não identifica conteúdo imutável. Uma análise aprovada para uma revisão pode deixar de representar outra revisão ou outro conjunto de parâmetros. O linter deteta problemas estáticos segundo regras configuradas; não demonstra permissões no destino nem capacidade da aplicação. Se a equipa altera o candidato depois da aprovação, deve reconciliar a evidência com aquilo que vai executar. O objetivo é conseguir explicar ao comité de mudança o âmbito exato da decisão e o risco ainda por demonstrar.
2. Separar âmbito, localização e identidade
Imagina que a equipa de plataforma cria o grupo da aplicação a partir de uma implantação de subscrição. Os recursos que pertencem ao grupo são implantados por um módulo com esse âmbito, e a identidade precisa de acesso ao destino. A localização dos metadados da implantação não obriga todos os recursos a residir nessa região. Se o mesmo nome de implantação já estiver registado noutra localização, corrige o nome ou reutiliza a localização anterior em vez de redesenhar a aplicação. Referenciar uma conta com existing também não a cria: confirma nome, subscrição e grupo ao investigar NotFound. Na revisão entre equipas, pede o identificador completo do recurso e confirma quem o mantém. Nomes iguais em destinos diferentes são uma fonte de erro particularmente perigosa quando teste e produção partilham convenções de nomes.
3. Ler previsões com as respetivas lacunas
What-if ajuda a rever diferenças antes de executar. Um Modify devido a uma expressão não resolvida exige investigação; uma lista vazia acompanhada de short-circuiting não confirma estabilidade do módulo. Mantém os diagnósticos junto do resumo, identifica componentes externos não expandidos e fecha a análise que falta. Confirma ainda o nível de validação: numa CLI compatível, ProviderNoRbac não demonstra autorização de escrita e Template tem âmbito estático. A equipa do exemplo revê uma ligação privada antes da janela, mas APS muda o DNS entretanto. Mesmo com o mesmo commit, o destino mudou. É necessário rever o impacto atual. Para deployment stacks, a documentação consultada contém uma divergência sobre disponibilidade de what-if. Confirma suporte no ambiente e cliente usados antes de depender da funcionalidade; trata resultados suportados como previsões sujeitas a limitações, sem os converter numa garantia universal.
4. Declarar estado e dependências reais
Incremental distingue recursos omitidos de recursos redeclarados. Não interpretes o template como um patch que preserva automaticamente todas as propriedades ausentes. Revê valores não predefinidos, particularidades da API e efeitos nas dependências. Numa rede partilhada, uma propriedade aparentemente secundária pode afetar outra aplicação. As condições também precisam de intenção explícita: tornar o pai opcional não torna automaticamente opcionais os filhos declarados separadamente. Protege referências que seriam inválidas quando o recurso não existe. Usa referências simbólicas para expressar dependências reais e evita serializar todos os módulos só para documentar a arquitetura. Se execuções concorrentes partilham nomes de implantação de módulo no mesmo âmbito, podem cruzar outputs. Corrige a identidade dessas implantações; isso não substitui controlo de concorrência quando duas execuções alteram efetivamente o mesmo recurso.
5. Decidir o que fazer com drift
APS pode ter aplicado um hotfix autorizado durante um incidente. Encontrar uma diferença entre código e realidade não determina automaticamente qual dos lados está correto. Liga o desvio ao incidente, confirma validade e prazo do ajuste e decide se atualizas a baseline ou revertes a alteração. Para definições dentro de máquinas suportadas, distingue Audit, Apply and Monitor e Apply and Autocorrect: observar uma divergência não prova que foi corrigida. Introduz autocorreção com pacote testado, âmbito adequado e uma decisão operacional sobre exceções. Em remediação Azure Policy, verifica os papéis da managed identity da atribuição. Os privilégios de quem escreveu o código não são os privilégios dessa identidade. Fecha o trabalho com uma observação posterior do estado, preservando a evidência de configuração e o responsável pela exceção quando ela continuar necessária.
6. Tratar a saída do ciclo de vida
Uma migração não termina quando os recursos desaparecem do template. Para cada recurso antigo, regista retenção, transferência ou eliminação, responsável e custo esperado. Uma stack com detachAll conserva recursos; uma execução bem-sucedida pode deixar uma VM temporária a consumir orçamento. No caso desta aula, um arquivo deve permanecer e a VM pode ser eliminada depois de confirmar dependências. Não uses a eliminação do grupo inteiro como atalho: inclui todos os seus conteúdos na análise. Se o inventário gerido estiver fora de sincronização, reconcilia-o antes de considerar bypass. Revê ainda proteção no âmbito do grupo pai; denyDelete nos recursos de uma stack não basta para assumir que o grupo inteiro está protegido. FINOPS e RUN precisam do estado final observado, não apenas da mensagem de sucesso do comando.
7. Planear recuperação e retirada de serviços
O rollback ARM on-error tem uma semântica específica: reimplanta um histórico anterior em complete, com os parâmetros anteriores, e não desfaz migrações de dados. Num grupo partilhado, um template histórico parcial exige especial atenção. Define recuperação da aplicação, infraestrutura e dados com testes de compatibilidade e critérios observáveis. Um ficheiro em Git não demonstra que o conjunto consegue recuperar dentro do tempo exigido. Considera também o ciclo de vida das ferramentas. A documentação de Azure Deployment Environments anuncia retirada em 22 de fevereiro de 2027. Continua a ser relevante compreender os ambientes existentes e os tipos de ambiente por projeto, mas uma proposta plurianual precisa de avaliar continuidade. Inventaria definições, permissões, subscrições e dependências de automação antes de escolher uma alternativa; não assumas migração automática só porque o conceito de catálogo é semelhante.
8. Exercitar a decisão e preparar RUN
Executa o modelo Python com dados fictícios. A primeira função compara identidade do candidato, snapshot declarado e completude dos módulos esperados. Muda um parâmetro ou marca a rede como não analisada: a evidência deixa de satisfazer a regra do exercício. A segunda função exige estado observado e responsabilidade coerentes com a decisão de reter ou eliminar. Experimenta marcar a VM como ainda presente depois da eliminação declarada. O modelo não consulta Azure, não autentica os inputs e não calcula alterações Bicep; apenas torna verificável uma regra de revisão sobre registos fornecidos. No handover real, entrega inventário, configuração, decisões pendentes, recuperação, responsáveis e custos. Resume o raciocínio em quatro perguntas: qual candidato, qual destino, que análise ficou concluída e que estado foi realmente observado?
"""Original fictional review model. No Azure calls or deployment simulation."""
from copy import deepcopy
def preview_matches(candidate, approval, current_snapshot, expected_modules):
# This checks supplied evidence only. A caller must obtain trustworthy inputs.
identity = ('template', 'parameters', 'scope', 'snapshot')
if any(candidate.get(k) != approval.get(k) or not candidate.get(k) for k in identity):
return False
if candidate['snapshot'] != current_snapshot:
return False
modules = candidate.get('modules', {})
return (bool(expected_modules) and set(modules) == set(expected_modules)
and all(value == 'complete' for value in modules.values()))
def handover_complete(resources):
# Fictional handover policy; 'detached' alone is deliberately insufficient.
if not resources or len({r['id'] for r in resources}) != len(resources):
return False
for r in resources:
if r['decision'] == 'retain':
if r['observed'] != 'present' or not r['owner'] or not r['cost_owner']:
return False
elif r['decision'] == 'delete':
if r['observed'] != 'absent' or not r['dependencies_checked'] or r['retention_required']:
return False
else:
return False
return True
candidate = dict(template='digest-A', parameters='digest-P', scope='subscription-X/group-Y',
snapshot='snapshot-10', modules={'network': 'complete', 'app': 'complete'})
approval = {k: candidate[k] for k in ('template', 'parameters', 'scope', 'snapshot')}
expected = {'network', 'app'}
assert preview_matches(candidate, approval, 'snapshot-10', expected)
for key in ('template', 'parameters', 'scope', 'snapshot'):
changed = deepcopy(candidate)
changed[key] = 'changed'
assert not preview_matches(changed, approval, 'snapshot-10', expected)
assert not preview_matches(candidate, approval, 'snapshot-11', expected)
for state in ('short-circuited', 'unsupported', 'unknown'):
changed = deepcopy(candidate)
changed['modules']['network'] = state
assert not preview_matches(changed, approval, 'snapshot-10', expected)
changed = deepcopy(candidate)
del changed['modules']['network']
assert not preview_matches(changed, approval, 'snapshot-10', expected)
changed = deepcopy(candidate)
changed['modules']['unexpected'] = 'complete'
assert not preview_matches(changed, approval, 'snapshot-10', expected)
assert not preview_matches(candidate, approval, 'snapshot-10', set())
resources = [dict(id='archive', decision='retain', observed='present', owner='APS',
cost_owner='Funds', dependencies_checked=True, retention_required=True),
dict(id='temp-vm', decision='delete', observed='absent', owner='APS',
cost_owner='Migration', dependencies_checked=True, retention_required=False)]
assert handover_complete(resources)
for key, value in (('owner', ''), ('cost_owner', ''), ('observed', 'absent')):
changed = deepcopy(resources)
changed[0][key] = value
assert not handover_complete(changed)
for key, value in (('observed', 'present'), ('dependencies_checked', False), ('retention_required', True)):
changed = deepcopy(resources)
changed[1][key] = value
assert not handover_complete(changed)
changed = deepcopy(resources)
changed[1]['decision'] = 'detached'
assert not handover_complete(changed)
assert not handover_complete([])
assert not handover_complete(resources + [deepcopy(resources[0])])
print('22 fictional IaC evidence checks passed')
Uma mudança de DNS posterior ao preview reabre a revisão de conectividade; no encerramento, uma VM desassociada ainda presente impede declarar o seu descomissionamento concluído.
Armadilhas comuns
Confundir análise ausente com NoChange; aprovar inputs diferentes; tratar incremental como patch; confundir detach com delete; assumir que rollback de recursos recupera dados.
Tópicos relacionados: Governance de mudança e risco · Gestão de configuração e drift · Descomissionamento e FINOPS · Resiliência e recuperação de dados
Liga cada decisão ao candidato e destino reais. Fecha lacunas de análise e confirma o estado final, incluindo recursos retidos, responsabilidades e recuperação.
Referência: Use the Bicep linter · AZ-400 objectives 2026-07-27