Cloud On-Ramp Part 2: Azure Virtual WAN and a Dual-Cloud Resilience Design
Part 1 made the architecture-decision case — a transit-hub model beats a peering mesh in the cloud for the same structural reasons Hub Placement Part 1 made it on-prem — and then worked through AWS Transit Gateway: BGP-over-IPsec mechanics, ASN selection, and mapping on-prem VRFs onto TGW route tables.
Azure’s Virtual WAN (vWAN) answers the same architectural question with a recognisably similar shape — and a genuinely different implementation underneath it. This post covers the vWAN model on its own terms, then does the thing Part 1 set up but couldn’t finish: a worked resilience design for an estate on-ramped into both clouds (or one cloud and a residual DC), built directly on the active/active-vs-active/standby reasoning from the SD-WAN Resilience series.
The vocabulary, mapped against what Part 1 already established
The fastest way into vWAN, coming from the TGW model just covered, is a direct term-for-term comparison — not because the two are interchangeable (they aren’t, and the differences matter), but because knowing which concept maps to which tells you exactly where to focus when something doesn’t behave the way the AWS side trained you to expect.
| Concept | AWS Transit Gateway | Azure Virtual WAN |
|---|---|---|
| The managed hub construct | Transit Gateway | Virtual hub (one or more, inside a vWAN resource) |
| The thing the FortiGate’s tunnels terminate into | VPN attachment | VPN gateway (a resource inside the virtual hub) |
| The on-prem side’s identity to the cloud | Customer Gateway | VPN site |
| Routing-domain separation construct | TGW route table | Route table, grouped and selected via labels |
| Which table an attachment routes via | Association | Association (same word — different mechanics, see below) |
| Which table an attachment’s routes land in | Propagation | Propagation (ditto) |
| Default Amazon/Microsoft-side ASN | 64512 | 65515 |
Two rows deserve more than a passing glance, because they’re where “looks the same, behaves differently” turns into a debugging session if you don’t know to expect it.
How vWAN actually wires up the tunnels — and why there can be four, not two
Recall from Part 1 that a TGW VPN attachment provisions exactly two IPsec tunnels, each with its own BGP session, specifically for redundancy. Azure’s VPN gateway takes the same redundancy goal and implements it with an extra layer: a vWAN VPN gateway is deployed as two gateway instances (Instance0 and Instance1) for high availability, and — if you provision the VPN site connection in active-active mode, which is the configuration this design wants — each instance presents its own public IP and its own BGP peering address. That’s two instances times two tunnels each, which means a single VPN site connection to a vWAN hub can mean the FortiGate is managing four IPsec tunnels and four BGP sessions, not two.
This isn’t Azure being needlessly complex — it’s a more granular redundancy model than TGW’s, and it buys something real: a maintenance event or failure affecting one gateway instance (not just one tunnel) leaves two of the four sessions completely untouched, on a code path that never shared infrastructure with the failed instance. The cost is exactly proportional to the benefit — twice the configuration surface, twice the BGP sessions to monitor, and (this is the detail worth writing into the design doc in bold) twice the IPsec licensing/throughput overhead to size for on the FortiGate side, compared with the AWS leg of the same dual-cloud estate.
config vpn ipsec phase1-interface
edit "azure-vhub-i0-tun1"
set interface "wan2"
set peertype any
set net-device disable
set proposal aes256-sha256
set dpd on-idle
set remote-gw <Instance0 public IP, tunnel 1>
set psksecret <PSK from Azure VPN site config>
next
edit "azure-vhub-i0-tun2"
... <Instance0 public IP, tunnel 2>
next
edit "azure-vhub-i1-tun1"
... <Instance1 public IP, tunnel 1>
next
edit "azure-vhub-i1-tun2"
... <Instance1 public IP, tunnel 2>
next
end
config router bgp
set as 65011
config neighbor
edit "<Instance0 BGP peer IP, tunnel 1>"
set remote-as 65515
set update-source "azure-vhub-i0-tun1"
next
edit "<Instance0 BGP peer IP, tunnel 2>"
set remote-as 65515
set update-source "azure-vhub-i0-tun2"
next
edit "<Instance1 BGP peer IP, tunnel 1>"
set remote-as 65515
set update-source "azure-vhub-i1-tun1"
next
edit "<Instance1 BGP peer IP, tunnel 2>"
set remote-as 65515
set update-source "azure-vhub-i1-tun2"
next
end
end
Notice the FortiGate’s own ASN here — 65011 — is deliberately different from the 65010 used against AWS in Part 1. That’s not arbitrary: a FortiGate peering into two cloud transit fabrics simultaneously is, from a BGP topology perspective, a multi-homed AS boundary, and giving each cloud-facing leg of the design a distinct ASN keeps the path-selection and loop-prevention story legible — exactly the same instinct the ASN selection section of Part 1 argued for, applied one level up: audit the whole reachable topology’s ASN space, including the part of it that’s about to span two different cloud providers’ default ranges (64512 and 65515 are conveniently far apart, but the FortiGate’s own choices still have to fit cleanly around both).
Route propagation: where “same word, different mechanics” gets real
Part 1 leaned hard on one TGW detail: association and propagation are independent settings on the same attachment, and that independence is the mechanism VRF mapping depends on. Azure vWAN has the same two concepts — and implements the relationship between them through an extra indirection layer that AWS doesn’t have: labels.
In vWAN, route tables aren’t associated with and propagated-to directly by name. Instead:
- Each route table can carry one or more labels (arbitrary tags —
corp,dmz, whatever the design calls for). - Each connection (the vWAN term for an attachment) specifies which route table it’s associated with and which labels it should propagate its routes to.
- A connection’s routes land in every route table carrying a label it propagates to — which can be more than one table at once.
That last point is the one with no clean TGW analogue, and it’s simultaneously the feature that makes complex multi-domain designs more expressive and the thing most likely to produce a route appearing somewhere nobody expected it. A connection that propagates to a label carried by two route tables has — intentionally or not — fanned its routes out to both. There’s nothing wrong with that when it’s the design; there’s everything wrong with it when it’s a leftover label assignment from an earlier iteration of the design that nobody remembered to remove.
The practical consequence for the VRF-mapping exercise: build the label scheme first, on paper, before creating a single route table or connection, and treat it with the same rigor Route Leaking Between VRFs argued for on-prem route-maps — write down, for every label, the complete list of route tables that carry it and the complete list of connections that propagate to it, and review that list as a single artifact whenever either side changes. The indirection that makes labels powerful is the same indirection that makes “what actually has a route to what” stop being something you can read off a single screen.
On-prem (FortiGate) Azure (Virtual WAN)
------------------- -------------------
VRF 0 (corporate) <---> label "corp"
- route table "corp-rt" carries it
- corp VPN connection: associated
with corp-rt, propagates to "corp"
- corp VNet connections: same
VRF 2 (DMZ/partner)<---> label "dmz"
- route table "dmz-rt" carries it
- DMZ VPN connection: associated
with dmz-rt, propagates to "dmz"
- NOTHING propagates "corp" routes
to a label "dmz-rt" carries —
verified, not assumed
Functionally this produces the same isolation outcome as the TGW design from Part 1 — separate routing domains, no shared table, no leak path. Structurally, it’s reached via a different mechanism, and that mechanism (labels as a many-to-many fan-out layer) is exactly the kind of “looks similar, isn’t” detail that turns into a half-day investigation if a migration or a multi-cloud design assumes the two clouds’ route-propagation models are interchangeable. They produce the same result when configured correctly. They fail in different ways when configured carelessly — TGW fails toward “this attachment simply has no route” (loud, easy to spot); vWAN can fail toward “this connection has a route it shouldn’t, via a label nobody remembered assigning” (quiet, and exactly the shape of problem the on-prem VRF material has spent two posts warning about).
A worked resilience design: dual-cloud, built on what the Resilience series already established
Here’s where the two halves of this series come together. An estate on-ramped into both AWS and Azure — or, just as commonly in practice, into one cloud plus a residual DC that hasn’t fully migrated — is structurally identical to the dual-hub, dual-DC shape SD-WAN Resilience Part 1 spent an entire post justifying, and Hub Placement Part 1 drew as two boxes side by side. The only thing that’s changed is what’s inside each box.
+----------------------+ +----------------------+
| AWS (Part 1) | | Azure (this post) |
| TGW + VPN attach | | vHub + VPN gateway |
| 2 tunnels, 2 BGP | | 4 tunnels, 4 BGP |
| sessions | | sessions |
+----------^-----------+ +-----------^----------+
| IPsec/BGP | IPsec/BGP
| AS 65010 AS 65011 |
+----------------+----------------+
|
+------+-------+
| FortiGate |
| (cloud-facing|
| hub or spoke|
| role) |
+---------------+
Apply the Resilience series’ active/active-vs-active/standby framework to that picture, and the design questions resolve cleanly because they’re not new questions — they’re the same questions, with cloud-specific answers slotted into a familiar decision structure:
Should the two cloud legs run active/active or active/standby? Resilience Part 1 argued at length for active/standby in topologies without a DC interconnect, on the grounds that active/active without one risks asymmetric routing and state-table chaos on stateful inspection devices. A direct AWS-to-Azure interconnect (cloud-to-cloud peering, or a transit device that bridges both) is the cloud-on-ramp equivalent of that DCI — and exactly as in the on-prem case, whether one exists is the single fact that should decide this, not a preference for one mode over the other in the abstract. No interconnect: active/standby, full stop, for precisely the reasons that post laid out. An interconnect that’s been deliberately built and tested for this purpose: active/active becomes viable, and worth the complexity it buys back in normal-case path diversity.
How does BFD and convergence tuning translate to cloud-side tunnels? Resilience Part 4 built its timer recommendations around physical-circuit failure characteristics — the kind of link-down event a transceiver notices in milliseconds. A cloud provider’s VPN endpoint failing over (an Azure gateway instance restarting during a platform maintenance window, say) is a different failure shape: the tunnel may stay technically “up” at the IPsec layer for longer than a snapped fibre ever would, while BGP convergence on the other side takes its own, provider-controlled time. Tuning BFD aggressively enough to catch a physical-circuit failure fast can mean it fires during a normal, self-healing cloud-side maintenance event — converting a non-event into a flap. The right answer isn’t “copy the Part 4 timers” or “don’t run BFD on cloud-facing tunnels at all” — it’s running BFD with timers calibrated to this failure population (provider SLA documentation on expected gateway-instance failover duration is the actual input here, not a guess), and verifying empirically, in a maintenance window, what a real instance failover looks like from the FortiGate’s perspective before trusting the tuning in production.
What actually breaks first, in practice? Almost never the thing the design spent the most time on. In a dual-cloud on-ramp, the empirically common first failure is one leg’s BGP session flapping during a provider-side maintenance event that the design correctly anticipated and is correctly configured to absorb — and the actual incident, when there is one, traces back to something the design didn’t think to name: an ASN that collided with something three acquisitions ago, a label that fanned routes somewhere nobody remembered assigning it, or a BFD timer tuned against the wrong failure population. None of those are exotic failures. All three are exactly the kind of thing this two-part series has spent its length trying to make visible before they’re discovered as an incident — which is, in the end, the same goal Hub Placement Part 3 closed on on-prem: not “build it and walk away,” but “build it so the next person who has to reason about it — quite possibly you, eighteen months from now — can see exactly why it’s shaped the way it is.”
Verifying the dual-cloud design
The checks are the same three from Part 1, run against both clouds’ sessions and compared side by side — which is, itself, the most useful diagnostic step of all, because asymmetry between the two legs is usually the first visible sign that something’s drifted:
diagnose vpn ike gateway list
get router info bgp neighbors
get router info routing-table bgp vrf <N>
Run each across every cloud-facing tunnel and BGP session, for every VRF that has a cloud-side counterpart, and look specifically for asymmetry: one leg showing four established BGP sessions and the other showing two when both should show four (a gateway-instance failure that hasn’t been actioned), prefix counts that don’t match between AWS and Azure for what should be the same advertised set (a propagation or label mismatch), or a VRF that sees routes via one cloud but not the other when the design says it should see both (an association pointing at the wrong route table on one side). None of those are exotic to find once you’re looking for asymmetry specifically — they’re exactly the kind of thing that hides in plain sight when each cloud’s health is checked in isolation and never compared against its twin.
Series recap
| Part | Topic |
|---|---|
| 1 | The architecture decision, AWS Transit Gateway, BGP-over-IPsec mechanics, ASN selection, mapping VRFs onto TGW route tables |
| 2 (this post) | Azure Virtual WAN, the vHub/label model compared with TGW, and a worked dual-cloud resilience design grounded in the Resilience series |
The thread that ties both parts together is the same one Hub Placement pulled on first: the hub’s job — terminate tunnels, anchor routing, enforce segmentation, stay reachable to its management plane — doesn’t change when the destination is a hyperscaler’s transit fabric instead of a DC core. What changes is the vocabulary, the mechanics, and — this is the part worth carrying forward into any future cloud work on this blog — the fact that two providers solving the identical architectural problem can, and routinely do, solve it with implementations different enough that “I know how this works in AWS” is a starting point for understanding Azure, not a substitute for actually checking.