How cool would it be to manage your Azure API Management Services through the services themselves? I tried it out for a case where we wanted to add the signed-up users to groups without going through the process of logging into the portal itself, look for the user and add that user to the specified group. It is entirely possible and you have a fine-grained control over what actions can be taken and by whom.
What this blog post will do:
In this blog, we will add our Azure API Management service (or APIM) in Azure Active Directory (or AAD) as an application and grant it the roles for the specific APIM instance, enabling it to authenticate itself and accessing its own API. Afterwards, we will build up a policy that will authenticate itself to AAD and save the token until it expires.
Walkthrough:
First of all, we will register our APIM on AAD. Go to your Azure Active Directory (No worries, every subscription has one) and add a New Application Registration.
Give your application a fitting name, choose API and put in something in the Sign-on URL. We won’t need it for this application.
When the application has been created, take note of the Application ID and generate a new key by giving the key a name and duration and saving.
IMPORTANT: The key is ONLY retrievable after you save. Be sure to store it somewhere safe.
Now that APIM has been registered as an app, we need to grant this app access to itself.
Locate your APIM Resource and navigate to the Access Control (IAM) blade. Add a new Access for the application that has been registered and give it the API Management Service Contributor role, giving the app permissions to change our APIM Service.
Before we start with the policy, a little bit on how to access the Azure REST API.
Create a new API in APIM that will hold all these self-managing endpoints. The backend service will be “https://management.azure.com” + the ID of your subscription (this can be found on the properties blade of your service).
For a GET that will return all groups, use /groups.
IMPORTANT: Every call must have a query parameter for the api-version. Currently the following is used: ?api-version=2016-10-10
For more information on what services and operations you can call, see this article.
Your link should like this:
https://management.azure.com/subscriptions/{{SubscriptionID}}/resourceGroups/{{ResourceGroupID}}/providers/Microsoft.ApiManagement/service/{{ServiceName}}/groups?api-version=2016-10-10
And finally, the policy. It will have to do the following:
- Look in the cache for the token
- If it is not set
- Send a request to the token endpoint
- Read the token from the body
- Store it in cache
- Set the Authorization header for the request and proceed
- If the token is still in cache
- Set the Authorization header for the request and proceed
We will now go a bit more into detail below. The policy itself can be found below that for reference (and copying).
- The cache-lookup-value policy will look in a key-value pair dictionary for the key we provided.
The key here is “token” and the result will be pushed into the variable “token” that we provided in variable-name.
If no value is found, the token variable will automatically get a “noToken” value. - The choose policy will check the token variable for the “noToken” value. If this is true, that means no access token exists in the cache
- POST a request to Microsoft’s OAuth token endpoint, replacing {{Tenant}} with your tenant and storing the response in a variable tokenResponse.
Your tenant can be found by clicking on your profile and will be typically end with “.onmicrosoft.com” - Set the body as in the example and replace {{ResourceID}} and {{ClientSecret}} with the values you received earlier when registering your application.
- Save the response in a variable for convenience.
The variable tokenResponse has to be cast to an IResponse before we’re able to access the body and our token. - Store the value in cache. The token we received expires in an hour so we set the cache to store that value for a little less than that.
- Set the header in the right format: Type of token, space, token.
- POST a request to Microsoft’s OAuth token endpoint, replacing {{Tenant}} with your tenant and storing the response in a variable tokenResponse.
- The token was still in cache
- Set the header in the right format: Type of token, space, token.
Apply the policy to all your operations for one API. This API will always be authenticated and should only handle the calls for its own Management API.
[code language=”xml”]
https://login.microsoftonline.com/{{Tenant}}/oauth2/tokenPOSTapplication/x-www-form-urlencoded
@{
return “grant_type=client_credentials&client_id={{ResourceID}}&client_secret={{ClientSecret}}&resource=https%3A%2F%2Fmanagement.azure.com%2F”;
}
()[“access_token”].ToString())” />
@(String.Concat(“Bearer “, ((string)context.Variables.GetValueOrDefault(“accessToken”))))@(String.Concat(“Bearer “, ((string)context.Variables.GetValueOrDefault(“token”))))
[/code]
The JSON token response looks like this:
Tips:
This article has a good overview on how the different OAuth flows work.
Accessing the API Management’s own API does not need the checkbox ticked on the old Publisher Portal. The API used here is Azure’s own API that can be used to manage your Azure.
For more information on what services and operations you can call with the Azure REST API, see this article.
Your secret has to be URL-encoded to be passed along through the policy.
Response bodies can not be worked with until they have been cast as an IResponse object.
Your tenant can be found by clicking on your profile and will be typically end with “.onmicrosoft.com”
When reading the response body in policy, you can opt to read out the response every time. To do this, you can pass the preserveContent parameter. I have opted to read it out once and put the necessary info in a variable for a clearer overview.
Conclusion:
This solution allows us to manage the APIM service through itself, using only our subscription key instead of the need to log in. Publicly opening up our management API can be dangerous but the granularity of choosing which endpoints we make available combined with the need for a subscription key and the implemented AAD RBAC contains and minimizes the risk as much as possible.