How to migrate Azure API Management Developer portal to another APIM instance with Azure DevOps

Published by Peter KARDA
Category : API Management / DevOps

The Azure API Management (APIM) Developer Portal is shipped with the default theme. Many customers choose to change it to reflect the corporate branding and to present the APIs in a custom way.

Technically the portal is based on the Paperbits framework which allows for redesigning the portal within the browser. The whole contents are stored in a JSON file and the images are hosted on the internal blob storage of API Management.

In the real world, we often have multiple APIM environments (DEV, STG, PROD, …) which raises the question of how to migrate the Developer Portal from one environment to another. In this article, we’ll show how to do that with Azure DevOps.

This article will show you how to create an Azure DevOps (YAML) pipeline and use the APIM Developer Portal tools to migrate the portal contents.


Migration tools

Azure APIM Developer Portal is an open-source project that along with the portal files contains also the migration tools. Currently, there are two versions of the tools (v2 and v3) but in the article, we’ll use the latest version that is located in the “scripts.v3” folder. The folder contains several Node.js scripts and batch files for the migration.


Migration process

Note that in this folder you can find the migrate.js and migrate.bat files that allow us to migrate the portal with only one command. It worked well when I did the migration locally where my Azure account had enough permissions to proceed with the migration. However, in my Azure DevOps environment, I had only Service connections for the source and destination (target) subscriptions so the migration had to be done in another way. Instead of one migrate command we’ll use multiple commands that allow us to proceed step-by-step with the migration.

The commands we’ll need to execute are (in that order) :

  • capture – captures the contents of the source Developer portal
  • cleanup – cleans-up the contents of the destination Developer portal to avoid the conflicts
  • generate – generates the contents for the destination Developer portal (replacing URLs,…).

Fair enough, let’s create an Azure DevOps pipeline with those commands to proceed with the migration.


Creating and configuring the Azure DevOps pipeline

Now let’s see how to create the YAML pipeline. As mentioned earlier, we need to execute three commands – capture, cleanup and generate. The pipeline has to capture the data from the source APIM Developer Portal, clean up the destination (portal) and generate contents for the destination APIM Developer Portal. Recently a new option (–publish true) was added to the generate command. With this option, we can publish the portal just after the contents generation.

As the migration tools are implemented in NodeJs, the first task in the pipeline will be the installation of the Node.js JavaScript runtime.

The source code below contains the Azure DevOps pipeline (YAML). It will execute all the tasks necessary for the migration of the Azure API Management Developer portal from one APIM instance to another. I’ll explain all the pipeline variables later in the article.

name: APIM_DeveloperPortalMigration	 
trigger: none

  location: northeurope
  sourceServiceConnection: <source_service_connection>
  destServiceConnection: <destination_service_connection>
  sourceSubscriptionId: <source_subscription_id>
  destSubscriptionId: <destination_subscription_id>
  sourceResourceGroupName: <source_apim_instance_resource_group>
  destResourceGroupName: <destination_apim_instnace_resource_group>
  sourceServiceName: <source_apim_instance_name>
  destServiceName: <destination_apim_instance_name>

  # All tasks on APIM Developer portal pipeline
  - job: Deploy_APIM_Developer_Portal
    displayName: Deploy APIM Developer portal from one APIM instance to another APIM instance Migration
      vmImage: ubuntu-latest
    timeoutInMinutes: 90
      - task: Npm@1
        displayName: Npm Install command
          command: "install"
          workingDir: 'portal/scripts.v3'
      - task: AzureCLI@2
        displayName: Capturing source portal
          azureSubscription: $(sourceServiceConnection)
          scriptType: 'pscore'
          workingDirectory: 'portal/scripts.v3'
          scriptLocation: 'inlineScript'
          inlineScript: 'node ./capture --subscriptionId $(sourceSubscriptionId) --resourceGroupName $(sourceResourceGroupName) --serviceName $(sourceServiceName)'
      - task: AzureCLI@2
        displayName: Cleaning up destination portal
          azureSubscription: $(destServiceConnection)
          scriptType: 'pscore'
          workingDirectory: 'portal/scripts.v3'
          scriptLocation: 'inlineScript'
          inlineScript: 'node ./cleanup --subscriptionId $(destSubscriptionId) --resourceGroupName $(destResourceGroupName) --serviceName $(destServiceName)'
      - task: AzureCLI@2
        displayName: Generating destination portal
          azureSubscription: $(destServiceConnection)
          scriptType: 'pscore'
          workingDirectory: 'portal/scripts.v3'
          scriptLocation: 'inlineScript'
        inlineScript: 'node ./generate --publish true --subscriptionId $(destSubscriptionId) --resourceGroupName $(destResourceGroupName) --serviceName $(destServiceName)'


Pipeline variables

The following  parameters (in angle brackets) should be specified:

<source_service_connection> – service connection in Azure DevOps, for accessing resources in the source subscription

<destination_service_connection> – service connection in Azure DevOps, for accessing resources in the destination subscription

<source_subscription_id> – subscription id of the source APIM instance

<destination_subscription_id> – subscription id of the destination APIM instance

<source_apim_instance_resource_group> – resource group name of the source APIM instance

<destination_apim_instnace_resource_group> – resource group name of the destination APIM instance

<source_apim_instance_name> – name of the source APIM instance

<destination_apim_instance_name> – name of the destination APIM instance


Preparing DevOps repository

Let’s prepare a workspace in the Azure DevOps repository. First, I’ve created a “portal” folder where we’ll keep all portal-related files and migration tools. I’ve cloned the APIM Developer portal GitHub repository to my local disk and then I’ve copied the “scripts.v3” folder.

Then I’ve created my Azure DevOps YAML pipeline and pushed it to the portal folder.

Finally, I have committed and pushed the files to the Azure DevOps repository so the structure on the Azure DevOps repository looks something like this:

APIM portal migration tools

After the files are in the Azure DevOps repository, all you need is to find the YAML pipeline file in the Azure DevOps and execute it. If everything is configured correctly and the service connections have sufficient permissions, the portal content will be migrated between the Azure APIM instances.


One step further

It might happen that the destination portal should use the different URLs (custom domains) that are attributed by default to the APIM instance. In that case, we can enrich our pipeline with one more task and call the updatecontenturl command:

- task: AzureCLI@2
  displayName: Updating destination urls
    azureSubscription: $(destServiceConnection)
    scriptType: 'pscore'
    workingDirectory: 'portal/scripts.v3'
    scriptLocation: 'inlineScript'
    inlineScript: 'node ./updatecontenturl --subscriptionId $(destSubscriptionId) --resourceGroupName $(destResourceGroupName) --serviceName $(destServiceName) --existingEnvUrls $(existingEnvUrls) --destEnvUrls $(destEnvUrls)'

We’ll also need to add two new variables to the variable list:

  • existingEnvUrls – one or more comma-separated existing URLs
  • destEnvUrls – one or more comma-separated destination URLs

If multiple URLs are specified, the number and order of URLs must match the existing and destination URL list.



That’s it! The latest version of the tools has made the migration process easier than it was some time ago. The source files of the migration commands are well documented, so do not hesitate the check them.