Loading Configurations Based on Environment in AWS CodeDeploy

The project I’ve been working on for some time has multiple environments for multiple clients and each environment has its environment configurations. Therefore, I needed a secure way to store the environment configurations and to fetch them in real-time while the application was being deployed.

After doing some research, I found that AWS provides Environment Variables for AWS CodeDeploy during deployment that we can use with lifecycle event hooks.

I came across the idea to store environment configurations as a secret in AWS Secrets Manager with name same as CodeDeploy deployment group name; and fetch the configurations using DEPLOYMENT_GROUP_NAME environment variable amidst the deployment.

e.g. If we have a deployment group with name Client-Dashboard-DG, we’ll also store the secret in secret manager with the same name.

Next, in the appspec.yml file, we will execute a bash script to get the secrets from the AWS Secret Manager. This is how our `appspec.yml` file would look like

hooks:
BeforeInstall:
- location: scripts/getconfig.sh
timeout: 600
runas: root

And `getconfig.sh` file would look like this

#!/bin/bash

cd /opt/codedeploy-agent/deployment-root/${DEPLOYMENT_GROUP_ID}/${DEPLOYMENT_ID}/deployment-archive/export 

NODE_PATH=/usr/lib/node_modules/

node scripts/secret-manager-to-env.js

Our JS file to get the secrets would look like

// Use this code snippet in your app.
// If you need more information about configurations or implementing the sample code, visit the AWS docs:
// https://aws.amazon.com/developers/getting-started/nodejs/

// Load the AWS SDK
DEPLOYMENT_GROUP_NAME = process.env.DEPLOYMENT_GROUP_NAME

var AWS = require('aws-sdk'),
    region = "us-west-2",
    secretName = `${DEPLOYMENT_GROUP_NAME}`,
    secret,
    decodedBinarySecret;
var fs = require('fs');

// Create a Secrets Manager client
var client = new AWS.SecretsManager({
    region: region
});

// In this sample we only handle the specific exceptions for the 'GetSecretValue' API.
// See https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html
// We rethrow the exception by default.

client.getSecretValue({SecretId: secretName}, function(err, data) {
    if (err) {
        console.log("*******************************")
        console.log("Error fetching secrets")
        console.log("*******************************")
        process.exit(1);
	if (err.code === 'DecryptionFailureException')
            // Secrets Manager can't decrypt the protected secret text using the provided KMS key.
            // Deal with the exception here, and/or rethrow at your discretion.
            throw err;
        else if (err.code === 'InternalServiceErrorException')
            // An error occurred on the server side.
            // Deal with the exception here, and/or rethrow at your discretion.
            throw err;
        else if (err.code === 'InvalidParameterException')
            // You provided an invalid value for a parameter.
            // Deal with the exception here, and/or rethrow at your discretion.
            throw err;
        else if (err.code === 'InvalidRequestException')
            // You provided a parameter value that is not valid for the current state of the resource.
            // Deal with the exception here, and/or rethrow at your discretion.
            throw err;
        else if (err.code === 'ResourceNotFoundException')
            // We can't find the resource that you asked for.
            // Deal with the exception here, and/or rethrow at your discretion.
            throw err;
    }
    else {
        // Decrypts secret using the associated KMS CMK.
        // Depending on whether the secret is a string or binary, one of these fields will be populated.
        if ('SecretString' in data) {
            secret = JSON.parse(data.SecretString);
            for (let key in secret) {
                if(secret.hasOwnProperty(key))
                fs.appendFile('.env', `${key}='${secret[key]}'` + '\r\n', function (err) {
                        if (err) throw err;
                        console.log(`${key}='${secret[key]}' Saved!`);
                });
            }

        } else {
            let buff = new Buffer(data.SecretBinary, 'base64');
            decodedBinarySecret = buff.toString('ascii');
        }
    }
    
    // Your code goes here. 
});

And, you may find this script helpful and clap if it does

Leave a Comment

Your email address will not be published. Required fields are marked *