I’ve been working during the last week or so on setting up a DR strategy for a solution that is based on API Management, Azure Functions and Service Bus. Most of the deployment to the secondary site is dealt by VSTS, but one of the main issues on the proposed strategy was the fact that APIM instance utilized is Standard, which doesn’t allow multi-region deployments. This way, to guarantee that all APIM configuration, including users, API policies and subscriptions, I had to leverage from the backup/restore functionality available in APIM, based on the Management API.
The API calls for backup and restores are quite straight forward, but use a authorization token that must be requested before the API call can be executed. So, to automate the process to generate the token and execute the backup or restore API calls, I decided to use Logic Apps.
Preparing the environment
To generate the authorization token, I need a service principal with the right permissions on both primary and secondary resource groups. This document shows how to implement a service principal.
Once the service principal was setup, I had to setup permissions in a couple of places:
- Assign delegation permissions to the service principal to access the Azure Management API, so the service principal had permissions to execute the API.
- Assign the correct roles on both the primary and dr resource groups, so the service principal could interact properly with them.
Assigning delegation permissions
To assign delegation permissions to the service principal, follow the steps below:
- Within your Active Directory tentnat navigate to App Registrations > [YourServicePrincipal]> settings (for example in my case the Service Principal is called APIMAuthentication.
- Select Required permissions
- Click on +Add
Select the right API to delegate permissions by clicking on:
- Select an API
- Selecting the Windows Azure Service Management API (remember to click Select afte this)
Finally select and grant the permissions by following the steps below.
- Click on Select Permissins
- Select Access Azure Service Management as organization users (preview)
Assign Resource Group roles
Assigne the following roles on primary and secondary resource groups:
- Primary resource group – API Management Service Contributor
- DR resource group – Contributor
With the service principal setup, my next step was to implement the logic apps.
Logic App structure
Each one of the logic apps followed the same pattern:
Trigger Schema
The logic app require the following input to run:
{ "applicationId": "467ee8da-b314-4ce9-8816-552280e****", "clientSecret": "4GUcWXsQt6ZySewexBSeF4XYqi3XypMNhG**********", "tenantId": "6dda9ee9-7661-42ac-8ea3-b5eb4e3*****", "subscriptionId": "a327e0b5-4c4b-491e-9424-************", "resourceGroup": "apimdrresourcegroup", "apimInstance": "sampleapim", "backupName": "apimbackup", "operation": "backup|restore", "containerName": "backup", "storageAccount": "apimdrstorage", "accessKey": "EDuZ52iakPUzRkzbQtlhdgysTB1ZjJ44heiR/9nHn3vlA/NCLJxAASn2N6ief2ExK/GouRdsD0GwvT**********" }
- applicationId – the unique identifier of the service principal
- clientSecret – the key created for the service principal
- tenantId – the id of the active directory hosting the service principal
- subscriptionId – the id of the subscription hosting the Primary or DR resource group
- resourceGroup – the name of the Primary or DR resource Group
- apimInstance – the name of the APIM instance being backed up or restored
- backupName – the name of the blob containing the backup image
- operation – the operation being performed (backup or restore)
- containerName – the name of the container storing the backup image
- storageAccount – the name of the storage account hosting the backup image
- accessKey – an access key for read/write the backup image.
A sample input payload would look like this:
{ "applicationId": "467ee8da-b314-4ce9-8816-552280e****", "clientSecret": "4GUcWXsQt6ZySewexBSeF4XYqi3XypMNhG**********", "tenantId": "6dda9ee9-7661-42ac-8ea3-b5eb4e3*****", "subscriptionId": "a327e0b5-4c4b-491e-9424-************", "resourceGroup": "apimdrresourcegroup", "apimInstance": "sampleapim", "backupName": "apimbackup", "operation": "backup|restore", "containerName": "backup", "storageAccount": "apimdrstorage", "accessKey": "EDuZ52iakPUzRkzbQtlhdgysTB1ZjJ44heiR/9nHn3vlA/NCLJxAASn2N6ief2ExK/GouRdsD0GwvT**********" }
The API call that generates the authentication token requires that the payload to be sent using url-form encoded, so instead of the usual json payload, the message must be sent in the following format:
In the payload above:
- client_id – this is the client unique identifier (in this case the unique id of the service principal, which is passed to the logic app as applicationId)
- client_secret – this is the client secret generated at AD (passed to the logic app as clientSecret)
- grant_type – this is set to client_credentials, so the API knows how to authenticate
- resource – this is set to https://management.azure.com – which is the API we will hit in the next step (and which we delegated permisssions to).
This payload is passed as the body of an HTTP action with the following configuration:
The authentication token is requested from:
https://login.microsoftonline.com/@{triggerBody()?['tenantId']}/oauth2/token
where tenantId is the ID of the AD tenant where the resources are deployed (and the service principal was created). Also notice the Content-Type: application/x-www-form-urlencoded header. Finally body received the output from the previous compose action were the url-form encoded payload was created.
The result of this execution is an object with multiple properties. One in particular – access_token – is the one we required. This will be required to create the authentication on the next API call. Since this is a bearer token authentication, this is represented as a Raw authentication on the next API call. I’ve created initializing a variable (bearertoken) with the following format:
@concat('Bearer ',body('Request_Auhtentication_Token')['access_token'])
Finally, the api call for the backup have the following format:
The api endpoint is created using the following properties:
- subscriptionId – the unique identifier of the subscription where the API Management instance is deployed (either the main instance or the DR instance), passed as a parameter when the logic app is invoked
- resourceGroup – the name of the resource group where the apim instance is deployed.
- apimInstance – the name of the apim instance being backed up or restored.
- operation – the name of the operation being performed – this should be either backup or restore, as they are the only operations accepted.
The body in this case is a normal json payload (hence the Content-Type: application/json in the header) and have the following properties:
- accessKey – the SAS key for the container where the backup blob will be deployed (or where it will be read from).
- backupName – the name of the blob that will hold the backup
- containerName – the name of the container holding the backup blob
- storageAccount – the name of the storage account where the container has been deployed.
Most important in this case, is the authentication method, which is set to Raw, and have the value of the bearertoken variable (initialized as “Bearer “+access_token).
Usage scenarios
I’ve implemented this logic app to automate backups and to execute restore during a DR event for a project where APIM has been used. The way I am taking advantage of this logic app is:
- I am creating a logic app that runs on a recurrence (in my case daily, but you can set it up on whatever retention makes sense) and invoke this logic app with the correct inputs.
- I’ve mad a small change in the original logic app, that instead of using simply @triggerBody()[‘backupName’] as the name of the backup blob, I am using:
@concat(triggerBody()?['backupName'],'_',convertFromUtc(utcNow(),'New Zealand Standard Time','yyyyMMdd'))
So I don’t override older backups (I am planning to use the latest updates from the storage account to set a retention policy and remove backups older than x days).
- I’ve cloned that logic app and created a specific logic app for backup and restore. This way I can give specific permissions for each one of them individually. In this case I am hardcoding backup or restore on the Management API call.
You can find an ARM template to deploy this logic app here, if you’re interested.
Leave a Reply