On August 1, 2026, Microsoft officially archived the Cloud Adoption Framework (CAF) enterprise-scale Terraform module. It has now moved into extended support, meaning no further updates, bug fixes, or policy library changes.
The mandated standard is now Azure Verified Modules (AVM). While transitioning a customized enterprise environment to AVM might look like a risky rebuild, it doesn't have to be. We recently transitioned a major enterprise client to AVM in a live production environment without downtime or disruption to the underlying Azure resources.
Here is a look at how we executed that migration and the framework we used to protect the client's production workloads.
What’s in this article:
- Our phased approach to CAF Terraform module to AVM migration
- How core infrastructure was transferred using direct state imports
- How complex edge cases were handled during the migration
- How the platform was modernized post-migration to AVM
Custom Overrides Complicate Standard Migrations
While the client's foundation relied on Microsoft CAF modules, their platform had evolved to include highly specific configurations that upstream modules simply did not support.
Before the migration, the environment consisted of three major components, and each presented a distinct migration challenge:
| Component | Architecture Role | Migration Challenge |
| Core (ALZ Module) | Foundational landing zone (management groups, connectivity, identity). | Straightforward ownership handover. |
| Custom Extensions | Native HCL for platform-specific resources outside standard CAF scope. | Required module-by-module scrutiny and custom mapping. |
| Policy Overlay | Custom Azure Policies enforcing tag inheritance, regional restrictions, and exclusions. | High risk of silently dropping parameter overrides. |
Typically, every CAF upgrade means a tedious reconciliation exercise between these custom extensions and upstream changes. Moving to AVM permanently eliminated this technical debt for the client, but the migration process had to respect their existing production constraints.
Those constraints shaped our migration strategy. Rather than treating every resource the same, we grouped them according to how they needed to be migrated.
Grouping Resources by Migration Method
Three technical realities dictated our strategy:
- Directly importable resources: Foundational components (Connectivity, Management, Core, and Resource Groups) map cleanly to AVM equivalents. These only require an ownership transfer via Terraform state import, with no architectural decisions needed.
- Resources requiring a deliberate strategy: Custom modules built on top of CAF lack a single "correct" migration path. Each must be analyzed individually to determine if it needs an in-place swap, a big-bang replacement, or re-architecting.
- Resources requiring refactoring: Governance and policy configurations must be refactored entirely to accommodate AVM’s stricter library models. With the migration strategy defined, we established a set of guardrails before making any infrastructure changes.
The Pre-Migration Checklist
Before touching the codebase, we established strict guardrails:
- Built a complete CAF inventory, including easily overlooked resources such as diagnostic settings, role assignments, route tables, locks, and private endpoints.
- Minimized configuration drift by freezing manual changes and parallel deployments during the migration.
- Validated Terraform, AzureRM provider, and AVM module compatibility upfront.
- Mapped resource dependencies to establish a safe migration sequence.
Our Phased Migration Strategy
We executed the migration in three phases, with each phase reducing risk before introducing the next set of changes.
-1785821377721.png)
Phase 1: Transfer Core Infrastructure via State Import
We focused first on the foundational components every CAF deployment shares. We migrated these resources via direct Terraform state import, executing an ownership handover from the legacy CAF module to the new AVM module path without re-architecting anything.
- Target AVM Code: Using the ALZ Accelerator tool, we generated the target Terraform code, matching the chosen reference architecture and mirroring variables exactly from the legacy deployment.
- Import State in Parallel: Using the Azure Terraform State Importer, we split execution into two parallel tracks to keep the blast radius small:
- Track A: Management and Connectivity.
- Track B: Management Groups and Policy (governance hierarchy).
- Reconcile Configuration Drift: We generated imports.tf files and ran a terraform plan. Using text-only output to catch attribute-level differences, we adjusted our .tfvars files iteratively until we reached a clean state: “0 to add, 0 to change, 0 to destroy.”
- Apply Zero-Impact: Once reconciled, we applied the changes on a dedicated migration branch, verified the expected state imports, and cleaned up the temporary files.
Phase 2: Map Custom Code to AVM Equivalents
Phase 2 addressed the custom HCL built over time to cover gaps in the original CAF structure (for example, Bastion and Private DNS Zones). Unlike the foundational components, these modules could not be migrated using a single repeatable process. Each required an individual assessment to determine whether it could be imported in place, replaced, or redesigned.
During this phase, we navigated several complex edge cases:
- Implicit Output Dependencies: Several custom modules referenced outputs from Phase 1, such as subnet IDs and policy scopes. Even minor differences in naming or casing after the state import caused Phase 2 modules to fail unexpectedly.
To prevent this, we verified all Phase 1 outputs before using them in Phase 2, rather than treating the phases as fully independent. - AI-Assisted Reconciliation: Comparing custom HCL against AVM expectations and drafting first-pass .tfvars mappings was repetitive.
We used Claude and Copilot to spot complex swaps and draft the initial .tfvars mapping, cutting down manual effort but without replacing human review. - State Scope vs. Architectural Shape: Sharing the same Terraform state does not guarantee the architecture remains unchanged. A VNet link, for example, might point to a VNet rather than the intended hub VNet.
To verify the migration, we documented the Azure environment before the migration and compared it with the post-migration state instead of relying solely on the Terraform plan. - Control-Plane Teardown Lag: We initially replaced Azure Bastion, expecting the stateless hosts to swap cleanly. Instead, Azure held the dedicated subnet and static IP for several minutes after deletion, causing the AVM deployment to fail mid-run and temporarily disrupting jumpbox access.
We addressed this by moving to a two-stage CI/CD process with separate destroy and apply stages, allowing time for Azure to release the resources. We also configured AVM to reuse the existing subnet and Public IP. - Restructuring the Policy Library: The client relied heavily on custom policy overrides, but AVM enforces a stricter schema than the legacy JSON policy definitions.
We restructured the custom policy library into a local lib directory matching the ALZ library’s expected asset-type folders. Expressing customizations as override files allowed them to layer cleanly over upstream defaults while satisfying AVM validation.
Phase 3: Deploy Immediate Architectural Upgrades
With the migration complete and the AVM foundation in place, we turned to improvements that were difficult to implement under the legacy CAF architecture.
- Firewall DNAT with Dedicated IPs: We leveraged AVM's multi-IP support to assign a dedicated Public IP for DNAT, separating inbound and outbound traffic, thereby resolving legacy asymmetric routing issues.
- Direct-to-Event Hub Log Streaming: We used AVM’s native multi-destination support to stream security logs directly to Event Hub, removing the dependency on Log Analytics Data Export Rules and preventing silent log drops.
Outcomes at a Glance
The migration delivered measurable improvements to the client's platform and day-to-day operations:
- Uninterrupted Business Operations: Full production uptime was maintained throughout the migration, with no service disruption or loss of operational hours.
- Drastic Reduction in Engineering Maintenance: Migration to AVM considerably reduced recurring manual reconciliation effort.
- Modernized Platform Architecture: Long-standing infrastructure and observability limitations were resolved, reducing operational risk and providing a scalable base for future growth.
- Simplified Future Governance: Policy updates and reconciliation only require a configuration change rather than a custom-code exercise.
- Faster Adoption of New Capabilities: New Azure features now ship straight into the platform, without waiting on a custom module to catch up.


