Thomas Step

← Blog

I recently launched a Slack app to help with channel bloat! Simply installing it would help me out a bunch. I have 2/10 installations required to submit my app to the Slack Marketplace. Thanks for helping me reach that goal.

Parameter Store and CloudFormation

Lately, I have been playing around with AWS SSM Parameter Store, and I have found a fun way of creating and referencing parameters through CloudFormation. Parameters help me stay organized throughout different environments by unifying all of my CloudFormation “variables” in one place. I populate my parameters based on the environment I am in and then use those parameters in other templates that create infrastructure. Of course doing this means that order matters; the Parameter Store CloudFormation template needs to be run before templates that reference those parameters. I create all my stacks through CodePipeline, so I can define the order that they are created and updated, which means I always know my parameters are present before I use them in subsequent templates. The strategies that I use can also be applied outside of CI/CD using CodePipeline as long as you make sure that your parameters template runs before you reference them in other templates.

The first example I want to walk through is populating parameters based on environment. The way I like to go about doing this is with CloudFormation Mappings.

AWSTemplateFormatVersion: 2010-09-09
Description: Adds parameters to systems manager
Parameters:
  AWSEnvironment:
    Type: String
    AllowedValues:
      - dev
      - preprod
      - prod

Mappings:
  Environment:
    dev:
      DBHost: "dev.db"
      .
      .
      .

    preprod:
      DBHost: "preprod.db"
      .
      .
      .

    prod:
      DBHost: "prod.db"
      .
      .
      .

Resources:
  AWSEnvironmentParameter:
    Type: AWS::SSM::Parameter
    Properties:
      Name: /awsEnvironment
      Description: Current AWS environment
      Tier: Standard
      Type: String
      Value: AWSEnvironment

  LogFirehoseParameter:
    Type: AWS::SSM::Parameter
    Properties:
      Name: /dbHost
      Description: Database Host
      Tier: Standard
      Type: String
      Value: !FindInMap [ Environment, !Ref AWSEnvironment, DBHost ]

Hopefully it is easy enough to see what is happening here. Under Mappings I am predefining the values for my parameters. You can add as many mappings as you would like, which is what I was attempting to show with the extra .s.

Next up is showing how to use the parameters we just created in other CloudFormation templates.

AWSTemplateFormatVersion: 2010-09-09
Parameters:
  Env:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /awsEnvironment
  DBHost:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /dbHost

Resources:
  MyLambdaFunction:
    Type: 'AWS::Lambda::Function'
    Properties:
      .
      .
      .
      Environment:
        Variables:
          NODE_ENV: !Ref Env
          DB_HOST: !Ref DBHost

The parameters from Parameter Store are passed into the Lambda CloudFormation template like any other parameters; however, the Type and Default properties of the CloudFormation parameters matter here. The Type is telling CloudFormation that the parameter input will be a value from SSM Parameter Store instead of a value that the user gives. The Default property is giving CloudFormation the name of the parameter from which we want to pull a value. The name here needs to coorespond to the Name property of the parameter in the Parameter Store template. I prefer to use a Default property here instead of passing (manually or otherwise) the Parameter Store name, so I never have to worry about what parameter the value actually came from since it is defined in my template. After you define the CloudFormation parameter, you can use the value like normal. In this example, I am passing the DB Host I set in Parameter Store to my Lambda as an environment variable. Now, if you ever need traceability into where a certain environment variable value came from, you can reference back to your templates, which can be version controlled.

Like I said before I use this in conjunction with CodePipeline, which I think is a match made in heaven. My CodePipeline instance picks up my templates from a GitHub webhook that fires off at a push, builds the parameters stack, and updates the lambda stack. I can add parameters and then reference them in a template, and after a simple push, they show up in AWS.

Categories: ops | aws