AppConfig in StepFunctions

2022-06-30

AWS AppConfig is one of those services that, for some reason, flew below my radar. I have never paid much attention to it. One reason could be that AWS AppConfig is actually part of AWS Systems Manager (SSM), and as a primarily serverless developer I don't pay much attention to SSM. So I decided that should experiment with it, since it's a very powerful way to control the flow in your application using configurations and feature flags. What I decided to do was to test and control flows in an AWS StepFunction by using different flags. That way it should be possible to control the flow and test different business logic with just the flip of a switch. In this post we will take a look on what I did.

First of all we must configure and setup resources in AppConfig so let's head over to the console and get started.

AppConfig Resources

We will create several different resources AppConfig. We will create an Application, Environment, and Feature Flag.

Application

The Application is the outer container and is as the name suggest represent our application or services.

I create an application that I name StepFunctions-Test, fill in a name and click create. image

Environment

First of all we'll create an environment. This so we can use the same flag, with different values and versions, for different environment. This makes it possible to turn on features in dev and test environments and keeping them off in the production environment. To create an environment select the Environments tab and click Create Environment. image

In this test there is no need for a multi environment setup so let's just create an environment named prod. image

We'll be using this new environment later in the setup and test.

Configuration Profiles and Feature Flags

Next we need to create a configuration and our feature flag. Select the Configuration Profiles and Feature Flags tab and click on create. image

We will use the Feature Flags configuration, we can create one or several flags. The free form configuration has much more freedom but for this test Feature Flags is a great choice. image

Before creating any feature flags we must create a Feature Flags so click Select to create the configuration. I call my configuration Test-Feature. image

To create our first flag click on + Add new flag button to the right in the UI. Here we will give our flag a name and a key, the name is more human readable and the key is what we will use to fetch the flag. We can also add optional attributes. We can use the attributes to create a complex flag. You can see the attributes as sub-flags to the "global" flag. In this test we will not use any attributes, instead just rely on the flag it self. Let's create a flag with name and key set to allow-feature. image

The flag must be saved as a new version, before we can start the deployment. Just hit the Save new version button to create the first version. image

Next step would be to deploy the feature flag. Here we have several interesting options, we can either deploy All at once, Linear 50% every 30 seconds, Canary 10% every 20 minutes. With this we can control how many of our users would get the new version of the flag, which is a very nice feature. Let's say that we like to enable a feature in our application, we do that by enabling a feature flag, but we don't every user to get access immediately, with these built in deployment strategies that is easy to to do. We could also create our own custom deployment strategy if we like to have even more control. image

But, in this test there is no need to slowly roll out the change. Therefor we'll just use the All at once option. Select the environment that we created previously and the version 1 of the flag and hit Start deployment image

Use it in a StepFunction

Time to try and read the feature flag from an StepFuntion, we'll use as little code as possible and use SDK Tasks to call AppConfig. First we just try and read the flag and later start controlling the flow in a simple demo. Move over to the StepFunctions part of the console and create a StepFunction, let's use the Standard Flow for an easy debugging. I will name my StepFunction AppConfig-Test and I will be using Workflow Studio when creating it. image

When reading an feature flag from AppConfig we must first start a session by calling StartConfigurationSession API. We can use an SDK Task to call that API. image

After adding the Task give the state a name, I will call mine StartConfigurationSession for simplicity. Add the following as API Parameters.

{
"ApplicationIdentifier": "StepFunctions-Test",
"ConfigurationProfileIdentifier": "Test-Feature",
"EnvironmentIdentifier": "prod"
}

Before we test and run the StepFuntion we must update the Execution Role with permissions to call AppConfig click the link to the IAM Role image

Next add a new inline policy and add permissions for AppConfig, we give full access during this test.

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "appconfig:*",
"Resource": "*"
}
]
}

image

After this we are ready to test the StepFuntion and see if we can call StartConfigurationSession. Start a new execution and just add any message as the input event. image

If all go well the execution should succeed and we should have a InitialConfigurationToken in the output. image

So far so good, now we need add one more state to be able to read the actual Feature Flag. To be able to do that we will need to use the InitialConfigurationToken from the previous state. So right after the StartConfigurationSession add a new Task that is calling GetLatestConfiguration API. The API Parameters should specify ConfigurationToken as such

{
"ConfigurationToken.$": "$.InitialConfigurationToken"
}

image

That looks great, we can now run more test execution and we should have a nice result coming back. We get the setting for the feature flag in the Configuration field. This is a string encoded JSON blob, which we need to take into considerations later. image

Demo in action

Now let's build out a small demo where we first check if the feature flag allow-feature is enabled of not and take different paths in the state machine. After that let's try and extend it with an attribute to do even more complex logic. We add a Choice state directly after the GetLatestConfiguration state. We add two rules to check if the feature is enabled or not. Remember we have the enabled / disabled value in the Configuration. But as mentioned this is string encoded JSON so we will not be able to read the value as we normally do in a JSON object, $.Configuration.allow-feature.enabled as this will cause an error. We need to add a ResultSelector to the GetLatestConfiguration state. We use the intrinsic function StringToJson to convert the string into a proper JSON object and store it in a new field. That will then give us the following configuration.

image

{
"StartAt": "StartConfigurationSession",
"States": {
"StartConfigurationSession": {
"Type": "Task",
"Parameters": {
"ApplicationIdentifier": "StepFunctions-Test",
"ConfigurationProfileIdentifier": "Test-Feature",
"EnvironmentIdentifier": "prod"
},
"Resource": "arn:aws:states:::aws-sdk:appconfigdata:startConfigurationSession",
"Next": "GetLatestConfiguration"
},
"GetLatestConfiguration": {
"Type": "Task",
"Parameters": {
"ConfigurationToken.$": "$.InitialConfigurationToken"
},
"Resource": "arn:aws:states:::aws-sdk:appconfigdata:getLatestConfiguration",
"ResultSelector": {
"Config.$": "States.StringToJson($.Configuration)",
"NextPollConfigurationToken.$": "$.NextPollConfigurationToken"
},
"Next": "Choice"
},
"Choice": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.Config.allow-feature.enabled",
"BooleanEquals": true,
"Next": "Enabled"
},
{
"Variable": "$.Config.allow-feature.enabled",
"BooleanEquals": false,
"Next": "Disabled"
}
],
"Default": "Disabled"
},
"Enabled": {
"Type": "Pass",
"End": true
},
"Disabled": {
"Type": "Pass",
"End": true
}
},
"Comment": "Testing AppConfig"
}

Now let's run a test and see the result, the feature is disabled and we should end up in that state. We can clearly see that the input to the choice state has been nicely transformed into proper JSON. image

Now we can move over to AppConfig part of the console, where we enable the feature, save it as version 2 and start a All at once deployment when that has completed we can rerun our StepFunction and we should end up in the enabled state instead. The deployment will take a few minutes too complete. When we have reached completed state we can run our test. image image image

Add some attributes

Now let's make everything a bit more complex and let's add an attribute that we name evaluate that is a boolean value. We then add this to our state machine and introduce additional paths for the logic to take. Save it as version 3 and start the deployment of that version. image

Then we update the Choice state and introduce a Evaluate True / False condition which gives us a definition below. image

{
"StartAt": "StartConfigurationSession",
"States": {
"StartConfigurationSession": {
"Type": "Task",
"Parameters": {
"ApplicationIdentifier": "StepFunctions-Test",
"ConfigurationProfileIdentifier": "Test-Feature",
"EnvironmentIdentifier": "prod"
},
"Resource": "arn:aws:states:::aws-sdk:appconfigdata:startConfigurationSession",
"Next": "GetLatestConfiguration"
},
"GetLatestConfiguration": {
"Type": "Task",
"Parameters": {
"ConfigurationToken.$": "$.InitialConfigurationToken"
},
"Resource": "arn:aws:states:::aws-sdk:appconfigdata:getLatestConfiguration",
"ResultSelector": {
"Config.$": "States.StringToJson($.Configuration)",
"NextPollConfigurationToken.$": "$.NextPollConfigurationToken"
},
"Next": "Choice"
},
"Choice": {
"Type": "Choice",
"Choices": [
{
"And": [
{
"Variable": "$.Config.allow-feature.enabled",
"BooleanEquals": true
},
{
"Variable": "$.Config.allow-feature.evaluate",
"BooleanEquals": true
}
],
"Next": "Evaluate True"
},
{
"And": [
{
"Variable": "$.Config.allow-feature.enabled",
"BooleanEquals": true
},
{
"Variable": "$.Config.allow-feature.evaluate",
"BooleanEquals": false
}
],
"Next": "Evaluate False"
},
{
"Variable": "$.Config.allow-feature.enabled",
"BooleanEquals": false,
"Next": "Disabled"
}
],
"Default": "Disabled"
},
"Evaluate True": {
"Type": "Pass",
"End": true
},
"Disabled": {
"Type": "Pass",
"End": true
},
"Evaluate False": {
"Type": "Pass",
"End": true
}
},
"Comment": "Testing AppConfig"
}

Now lets run a new test in which the evaluate attribute is set to true. It will give us a flow that looks like this, and if we flip the switch and set evaluate to false it will take a different path. If the flag is disabled totally it will go down a third route. Update the values and play around. image

Conclusion

After using AppConfig for the first time I must say that I'm impressed. It comes packed with some really nice features, such deployment strategies. I can see that in a serverless application that this service can be very helpful to test out different business logic. The service is a really powerful complement to normal blue/green and canary deployments. I will keep using it for different tasks. Just wished it wasn't hidden away among all other SSM features.

Final Words

Don't forget to follow me on Twitter and read rest of my Blogs