Upgrade public IP addresses attached to VM from Basic to Standard
On September 30, 2025, Basic SKU public IPs will be retired. For more information, see the official announcement. If you are currently using Basic SKU public IPs, make sure to upgrade to Standard SKU public IPs prior to the retirement date. This article will help guide you through the upgrade process.
For more information about the retirement of Basic SKU Public IPs and the benefits of Standard SKU Public IPs, see here
This script upgrades any Public IP Addresses attached to VM from Basic to Standard SKU. In order to perform the upgrade, the Public IP Address allocation method is set to static before being disassociated from the VM. Once disassociated, the Public IP SKU is upgraded to Standard, then the IP is re-associated with the VM.
Because the Public IP allocation is set to 'Static' before detaching from the VM, the IP address won't change during the upgrade process, even in the event of a script failure. The module double-checks that the Public IP allocation method is 'Static' prior to detaching the Public IP from the VM.
The module logs all upgrade activity to a file named
PublicIPUpgrade.log, created in the same location where the module was executed (by default).
Constraints/ Unsupported Scenarios
VMs with NICs associated to a Load Balancer: Because the Load Balancer and Public IP SKUs associated with a VM must match, it isn't possible to upgrade the instance-level Public IP addresses associated with a VM when the VM's NICs are also associated with a Load Balancer, either through Backend Pool or NAT Pool membership. Use the scripts Upgrade a Basic Load Balancer to Standard SKU to upgrade both the Load Balancer and Public IPs as the same time.
VMs without a Network Security Group: VMs with IPs to be upgraded must have a Network Security Group (NSG) associated with either the subnet of each IP configuration with a Public IP, or with the NIC directly. This is because Standard SKU Public IPs are "secure by default", meaning that any traffic to the Public IP must be explicitly allowed at an NSG to reach the VM. Basic SKU Public IPs allow any traffic by default. Upgrading Public IP SKUs without an NSG would result in inbound internet traffic to the Public IP previously allowed with the Basic SKU being blocked post-migration. See: Public IP SKUs
Virtual Machine Scale Sets with Public IP configurations: If you have a virtual machine scale set (uniform model) with public IP configurations per instance, note these configurations aren't Public IP resources and as such cannot be upgraded. Instead, you can remove the Basic IP configuration and use the SKU property to specify that Standard IP configurations are required for each virtual machine scale set instance as shown here.
- Install the latest version of PowerShell
- Ensure whether you have the latest Az PowerShell module installed (and install the latest Az PowerShell module if not)
Download the script
Download the migration script from the PowerShell Gallery.
PS C:\> Install-Module -Name AzureVMPublicIPUpgrade -Scope CurrentUser -Repository PSGallery -Force
Use the module
Connect-AzAccountto connect to the required Microsoft Entra tenant and Azure subscription
PS C:\> Connect-AzAccount -Tenant <TenantId> -Subscription <SubscriptionId>
Locate the VM with the attached Basic Public IPs that you wish to upgrade. Record its name and resource group name.
Examine the module parameters:
- VMName [string] Required - This parameter is the name of your VM.
- ResourceGroupName [string] Required - This parameter is the resource group for your VM with the Basic Public IPs attached that you want to upgrade.
Run the Upgrade command.
Example uses of the script
To upgrade a single VM, pass the VM name and resource group name as parameters.
Start-VMPublicIPUpgrade -VMName 'myVM' -ResourceGroupName 'myRG'
To evaluate upgrading a single VM, without making any changes, add the -WhatIf parameter.
Start-VMPublicIPUpgrade -VMName 'myVM' -ResourceGroupName 'myRG' -WhatIf
To upgrade all VMs in a resource group, skipping VMs that do not have Network Security Groups.
Get-AzVM -ResourceGroupName 'myRG' | Start-VMPublicIPUpgrade -skipVMMissingNSG
Recovering from a failed migration
If a migration fails due to a transient issue, such as a network outage or client system issue, the migration can be re-run to configure the VM and Public IPs in the goal state. At execution, the script outputs a recovery log file, which is used to ensure the VM is properly reconfigured. Review the log file
PublicIPUpgrade.log created in the location where the script was executed.
To recover from a failed upgrade, pass the recovery log file path to the script with the
-recoverFromFile parameter and identify the VM to recover with the
-VMResourceID parameters, as shown in this example.
Start-VMPublicIPUpgrade -RecoverFromFile ./PublicIPUpgrade_Recovery_2020-01-01-00-00.csv -VMName myVM -VMResourceGroup -rg-myrg
How long will the migration take and how long will my VM be inaccessible at its Public IP?
The time it takes to upgrade a VM's Public IPs depends on the number of Public IPs and Network Interfaces associated with the VM. In testing, a VM with a single NIC and Public IP takes between 1 and 2 minutes to upgrade. Each NIC on the VM adds about another minute, and each Public IP adds a few seconds each.
Can I roll back to a Basic SKU Public IP?
It isn't possible to downgrade a Public IP address from Standard to Basic.
Can I test a migration before executing?
There is no way to evaluate upgrading a Public IP without completing the action. However, this script includes a
-whatif parameter, which checks that your VM will support the upgrade and walks through the steps without taking action.
Does the script support Zonal Basic SKU Public IPs?
Yes, the process of upgrading a Zonal Basic SKU Public IP to a Zonal Standard SKU Public IP is identical and works in the script.
Use Resource Graph to list VMs with Public IPs requiring upgrade
Query to list virtual machines with Basic SKU public IP addresses
This query returns a list of virtual machine IDs with Basic SKU public IP addresses attached.
Resources | where type =~ 'microsoft.compute/virtualmachines' | project vmId = tolower(id), vmNics = properties.networkProfile.networkInterfaces | join ( Resources | where type =~ 'microsoft.network/networkinterfaces' | project nicVMId = tolower(tostring(properties.virtualMachine.id)), allVMNicID = tolower(id), nicIPConfigs = properties.ipConfigurations) on $left.vmId == $right.nicVMId | join ( Resources | where type =~ 'microsoft.network/publicipaddresses' and isnotnull(properties.ipConfiguration.id) | where sku.name == 'Basic' // exclude to find all VMs with Public IPs | project pipId = id, pipSku = sku.name, pipAssociatedNicId = tolower(tostring(split(properties.ipConfiguration.id, '/ipConfigurations/')))) on $left.allVMNicID == $right.pipAssociatedNicId | project vmId, pipId, pipSku
az graph query -q "Resources | where type =~ 'microsoft.compute/virtualmachines' | project vmId = tolower(id), vmNics = properties.networkProfile.networkInterfaces | join (Resources | where type =~ 'microsoft.network/networkinterfaces' | project nicVMId = tolower(tostring(properties.virtualMachine.id)), allVMNicID = tolower(id), nicIPConfigs = properties.ipConfigurations) on \$left.vmId == \$right.nicVMId | join ( Resources | where type =~ 'microsoft.network/publicipaddresses' and isnotnull(properties.ipConfiguration.id) | where sku.name == 'Basic' | project pipId = id, pipSku = sku.name, pipAssociatedNicId = tolower(tostring(split(properties.ipConfiguration.id, '/ipConfigurations/')))) on \$left.allVMNicID == \$right.pipAssociatedNicId | project vmId, pipId, pipSku"