Using Azure App Configuration for Remote Config with Android
September 22, 2019
11 minute read
I’ve been playing with a new app recently. I decided I needed some support from the cloud around feature flags (turning on and off features for specific people so I can test things) and for remote configuration. Fortunately, Azure has a service in preview - App Configuration - and it does both of these things. There are preview libraries to go along with it for .NET, Java 8, JavaScript, and Python.
Sadly, there is no library for Android Java.
Fortunately, we can fix that! Let’s take a look at how I did it.
Creating an App Configuration resource
First step is to create an App Configuration resource. Log on to the Azure portal, create yourself a resource group, then create the App Configuration resource - just like any other resource within Azure. You’ll have to give it a name and select a region, but that’s it.
Now, let’s put some data within the parameter store. To do this:
Select your App Configuration resource
Select Configuration explorer
Click Create
Enter a key and a value. If you want (and I suggest it), also give them a label. I’m using production for my label.
Then click Apply
Repeat the create loop for as many settings as you want.
Once done, select Access keys, then select Read-only keys.
Make a note of the Endpoint, Id, and Secret.
I’ve created a couple of settings here:
Configure the Android project
First step is to ensure that the code we write will be able to access the settings necessary to connect to the remote configuration service. To do this, I’ve added them to the strings.xml file:
The actual values will come from the Access keys page of your resource. I’ve also created a settings data class to hold these values so they are easier to pass around:
To communicate with the backend resource, I’m going to use OkHttp. The configuration comes back with just one call (unless you have a large number of settings - in which case, you need to think a little bit more about the process of remote configuration). I don’t need to communicate with a large number of REST endpoints, so Retrofit (as an example) is overkill in this case.
I’ve added the following to my dependencies:
The current version of OkHttp is 4.2.0.
Create the App Configuration Service
As always when communicating with a remote service, I always create a service class that does the final leg of communication with the service. The methods in the service class correspond 1:1 with the REST methods on the service. I also delegate all the “protocol” level stuff (like authorization, logging, required headers) to an interceptor to make the code for the service class as simple as possible.
In this case, I have an AzureAppConfigurationService as follows:
We can look at this in two sections:
During initialization, I read the settings I placed into the strings.xml file and set up the HTTP client. Interceptors construct a pipeline between the request (the thing you do) and the eventual communication, allowing you to adjust the request and response along the way. In this case, there are two interceptors - one for doing all the requirements for Azure App Configuration, and one for logging the request and response. I’m using the standard logging interceptor to send the request/response to Timber for the logging.
The only endpoint I care about is the one that reads the entire configuration. I read all the settings configured that are labelled a specific way (in this case, production). This allows me to have different configuration sets for production vs. test vs. debug (or production vs. development). It turns the response into a key-value map and returns it. If anything goes wrong, it throws an error.
This is a pretty standard call, but where did I get the information from? Azure does a really good job of explaining all their REST calls by publishing the Swagger. This is the first place I look when discovering what a service can actually do. In this particular case, however, the App Configuration team has published a series of explanatory documents as well. I’m using the key-values document to determine what needs to be done. It says if I do a GET /kv?label=<something>, then I’ll get a JSON document back with an element items that contains an array of items. Those items have a key, value, etag (to determine if it has changed), date, etc. I’m only interested in the current key-value pair, so I extract those and return them as a map.
If anything goes wrong in the decoding, I just throw an error. Aside from being logged, we’ll see how that gets trapped later on.
The special App Configuration interceptor
Every single service on the Internet has a method of authenticating the user and authorizing operations. This is codified for App Configuration in their authentication document. I don’t like to have this detail embedded with every single request, so I abstract it into an interceptor. Let’s go through the code:
The main method here is the intercept method. It is the only method that is required to be an interceptor. In this particular version, we are altering the request object for every single request that comes through. We need some headers, which are added automatically. There are two special headers:
x-ms-date is a copy of the Date header and must be included.
x-ms-content-sha256 is the Base64-encoded SHA256 hash of the body of the request.
In our case, we don’t have a body, so I compute the hash of the empty string. Then, I need to compute the Authorization header. This is a HMAC-SHA256 signature of a specific string, using the Id and Secret of the resource to compute the signature. The computation is covered (in several languages - just not Kotlin) in the authentication.md file. I like the Kotlin computation - it feels easier. The only gotcha is that the header values must be in the same order as the signed headers list, and the code enforces this.
Handling errors
Your app should not cease to function if the App Configuration service goes down, and there are many reasons you may not be able to reach the App Configuration service - throttling, network issues, and service issues all come into the picture.
If, for whatever reason, we cannot reach the service, we can use a cached version of the configuration. For this purpose, I’ve got a higher level configuration service:
This is a singleton object, which needs to call LocalConfigurationStore.initialize(context) to retrieve the configuration into memory. It constructs it by:
Reading the configuration from the cached config.json file. It won’t exist the first time through.
Reading from remote configuration, overwriting the local configuration.
If remote configuration was successful, then write out the cached config.json version.
If remote configuration (i.e. the read from the App Configuration service) was not successful for any reason, then the latest cached copy of the configuration is used instead. The only time this fails is if there is no cached copy. We haven’t properly initialized the configuration yet. Adding in retry logic with incremental back-off would alleviate this problem as well unless the user had no network signal at all.
I don’t want to be doing LocalConfigurationStore.instance.kvStore.whatever() all the time, especially since the service is read-only, so I added common get() operations to the companion object to do that for me:
This allows me to do LocalConfigurationStore.get("auth.fb.appkey"), for example, to retrieve a specific key. The rest of my code is likely to know what keys they need.
Future improvements
Aside from the afore mentioned incremental back-off and retry logic, I might also distribute a config.json file in the res/raw directory. This can be read through resources.openRawResource(). The point? It allows me to distribute a “default” configuration and use that for the condition of “I’ve not downloaded the current configuration yet” situation.
I’m currently working on some automation within Azure to deploy a hub-spoke web application. This web application authenticates with Entra ID using an App Registration and Role-Based Access Controls (RBAS) using App Roles. So, I need to create an app registration, enterprise app, and create the app roles.
One of the persistent questions I get asked about Azure Mobile Apps is how to support older tables. Let’s say you have an older application that was never designed for mobile applications. It likely has a model like this:
If you are an Azure developer, you likely spin up an application, do some work, then shut it down again. However, shutting down resources and deleting them has an order to them. If your service is network isolated (in a virtual network), then you can’t delete the application until the private endpoints are shut down. Budgets don’t get deleted with the resource group. Diagnostic settings can’t be deleted if the resource no longer exists. There is an order you should do things:
I work a lot with Azure API Management, which means I turn up and down services quite a few times a day. Azure API Management has an awesome feature that prevents you from accidentally deleting a service, called soft-delete. Instead of immediately deleting the service, it marks it as soft deleted and purges it later on. Unfortunately, that means that you can’t immediately reuse that service name. In production, this is a great thing to have. In development, it turns into a pain. That’s b...
Leave a comment