Migrating Azure API Management Between Regions

We’ve had a tenancy in Azure for years – so long that our original assumptions and architecture strategies are needing to be overhauled. More importantly, mistakes are becoming glaring limitations. Our original strategy to deploy most primaries to North Central US has evolved into an ExpressRoute connection to East US. We stood up an API Management instance in North Central US and shipped code with a direct link to the instance’s native name (gateway address) before the API management instance could be migrated to East US. Over the Thanksgiving holiday I migrated an Azure API management instance between 2 regions – here’s how I did the migration:

The Elephant in the Room

Before I talk about how to migrate an Azure API management instance – I want to address the mistake – don’t use the native URL for an API management instance. Set up a custom domain.

I’ll cover setting up a custom domain another day – but if you have applications pointed at the specific API management instance name for the gateway URL, you will be facing a roughly 60-minute downtime during the process. What I’ll be talking about below is “option 2” according to this Microsoft documentation. At the very end of the post, I’ll include a script to accomplish the option 1 migration as well.

Get Setup with Azure PowerShell

If you’re not familiar with Azure PowerShell yet (that’s ok!), here’s a great opportunity to get your feet wet. Walk through at least the first 5 units in this MS Learn module on Azure PowerShell: https://docs.microsoft.com/en-us/learn/modules/automate-azure-tasks-with-powershell/

By the time you’re done with those 5 units, you will have PowerShell setup on your Windows/Mac/Linux workstation and be comfortable connecting to your Azure subscription. As a reminder, here are the commands to load the Azure module and open the interactive sign-in window:

import-module Az
Connect-AzAccount

Backup the Current API Management Instance

For both options 1 and 2 of the Microsoft-recommended migration pathways, you need to start by backing up the current API management configuration. You’ll be able to use this backup multiple times (until it expires in 30 days) so we only need to backup once for both the test and production runs of the migration if no changes to the API management config are made during this time.

$resourceGroup = "APIinstances"
$apimgmt = "drewapi"
$newregion = "East US"
$newStorageAccount = "drewapibackups"
$newStorageContainer = "apibackups"
$backupFile = "originalbackup.apimbackup"

New-AzStorageAccount -StorageAccountName $newStorageAccount -Location $newregion -ResourceGroupName $resourceGroup -Type Standard_LRS
$storageKey = (Get-AzStorageAccountKey -ResourceGroupName $resourceGroup -StorageAccountName $newStorageAccount)[0].Value
$storageContext = New-AzStorageContext -StorageAccountName $newStorageAccount -StorageAccountKey $storageKey
Backup-AzApiManagement -ResourceGroupName $resourceGroup -Name $apimgmt -StorageContext $StorageContext -TargetContainerName $newStorageContainer -TargetBlobName $backupFile

Lines 1-6 are setting variables for use throughout the commands – lines 8-11 create a storage account, securely obtain access, and run the backup job (command Backup-AzApiManagement). The file size will likely be in the 1-20 MB range, as our instance backed up to a 1.4MB apimbackup file.

You’ll want to set the $newRegion variable to the destination region for the migration so your backup file is closer to the restoration location. The $apimgmt variable is the current API management instance’s name, which is also the subdomain of the gateway URL. In this example, it is: https://drewapi.azure-api.net , where drewapi is the instance name.

Test the Migration

Create a Test Instance

Even though you’re a knucklehead like myself who has to migrate using option 2 without custom domains, I know you’re going to test the migration before doing it on a live instance(s). Let’s create a new API management instance for testing.

$resourceGroup = "APIinstances"
$apimgmt = "testdrewapi"
$newregion = "East US"
$newStorageAccount = "drewapibackups"
$newStorageContainer = "apibackups"
$backupFile = "originalbackup.apimbackup"

$currenttime = Get-Date
Write-Host "Adding New API Mgmt Instance at: " -NoNewline
Write-Host $currenttime

New-AzApiManagement -ResourceGroupName $resourceGroup -Name $apimgmt -Location $newregion -Organization "DrewskTech" -AdminEmail "drew@drewsk.tech" -Sku Basic -Capacity 1

$currenttime = Get-Date
Write-Host "Completed at: " -NoNewline
Write-Host $currenttime

You’ll notice that the variable $apimgmt has been changed to “testdrewapi” as we’re creating a separate API management instance solely for the purpose of running through the entire destroy-recreate-restore process.

The command New-AzApiManagement creates a new API management instance and returns when the instance has finished spinning up. Depending on your needs, the -Sku flag may be Basic, Standard, or Premium. We’re starting small with a single unit, but if you need to deploy at multiple units set capacity higher than 1. Be patient, have a cup of coffee, finish another task, etc – it will take about 30 minutes.

Practice and Plan

With an instance available for the full destruction, recreation, and restoration process, we can put together our migration script. Warning: the following script contains the destructive command Remove-AzApiManagement. Double-check your variables and use the script at your own risk.

$resourceGroup = "APIinstances"
$apimgmt = "testdrewapi"
$newregion = "East US"
$newStorageAccount = "drewapibackups"
$newStorageContainer = "apibackups"
$backupFile = "originalbackup.apimbackup"

$storageKey = (Get-AzStorageAccountKey -ResourceGroupName $resourceGroup -StorageAccountName $newStorageAccount)[0].Value
$storageContext = New-AzStorageContext -StorageAccountName $newStorageAccount -StorageAccountKey $storageKey

# remove the API management instance
$currenttime = Get-Date
Write-Host "Removing Current API Mgmt Instance at: " -NoNewline
Write-Host $currenttime

Remove-AzApiManagement -ResourceGroupName $resourceGroup -Name $apimgmt

# add it back in a new region
$currenttime = Get-Date
Write-Host "Adding New API Mgmt Instance at: " -NoNewline
Write-Host $currenttime

New-AzApiManagement -ResourceGroupName $resourceGroup -Name $apimgmt -Location $newregion -Organization "DrewskTech" -AdminEmail "drew@drewsk.tech" -Sku Basic -Capacity 1
Restore-AzApiManagement -ResourceGroupName $resourceGroup -Name $apimgmt -StorageContext $StorageContext -SourceContainerName $newStorageContainer -SourceBlobName $backupFile

$currenttime = Get-Date
Write-Host "Completed at: " -NoNewline
Write-Host $currenttime

The $apimgmt variable is still the test instance! The script will output timestamps to the console, so even if you wander off during the roughly 1-hour process, you will know exactly when the events were completed. The Restore-AzApiManagement command utilizes the storage credentials/connection from the backup file and restores the backup into the API management instance specified. This process is the other half of the roughly 1 hour time to complete the migration task.

Cleanup

Azure API management instances aren’t cheap/free so we’re going to clean up our test instance before we go any further – or save the script to run later on the test instance when the migration is complete.

$resourceGroup = "APIinstances"
$apimgmt = "testdrewapi"

$currenttime = Get-Date
Write-Host "Removing Current API Mgmt Instance at: " -NoNewline
Write-Host $currenttime

Remove-AzApiManagement -ResourceGroupName $resourceGroup -Name $apimgmt

Complete the Azure API Management Migration to a New Region

Create a Backup

Option 2

The following script:

  • looks at the storage account where your backup is stored and connects (but does not test the backup file!)
  • removes your API management instance from the original region
  • creates a new API management instance in the new region with the same name
  • restores the new API management instance from the backup
$resourceGroup = "APIinstances"
$apimgmt = "drewapi"
$newregion = "East US"
$newStorageAccount = "drewapibackups"
$newStorageContainer = "apibackups"
$backupFile = "originalbackup.apimbackup"

$storageKey = (Get-AzStorageAccountKey -ResourceGroupName $resourceGroup -StorageAccountName $newStorageAccount)[0].Value
$storageContext = New-AzStorageContext -StorageAccountName $newStorageAccount -StorageAccountKey $storageKey

# remove the old API management instance
$currenttime = Get-Date
Write-Host "Removing Current API Mgmt Instance at: " -NoNewline
Write-Host $currenttime

Remove-AzApiManagement -ResourceGroupName $resourceGroup -Name $apimgmt

# add it back in a new region
$currenttime = Get-Date
Write-Host "Adding New API Mgmt Instance at: " -NoNewline
Write-Host $currenttime

New-AzApiManagement -ResourceGroupName $resourceGroup -Name $apimgmt -Location $newregion -Organization "DrewskTech" -AdminEmail "drew@drewsk.tech" -Sku Basic -Capacity 1
Restore-AzApiManagement -ResourceGroupName $resourceGroup -Name $apimgmt -StorageContext $StorageContext -SourceContainerName $newStorageContainer -SourceBlobName $backupFile

$currenttime = Get-Date
Write-Host "Completed at: " -NoNewline
Write-Host $currenttime

When the script has completed, you’ll see a “Completed at: ” output to the console. Your API management instance will be online and accepting requests! (go ahead and start planning that custom domain name now)

Option 1

The following script:

  • looks at the storage account where your backup is stored and connects (but does not test the backup file!)
  • creates a new API management instance in the new region with the new name
  • restores the new API management instance from the backup
$resourceGroup = "APIinstances"
$apimgmt = "drewapi2"
$newregion = "East US"
$newStorageAccount = "drewapibackups"
$newStorageContainer = "apibackups"
$backupFile = "originalbackup.apimbackup"

$storageKey = (Get-AzStorageAccountKey -ResourceGroupName $resourceGroup -StorageAccountName $newStorageAccount)[0].Value
$storageContext = New-AzStorageContext -StorageAccountName $newStorageAccount -StorageAccountKey $storageKey

# add a new instance in a new region
$currenttime = Get-Date
Write-Host "Adding New API Mgmt Instance at: " -NoNewline
Write-Host $currenttime

New-AzApiManagement -ResourceGroupName $resourceGroup -Name $apimgmt -Location $newregion -Organization "DrewskTech" -AdminEmail "drew@drewsk.tech" -Sku Basic -Capacity 1
Restore-AzApiManagement -ResourceGroupName $resourceGroup -Name $apimgmt -StorageContext $StorageContext -SourceContainerName $newStorageContainer -SourceBlobName $backupFile

$currenttime = Get-Date
Write-Host "Completed at: " -NoNewline
Write-Host $currenttime

You have a few tasks to complete after the restoration, including:

  • update the custom domain and CNAME to the new API management instance
  • remove the old API management instance

Success!

With the power of PowerShell, you’re able to script out the changes, test, and run when best suited.