Skip to main content
emnode
Cost

Release orphaned public IP addresses

One capability across the whole estate: find every standard-SKU public IP that is attached to nothing, or to a NIC that itself does nothing, and release it, because Azure bills a static public IP whether or not anything is using it.

12 min·10 sections·AZURE

Last reviewed

Orphaned public IPs: the basics

Why does a public IP attached to nothing still cost money?

A public IP address is one of the few Azure resources that bills for simply existing. A standard-SKU static public IP is charged irrespective of whether it is associated with a running resource, so the moment you delete the virtual machine, load balancer or NAT gateway it used to front, the address keeps ticking on the bill while doing nothing at all. Microsoft documents this plainly: you are charged for a static public IP whether or not it is attached. That is the whole shape of this waste, an address sitting in a resource group with no ipConfiguration, quietly priced.

Orphans appear in two distinct shapes, and a good sweep catches both. The first is the truly unattached address: its ipConfiguration is null and it is not bound to a NAT gateway, so nothing in Azure references it. The second is subtler: an address that is still attached, but to a network interface on a virtual machine that has been stopped and deallocated for weeks, or to a load balancer with no backend. The address is technically in use, yet the thing it fronts does no work, so the spend is just as wasted.

Almost all of this is drift, not intent. A VM was rebuilt and its old IP was left behind. A load balancer was decommissioned but its frontend address was never released. The Basic SKU retired on 30 September 2025, and migrations to standard left a trail of detached addresses. Azure Advisor surfaces the unassociated ones under its cost recommendations, and the Cost Optimization workbook's Networking tab lists them with the 'Unattached Public IPs' query. The work is to release every address that fronts nothing, while keeping the handful you are holding on purpose, such as a reserved address kept for an allow-list that a partner has whitelisted.

In this lesson you will learn why an Azure public IP bills even when it is attached to nothing, how to find every orphaned address across a subscription, the two shapes an orphan takes, and how to release them safely without touching the handful you are holding deliberately. You will use Azure Advisor, the Cost Optimization workbook and the az CLI to build the inventory, then a single safe deletion loop and a Bicep-plus-policy backstop to stop the drift returning.

Fun fact

The address that outlived its server

When the Basic SKU public IP retired on 30 September 2025, a lot of estates discovered just how many addresses they had been carrying with nothing behind them. Basic dynamic IPs only cost money while attached, so they hid in plain sight for years; the move to standard, where a static address bills whether or not it is associated, turned a free orphan into a paid one overnight. The lesson teams took away was simple: in Azure, deleting the server is not the same as deleting its bill. The address has to be released explicitly, or it keeps charging long after the thing it fronted is gone.

Finding orphaned public IPs across a subscription

Priya runs the platform team at a mid-size SaaS company doing its first serious cost pass. Azure Advisor's cost tab is showing a recommendation to delete unassociated public IP addresses, and she wants to know how many there really are before anyone deletes anything.

Rather than work the Advisor list address by address, she starts from the CLI so she can separate the two shapes of orphan, the truly unattached and the attached-but-idle, before anything is released.

List the standard-SKU public IPs that are attached to nothing: no ipConfiguration and no NAT gateway. These are the clean orphans Advisor flags.

$ az network public-ip list --query "[?ipConfiguration==null && natGateway==null && sku.name=='Standard'].{name:name, rg:resourceGroup, ip:ipAddress}" -o table
Name Rg Ip
------------------- --------------- --------------
vm-web01-pip-old rg-web-prod 20.51.12.44
lb-legacy-frontend rg-net-prod 20.51.9.130
partner-allowlist rg-net-prod 20.51.9.131
# Three addresses front nothing. The first two are drift; partner-allowlist may be held on purpose.

An address with a null ipConfiguration and no NAT gateway is billing for nothing. Confirm each is not a reserved address held for an allow-list before releasing it.

How Azure decides a public IP is orphaneddeep dive

An orphaned address resolves to two properties on the public IP resource. The first is 'ipConfiguration': when it is null, the address is bound to no network interface, load balancer frontend or other configuration, so nothing references it. The second is 'natGateway': an address can be attached to a NAT gateway instead of a NIC, which does not show up as an ipConfiguration, so a sweep that checks only ipConfiguration would wrongly flag a working NAT address. The reliable test for a true orphan is that both are empty, which is exactly the check the Cost Optimization workbook's Networking tab applies in its 'Unattached Public IPs' query.

Billing follows the SKU, not the attachment. A standard-SKU static public IP is charged whether or not it is associated with a resource, which is why a detached address keeps costing. The classic exception is the Basic dynamic IP, which was only charged while attached and was released automatically when its VM deallocated, but Basic retired on 30 September 2025, so on a modern estate almost every address is standard and bills regardless. The second shape of waste, an address attached to a stopped-deallocated VM or an empty load balancer, will not show as null ipConfiguration at all, so it needs a separate check against the state of the resource behind it.

Azure Advisor evaluates the unassociated addresses on a periodic cycle and surfaces them as a cost recommendation, so deleting an address clears the recommendation on the next assessment rather than instantly. The strongest position pairs the regular sweep with an Azure Policy that audits for unused public IPs by display name, so a new orphan is flagged the moment it appears rather than waiting for the next manual pass. That is the difference between cleaning up the orphans you have today and stopping the estate accruing new ones tomorrow.

What is the impact of leaving orphaned IPs running?

The direct impact is a recurring charge for nothing. A standard-SKU static public IP bills at a fixed rate per address every hour it exists, attached or not, so each orphan is a small standing cost that never produces a request, serves a connection or fronts a workload. One address is negligible; the impact is that estates accumulate them silently, one per rebuilt VM and decommissioned load balancer, until a sweep finds dozens carrying a monthly total that surprises everyone.

The second-order impact is forecasting drift. Because the charge is fixed and quietly recurring rather than spiky and usage-driven, it does not trip the alerts that catch a runaway workload. It just sits in the baseline, nudging the bill up a little more each quarter, which is precisely the kind of slow creep that makes a budget hard to reason about. Releasing the orphans cleans the baseline so the spend that remains actually maps to something running.

There is also an operational tidiness impact. Addresses count against subscription quotas, so a drawer full of orphans can push you toward a public IP limit and block a legitimate deployment until something is released. Sweeping them keeps the address space honest, the quota free for real work, and the bill an accurate picture of what the estate is actually doing.

How do you release orphaned IPs safely?

Work this as one repeatable sweep rather than a one-off cleanup. The order matters: confirm an address is genuinely idle and not a deliberate reservation before you delete it, because a released static address is gone and a new one will not come back with the same value.

1. Inventory both shapes of orphan

List standard-SKU addresses with a null ipConfiguration and no NAT gateway, the clean orphans. Then separately check for addresses attached to a stopped-deallocated VM or an empty load balancer, the idle-but-attached shape. Use the Cost Optimization workbook's Networking tab and Azure Advisor's cost recommendation as cross-checks against your own query, since one address can be missed by a check that looks at ipConfiguration alone.

2. Separate deliberate reservations from drift

A few addresses are held on purpose: a reserved IP a partner has put on an allow-list, an address kept warm for a planned cutover. For each orphan, confirm whether anything external depends on that specific value. Anything with a documented reason to exist is ring-fenced and tagged; everything else is drift and can be released.

3. Release the drift, recording what you remove

Delete each confirmed orphan with a single command and log the name and address so the saving is auditable and a mistaken deletion is recoverable as a known event. Releasing a static address frees its value back to the pool, so do this only once you are sure no allow-list still references it. The deletion takes effect immediately; the Advisor recommendation clears on the next assessment cycle.

4. Stop the drift returning with policy and IaC

Assign an Azure Policy that audits for unused public IPs so a new orphan is flagged as soon as it appears, and define addresses you do keep in Bicep with explicit tags and ownership, so a reserved address is a deliberate, documented resource rather than an accident waiting to be re-orphaned. This turns a one-off sweep into a self-maintaining baseline.

# Release every standard-SKU public IP that is attached to nothing.
# Both ipConfiguration AND natGateway must be empty for a true orphan.
for id in $(az network public-ip list \
    --query "[?ipConfiguration==null && natGateway==null && sku.name=='Standard'].id" -o tsv); do
  name=$(az network public-ip show --ids "$id" --query name -o tsv)
  echo "Releasing orphan: $name"
  az network public-ip delete --ids "$id"
done

# Back it with policy: audit any unused public IP by EXACT display name.
# Look the definition up at runtime so no GUID is hard-coded.
pid=$(az policy definition list \
  --query "[?displayName=='Unused Public IP addresses driving cost should be avoided'].name" -o tsv)
az policy assignment create \
  --name audit-unused-pip \
  --policy "$pid" \
  --scope "/subscriptions/<subscription-id>"

# Addresses you keep on purpose belong in IaC. Bicep for a reserved, tagged standard IP:
#
# resource partnerAllowlistPip 'Microsoft.Network/publicIPAddresses@2024-05-01' = {
#   name: 'partner-allowlist'
#   location: location
#   sku: {
#     name: 'Standard'
#     tier: 'Regional'
#   }
#   properties: {
#     publicIPAllocationMethod: 'Static'
#     publicIPAddressVersion: 'IPv4'
#   }
#   tags: {
#     purpose: 'partner-allowlist'
#     owner: 'platform-team'
#   }
# }

Quick quiz

Question 1 of 5

Why does a standard-SKU static public IP attached to nothing still appear on the Azure bill?

You can now treat orphaned public IPs as one repeatable sweep rather than a scatter of small charges: inventory both shapes of orphan, the truly unattached and the idle-but-attached, separate the deliberate reservations, release the drift, and back it with an audit policy and infrastructure as code so the estate stops accruing new orphans. A static address bills whether or not anything uses it, so releasing the ones that front nothing is a clean, recurring saving with no trade-off attached.

Back to the library