Azure DDoS Protection Standard Costs Estimation
Published Jun 13 2022 12:00 AM 4,948 Views
Microsoft

If you are considering the activation of Azure DDoS Protection Standard – a great solution to better protect your Azure Virtual Network (VNet) resources from DDoS attacks – you may ask yourself: Which VNet(s) should you enable the service in? Or how many IPs can be covered by the base pricing? This sometimes isn’t trivial to find out, especially if you have a large or complex Azure infrastructure, made of multiple VNets and public resource types.

 

Azure DDoS Protection Standard protects the following public resources:

 

  • Virtual Machine Network Interface Cards (NIC) (directly attached to a Public IP)
  • Azure Firewalls
  • Bastion Hosts
  • Virtual Network Gateways
  • Application Gateways
  • Public Load Balancers

 

The biggest challenge when estimating DDoS Protection Standard usage is that counting how many Public IPs are within a VNet is not easy math, as there is no direct association between a Public IP and a VNet. You always have at least one resource in between both, that is making the bridge. For example, NICs, Application Gateways, Bastions, Firewalls, etc. all have a reference to the subnet they were deployed in and to the Public IP that is exposing them to the Internet.

 

hspinto_0-1654852954197.png

 

 

Furthermore, Public Load Balancers are not directly associated with a VNet, as they refer directly only to the NIC of the backend pools. This math can get very complicated!

 

hspinto_0-1654853856291.png

 

This article helps you identify all the Public IPs in use and the respective VNet, so that you can prioritize protecting the VNets with the most critical public resources and, on the way, estimate the costs of your solution – Azure DDos Protection Standard base pricing covers 100 Public IPs with each additional IP being billed individually (see pricing details).

 

The Public IPs per VNet math is done with the help of three Azure Resource Graph (ARG) queries. Each query returns the same columns, which you can then export to CSV and join all together. Unfortunately, ARG has limitations that do not allow for a single query joining everything. For your convenience, I published a PowerShell script (Az.Accounts + Az.ResourceGraph PowerShell modules required) that does all this and nicely gives you a single CSV file at the end. You can then import the CSV(s) into Excel and build a pivot table aggregating Public IP counts per VNet. These are the columns returned:

 

  • Id - Public IP resource Id
  • ipAddress – Public IP address (may be empty if dynamic allocation and the respective resource is not allocated)
  • name – Public IP name
  • resourceGroup
  • subscriptionId
  • associatedResourceType – networkinterfaces, azurefirewalls, etc.
  • associatedResourceId
  • associatedResourceName
  • vnetId
  • vnetName

 

Finding Network Interfaces, Virtual Network Gateways, Azure Firewalls, and Bastion Hosts

 

This ARG query is the easiest, because fortunately all these resources have a common Azure Resource Manager properties structure to declare their association to a Public IP and a VNet.

 

 

resources
| where type =~ 'microsoft.network/publicipaddresses'
| extend ipAddress = tostring(properties.ipAddress)
| extend ipConfigurationId = tolower(properties.ipConfiguration.id)
| where isnotempty(ipConfigurationId)
| extend associatedResourceType = tostring(split(ipConfigurationId, '/')[7])
| join kind=inner ( 
    resources
    | where type in ('microsoft.network/azurefirewalls', 'microsoft.network/networkinterfaces','microsoft.network/virtualnetworkgateways','microsoft.network/bastionhosts')
    | mvexpand ipConfiguration = properties.ipConfigurations
    | extend ipConfigurationId = tolower(ipConfiguration.id)
    | extend pipId = tolower(ipConfiguration.properties.publicIPAddress.id)
    | extend subnetId = tolower(ipConfiguration.properties.subnet.id)
    | project ipConfigurationId, associatedResourceId=tolower(id), associatedResourceName=name, pipId, subnetId
) on ipConfigurationId
| extend vnetId = strcat_array(array_slice(split(subnetId,'/'),0,8),'/')
| extend vnetName = tostring(split(vnetId,'/')[8])
| where isnotempty(vnetId)
| distinct id, ipAddress, name, resourceGroup, subscriptionId, associatedResourceType, associatedResourceId, associatedResourceName, vnetId, vnetName

 

 

Finding Application Gateways

 

Application Gateways have a specific properties schema, which requires a separate query.

 

 

resources
| where type =~ 'microsoft.network/publicipaddresses'
| extend ipAddress = tostring(properties.ipAddress)
| extend ipConfigurationId = tolower(properties.ipConfiguration.id)
| where isnotempty(ipConfigurationId)
| extend associatedResourceType = tostring(split(ipConfigurationId, '/')[7])
| join kind=inner ( 
    resources
    | where type == 'microsoft.network/applicationgateways'
    | mvexpand ipConfiguration = properties.frontendIPConfigurations
    | extend ipConfigurationId = tolower(ipConfiguration.id)
    | extend pipId = tolower(ipConfiguration.properties.publicIPAddress.id)
    | mvexpand gwIpConfiguration = properties.gatewayIPConfigurations
    | extend subnetId = tolower(gwIpConfiguration.properties.subnet.id)
    | project ipConfigurationId, associatedResourceId=tolower(id), associatedResourceName=name, pipId, subnetId
) on ipConfigurationId
| extend vnetId = strcat_array(array_slice(split(subnetId,'/'),0,8),'/')
| extend vnetName = tostring(split(vnetId,'/')[8])
| distinct id, ipAddress, name, resourceGroup, subscriptionId, associatedResourceType, associatedResourceId, associatedResourceName, vnetId, vnetName

 

 

Finding Public Load Balancers

 

This is the most complex query, because it must uncover the Load Balancer backend network interfaces to identify their VNet. Due to its complexity, this query can only be run in the Azure Portal and had to be adapted in the PowerShell script I referred to above, to deal with the ARG limitations I already mentioned.

 

 

resources
| where type =~ 'microsoft.network/publicipaddresses'
| extend ipConfigurationId = tolower(properties.ipConfiguration.id)
| where isnotempty(ipConfigurationId)
| extend associatedResourceType = tostring(split(ipConfigurationId, '/')[7])
| join kind=inner ( 
    resources
    | where type =~ 'microsoft.network/loadbalancers'
    | mvexpand backendPool = properties.backendAddressPools
    | mvexpand backendIPConfiguration = backendPool.properties.backendIPConfigurations
    | mvexpand ipConfiguration = properties.frontendIPConfigurations
    | extend ipConfigurationId = tolower(ipConfiguration.id)
    | extend pipId = tolower(ipConfiguration.properties.publicIPAddress.id)
    | where isnotempty(pipId)
    | extend nicIpConfigId = tolower(backendIPConfiguration.id)
    | project ipConfigurationId, associatedResourceId=tolower(id), associatedResourceName=name, pipId, nicIpConfigId
    | where isnotempty(nicIpConfigId)
    | extend nicIpConfigId = iif(nicIpConfigId contains "microsoft.compute/virtualmachinescalesets", strcat_array(array_slice(split(nicIpConfigId,'/'),0,8),'/'), nicIpConfigId)
    | join kind=inner (
        resources
        | where type =~ 'microsoft.network/networkinterfaces'
        | mvexpand ipConfiguration = properties.ipConfigurations
        | extend nicIpConfigId = tolower(ipConfiguration.id)
        | extend subnetId = tolower(ipConfiguration.properties.subnet.id)
        | project nicIpConfigId, subnetId
        | union ( 
            resources
            | where type =~ 'microsoft.compute/virtualmachinescalesets'
            | project nicIpConfigId=tolower(id), subnetId = tolower(properties.virtualMachineProfile.networkProfile.networkInterfaceConfigurations[0].properties.ipConfigurations[0].properties.subnet.id)
        )
    ) on nicIpConfigId
    | project ipConfigurationId, associatedResourceId, associatedResourceName, pipId, subnetId
) on ipConfigurationId
| extend vnetId = strcat_array(array_slice(split(subnetId,'/'),0,8),'/')
| extend vnetName = tostring(split(vnetId,'/')[8])
| distinct id, name, resourceGroup, subscriptionId, associatedResourceType, associatedResourceId, associatedResourceName, vnetId, vnetName

 

 

And that’s it! Now your Azure DDoS Protection Standard Costs Estimation is much easier! Hope this helps ;)

 

Disclaimer

 

The sample scripts are not supported under any Microsoft standard support program or service. The sample scripts are provided AS IS without warranty of any kind. Microsoft further disclaims all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or documentation, even if Microsoft has been advised of the possibility of such damages.

1 Comment
Co-Authors
Version history
Last update:
‎Jun 10 2022 02:39 AM
Updated by: