Part 1 of Why the CAF works: how the Cloud Adoption Framework (CAF) solves governance and scale problems. This one: DDoS protection.
To mitigate the threat from DDoS attacks we can use Azure DDoS Protection. In October 2025 Azure mitigated a 15.72 Tbps DDoS attack from more than 500,000 source IPs, the largest ever seen in the cloud (CPO Magazine). Cloudflare’s 2025 Q4 report shows network-layer DDoS attacks more than tripled in 2025, with attacks reaching 31.4 Tbps.
So the question becomes: how can we use Azure DDoS Protection to automatically protect our Azure estate? The solution needs to work reliably, at scale, under full automation.
A few caveats
When we talk about protecting our Azure estate with DDoS protection, we really mean protecting public IP addresses in Online landing zones in a hub-and-spoke topology. That’s where our network is exposed to the internet. These PIPs might be attached to VMs, Application Gateways, API Management, Bastion, Azure Firewall and so on.
DDoS Protection works at the network layer (L3 and L4). For application-layer protection we need Azure WAF or a similar solution.
PaaS services with public endpoints (for example a storage account) are out of scope. They sit behind Microsoft’s own edge protection, not your VNet.
Azure DDoS Protection is a resource like any other. It lives in the Microsoft.Network/ddosProtectionPlans resource provider.
Which tier do we need?
Every public IP in Azure gets infrastructure protection for free. It’s tuned to protect Azure itself, not your specific workload. Above that there are two paid tiers:
DDoS IP Protection : per-IP pricing, suited to small deployments with a handful of public IPs. You get core DDoS mitigation, but no DDoS Rapid Response, no cost protection and no WAF discount.
DDoS Network Protection : a plan that covers every public IP in any VNet enrolled in it. You also get DRR (access to the DDoS response team during an active attack), cost protection and a discount on Azure WAF.
The key design decision is scale. Once an estate has more than a handful of public IPs, IP Protection’s per-IP pricing quickly approaches the cost of a plan, and the plan covers 100 PIPs. Protect the VNet, not individual IPs. For an enterprise-scale estate, Network Protection is the correct default.
The problem this solves
In the CAF architecture, Online landing zones may be added frequently. Each one gets a spoke VNet, and each spoke VNet might get a PIP. If DDoS enrollment is a manual step, it might be missed or done incorrectly. Either way you end up with unprotected public endpoints. So we treat DDoS protection, along with other aspects of core networking, as a Platform concern. Also DDoS IP protection is less cost effective per resource than a DDoS plan (assuming you deploy more than about 15 PIPs).
This is exactly the class of problem CAF solves: a platform team that owns shared network services, and policy-driven governance that enforces the desired state instead of relying on a documented manual process.
Who owns the spoke VNet?
This is the question the whole design depends on, and it’s easy to skip past.
In the CAF reference model, the spoke VNet isn’t built by the workload team. It arrives with the landing zone through subscription vending. The platform team’s vending pipeline creates the subscription, places it in the Online Management Group, creates the spoke VNet and peers it to the hub. The workload team gets a subscription with its network already connected, and builds inside it.
That vending principal already has rights in the Connectivity subscription, because it has to create the hub side of the peering. That’s also important to note.
The CAF pattern
- One plan. Create a single Microsoft.Network/ddosProtectionPlans resource in the Connectivity subscription alongside the hub. One plan can be shared across subscriptions in the same tenant.
- Vend VNets already protected. Subscription vending creates each spoke VNet with enableDdosProtection: true and ddosProtectionPlan.id set to the central plan. New landing zones are protected from the moment the VNet exists.
- Policy as the guardrail. Assign the built-in Virtual networks should be protected by Azure DDoS Protection (94de2ad3-e0c1-4caf-ad78-5d47bbc83d3d) at the Online landing zones Management Group. Don't assign it at the intermediate root, or it will also catch Platform and Corp VNets (or do, it depends on a few factors). It uses the Modify effect: any VNet created or updated without the plan gets it added in flight, and a remediation task fixes existing VNets.
Vending is the primary mechanism. The policy catches drift and anything created outside vending.
Modify is the right effect here. We’re only setting two properties on the resource being evaluated, there’s no nested deployment, and the VNet is protected at creation rather than minutes afterwards.
Why it works: the join check
Associating a VNet with a DDoS plan is an operation with two scopes. On every VNet create or update that references the DDoS protection plan, ARM checks that the caller has Microsoft.Network/ddosProtectionPlans/join/action on the plan.
Modify alters the caller’s request, so that check runs against whoever made the request. In the CAF model, that’s the platform vending principal, which already has rights in Connectivity. The check passes and everything works.
Remediation tasks for existing VNets run as the policy assignment’s managed identity instead, so that identity needs join rights on the plan too. The portal only grants the roles in roleDefinitionIds at the assignment scope (the Online MG). It doesn't grant anything on a plan in another subscription. Add that grant yourself: a custom role with just ddosProtectionPlans/join/action, or Network Contributor on the plan's resource group.
When the model breaks
The pattern is simple as long as the platform owns the VNet. It breaks when workload teams deploy VNets themselves, for example because:
- application teams own their networking end to end,
- a workload team takes over the vended VNet in its own IaC and redeploys the whole resource, or
- landing zones are built by hand or per team instead of vended.
Now the VNet PUT runs as the workload team’s service principal. Modify adds the plan reference, ARM runs the join check against a principal with no rights in Connectivity (and it shouldn’t have any), and the deployment fails:
LinkedAuthorizationFailed: The client '<sp-object-id>' has permission to perform action
'Microsoft.Network/virtualNetworks/write' on scope 'vnet-spoke-01'; however, it does not have
permission to perform action 'Microsoft.Network/ddosProtectionPlans/join/action' on the
linked scope(s) 'ddos-central'
The policy that was meant to protect the landing zone now blocks the landing zone team from deploying its own network.
Three ways out
Put the ownership boundary back. The platform owns the VNet resource, and workload teams manage subnets, NSGs and route tables inside it. Subnets are child resources with their own PUT, so adding one doesn’t redeploy the VNet. This is the CAF answer, and it keeps the simple Modify pattern.
Grant join rights to workload deployers. Give each landing zone’s deploying principal a custom role with only ddosProtectionPlans/join/action on the plan (most network built-in roles will be too broad). The downside here is it adds permission on a platform resource across every landing zone, and it has to be granted again for each new one. Vending could automate this.
Switch to DeployIfNotExists. DINE doesn’t touch the caller’s request. The VNet is created as normal, the policy evaluates it afterwards, and a deployment runs as the policy assignment’s managed identity to enroll it. Only one identity ever holds join rights. The trade-offs:
- The gap: a new VNet is briefly unprotected (typically minutes).
- Complexity: a VNet PUT replaces the whole resource, so the template must carry across the existing address space, subnets, peerings and tags.
- Flapping: if the workload’s IaC redeploys the VNet without the DDoS properties, the enrollment is removed and DINE adds it back afterwards. Each redeploy opens a short unprotected window.
- A policy Denying resources without DDoS protection won't be happy
What the cloud operating model tells you
This is the real lesson, and it’s why the series is called Why the CAF works. Protecting the estate from DDoS is simple (one plan, one built-in policy, one assignment) because the operating model puts VNet ownership with the platform team. The policy effect, the permissions and the failure modes all follow from that one decision.
Move VNet ownership to workload teams and the same control needs extra permissions, a different policy effect, or both. That’s not a flaw in the built-in policy. It’s the cost of changing the operating model. Decide who owns the network first, then pick the mechanism.
Cost notes
A Network Protection plan is a flat monthly charge covering up to 100 public IPs, with a per-IP charge above that. A single plan can be shared across subscriptions in the same tenant. For any estate with more than a few Online landing zones, one shared plan plus policy enrollment is both cheaper and safer than per-IP protection or a manual process. Check current pricing for your region: Azure DDoS Protection pricing.
Summary
DDoS attacks at multi-terabit scale are now commonplace. Azure DDoS Network Protection mitigates L3/L4 attacks against public IPs in enrolled VNets. In the CAF model, the platform vends each spoke VNet already attached to a central plan in Connectivity, and the built-in Modify policy at the Online Management Group catches anything that drifts.
It works because the platform owns the VNet. When workload teams deploy VNets themselves, Modify runs as their principal and fails the join check on the plan. You can restore the ownership boundary, grant workload deployers join rights, or switch to a DINE policy running as a single managed identity. Pick based on who owns the network.
Top comments (0)