We'll start with setting up an AppConfig application in the account where we want to centrally manage the feature flags. In our case the Tools account.
Create an Application. Applications are a logical grouping of Configuration Profiles which in turn can have flags. A suggestion would be to name it after your application/solution you're developing.
Next up is creating an Environment. AWS describes them as "logical deployment groups of AppConfig targets" so a name like dev
or prod
would make sense here.
The next step is to create a Configuration Profile. Configuration profiles are either collections of feature flags or a "Freeform configuration". In this post we're focusing on FFs.
In the configuration profile you create the flags you want to toggle.
Save the configuration with the flags. Give it a version label or let AWS handle it for you.
Create an IAM Role in the same account with a policy which allows the actions: GetLatestConfiguration
and StartConfigurationSession
. Example policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"appconfig:GetLatestConfiguration",
"appconfig:StartConfigurationSession"
],
"Resource": "*"
}
]
}
AssumeRole
on all AWS accounts (wait for it), but have a condition. The condition is a StringLike
where you can define what OUs you want to allow access to the FFs. Example trust relationship:{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "sts:AssumeRole",
"Condition": {
"ForAnyValue:StringLike": {
"aws:PrincipalOrgPaths": [
"o-orgiId/r-rootId/ou-WorkloadsId*"
]
}
}
}
]
}
In the example here I'm allowing the AssumeRole action on all OrgPaths that begin with my organization id/organization root id/WorkloadsOuId*
which essentially means all accounts within my Workloads OU. Since it's an array you can add several OUs if you need to. Now everything is ready in the Tools account!
To then fetch the feature flags in your front- or backend you need to first get the STS token and then use that as the credentials for the AppConfig client. Here is an example code snippet to finish the writeup:
const stsClient = new STSClient({
region: REGION,
});
interface Settings {
applicationID: string;
configurationProfileID: string;
environmentID: string;
}
export const getAppConfiguration = async (
settings: Settings,
) => {
const stsResponse = await stsClient.send(
new AssumeRoleCommand({
RoleArn: READ_FLAGS_ROLE,
RoleSessionName: ASSUME_ROLE_SESSION_NAME,
}),
);
if (!stsResponse.Credentials) {
return null;
}
const { AccessKeyId, SecretAccessKey, SessionToken } =
stsResponse.Credentials;
if (!AccessKeyId || !SecretAccessKey) {
throw new Error(`Failed to retrieve ${ASSUME_ROLE_SESSION_NAME} `);
}
const client = new AppConfigDataClient({
region: REGION,
credentials: {
accessKeyId: AccessKeyId,
secretAccessKey: SecretAccessKey,
sessionToken: SessionToken,
},
});
const sessionResponse = await client.send(
new StartConfigurationSessionCommand({
ApplicationIdentifier: settings.applicationID,
ConfigurationProfileIdentifier: settings.configurationProfileID,
EnvironmentIdentifier: settings.environmentID,
}),
);
const configurationResponse = await client.send(
new GetLatestConfigurationCommand({
ConfigurationToken: sessionResponse.InitialConfigurationToken,
}),
);
return JSON.parse(
Buffer.from(configurationResponse.Configuration ?? '').toString('utf-8'),
);
};
For additional reading on retrieving and using a configuration AWS has a detailed page in their docs.
That's it! Hopefully you learned something!
If you enjoyed this post you could follow me on twitter at @Paliago where I honestly mostly retweet dumb AWS related things. There might be some posts on the horizon as well πΆβπ«οΈ
Elva is a serverless-first consulting company that can help you transform or begin your AWS journey for the future