Typical Azure environments have hundreds or thousands of resources and part of them with public IP addresses. When analyzing public IP addresses in Azure, network and solution architecture needs to take into account. What I mean by this, some resources need public IP to work properly and not all environments have restrictions on using public IP addresses on the deployed resource. Even though, there would be restrictions on how resources can communicate with inside/outside it’s important to keep track of what resources have a public IP address and react when needed.

Recently, I had a case where I needed to find public IP addresses on deployed Azure resources and create detection rules when one was provisioned. This blog post summarizes the experiences.

Resources with Public IP Addresses

In the Microsoft Azure environment are several resource types that can have a public IP address assigned (dynamic/static). To name a few, here are some of the resource types that can have a public IP address in Azure.

  • Virtual machine network interfaces
  • Virtual machine scale sets
  • Public Load Balancers
  • Virtual Network Gateways (VPN/ER)
  • NAT gateways
  • Application Gateways
  • Azure Firewall
  • Bastion Host
  • Route Server

Azure Databricks, Azure Batch, Azure Kubernetes & HD Insight are resource types that can also have a public IP address. These resource types are often seen in the Azure environment and their IPs are maintained automatically by Azure (at least in some of the scenarios).

Where the information is stored?

The ultimate goal in the case I worked with was to establish a snapshot of the current status and create an alert when the public IP address is provisioned. To accomplish the task, it’s important to know where the needed information is stored. There comes the interesting part that is important to emphasize:

  • If a resource has a public IP address assigned but hasn’t been started – the information is found in Azure Resource Graph (ARG)
  • If the resource has been started & it’s monitored – the information is found in Azure Log Analytics (LA)

This sets a scene for detection queries and gives the needed information on where the data should be looked for. In Azure Log Analytics there is one limitation for this case, in LA you are not able to query ARG data. It means that ARG data cannot be used in detection rules. On the other hand, Azure Workbook supports ARG data & LA on queries and data can be merged on the Workbook.

Detection Queries

Azure Workbooks provide great information for evaluation and a kind of ‘snapshot view’ but for alerting you need another mechanism, in other words, Log Analytics or Azure Monitor and Kusto Query Language (KQL).

Side note: How to create alerts based on the data is not covered in this blog post.

Azure Resource Graph

Find all resources that have a public IP address defined excluding some of the resource types (Databricks) which are allowed to communicate with the public IP address.

Resources
| where type == 'microsoft.network/publicipaddresses'
| where isnotempty(properties.ipAddress)
| where resourceGroup !contains 'databricks'
| summarize count() by subscriptionId, resourceGroup
, publicIPAllocationMethod = tostring(properties.publicIPAllocationMethod), name, tostring(properties.ipAddress)
, tostring(properties.provisioningState)
, tostring(properties.pulicIPAddressversion)

The second query is found from Azure Resource Graph templates (Find all Virtual Machines with public IP-address) and it lists, as the name states, all IP addresses from the Virtual Machines.

Resources 
| where type =~ 'microsoft.compute/virtualmachines' 
| extend nics=array_length(properties.networkProfile.networkInterfaces)  
| mvexpand nic=properties.networkProfile.networkInterfaces  
| extend hostName = properties.osProfile.computerName 
| where nics == 1 or nic.properties.primary =~ 'true' or isempty(nic)  
| project vmId = id, 
          vmName = name, 
          nicId = tostring(nic.id), 
          hostName 
| join kind=leftouter ( 
    Resources 
    | where type =~ 'microsoft.network/networkinterfaces' 
    | extend ipConfigsCount=array_length(properties.ipConfigurations)  
    | mvexpand ipconfig=properties.ipConfigurations  
    | extend privateIp = ipconfig.properties.privateIPAddress,
             publicIpId = tostring(ipconfig.properties.publicIPAddress.id)
    | where ipConfigsCount == 1 or ipconfig.properties.primary =~ 'true' 
    | project nicId = id, 
              privateIp, 
              publicIpId
) 
on nicId 
| project-away nicId1 
| summarize by vmId, vmName, tostring(hostName), nicId, publicIpId, tostring(privateIp) 
| join kind=leftouter ( 
    Resources 
    | where type =~ 'microsoft.network/publicipaddresses' 
    | project publicIpId = id, publicIpAddress = properties.ipAddress) 
on publicIpId 
| where publicIpAddress != ""
| project-away publicIpId1

The next query is from the Microsoft TechCommunity blog, and it was super useful. There is one problem with the ARG template ‘List all Public IP Addresses’, it’s querying the resources that have the ‘publicIPAddresses’ parameter which all resources that have public IP don’t have (for example Network GWs, Vnet GWs, etc). That being said, on the blog, there is regex magic that solves the puzzle.

The query below finds all Azure resources that have a public IP address.

  • List resources that don’t have the ‘publicIPAddresses’ parameter defined
  • Excluded private IP address ranges
resources
| where properties matches regex @'[12]\d\d(\.([1-9]?\d|[12]\d\d)){3}'
|project  name, type, location, resourceGroup, subscriptionId, properties

KQL

Azure Activity Log has IP address information from the resources IF the resource has been started. In the Log Analytics, the correct table is ‘AzureActivity’ and the provider is Microsoft.Network provider.

The following query can be used in Azure Monitor Alerts or in Sentinel Analytic rules to create an alert when the activity is detected.

AzureActivity
| where TimeGenerated > ago(30d)
| extend message_ = tostring(Properties_d.message)
| extend scope_ = tostring(parse_json(Authorization).scope)
| extend name_ = tostring(parse_json(tostring(Properties_d.responseBody)).name)
| extend publicIPAddressVersion_ = tostring(parse_json(tostring(parse_json(tostring(Properties_d.responseBody)).properties)).publicIPAddressVersion)
| extend publicIPAddressallocationMethod_ = tostring(parse_json(tostring(parse_json(tostring(Properties_d.responseBody)).properties)).publicIPAddressAllocationMethod)
| extend resourceGuid_ = tostring(parse_json(tostring(parse_json(tostring(Properties_d.responseBody)).properties)).resourceGuid)
| extend entity_ = tostring(parse_json(Properties).entity)
| where Category == 'Administrative'
| where ResourceProviderValue == 'Microsoft.Network'
| where OperationName == 'Create or Update Public Ip Address'
| where ActivitySubstatusValue == "Created"
// Filtering out Azure Databricks
| where ResourceGroup !contains "databricks"
// Filtering out Azure Batch 
| where ResourceId !contains "azurebatch"
| sort by TimeGenerated desc
| project TimeGenerated, name_, OperationNameValue, ActivitySubstatusValue, ResourceGroup, Resource, ResourceProviderValue, message_, scope_, publicIPAddressVersion_, CallerIpAddress, Caller, ResourceId

Azure Workbook

Clive Watson, the KQL & Workbook wizard from Microsoft, has published a great workbook for identifying public IPs with a merged view from Log Analytics & Azure Resource Graph.

Hope this helps!

References

Workbook showing Public IP information

General information on Azure Public IP Addresses

Techcommunity blog on finding public IP addresses from Azure