This is the final article in a series that describes Using AWS KMS to Encrypt Values in CloudFormation Stacks.

Up to this point I’ve just been setting up all of the pieces required for this final part. That is, making encrypted values available to our CloudFormation stacks.

It’s important to note that it’s technically possible to include everything that’s been done so far directly in a CloudFormation template (as long as all of it is included), however this may not be desirable. If you’re like me, you deploy a lot of CloudFormation stacks. Do you really want to be creating all of these supporting pieces for each stack? Probably not. If nothing else, think about the KMS charges . . . in this case, I prefer a one time setup like I’ve demonstrated here.

With everything ready to go, let’s launch a CloudFormation stack that makes use of all this juicy stuff. To do that, grab a copy of the CloudFormation template example that I’ve made available on GitHub. Note that this template doesn’t actually create any real resources, it just accepts some parameters, and delivers the results to the stack’s outputs.

There are several things to talk about in this template.

Parameters

{
  "SuperSecretThing": {
    "Description": "Some password or other thing that has to be secure",
    "Type": "String",
    "Default": "This could be anything . . . ",
    "NoEcho": true
  },
  "KmsKeyId": {
    "Description": "The key ID of the AWS KMS key to be used for encryption",
    "Type": "String"
  },
  "LambdaEncryptionResourceArn": {
    "Description": "The ARN of the AWS Lambda function backing the encryption custom resource",
    "Type": "String"
  }
}

This is just a standard parameters section. It defines the following template parameters:

  • SuperSecretThing: This is some value that you want to hide, hence the use of the NoEcho option. However, as mentioned in the introductory article of this series, we also want this value to be available when describing the stack. Use your imagination, and let’s pretend it’s something like a password.

  • KmsKeyId: Remember waaaaay back in the previous post when I told you to make a note of the key you created in KMS? Hmmmm?

  • LambdaEncryptionResourceArn: This is the ARN of the Lambda function we previously created. At that time, I recommended taking note of the function’s ARN. You did that, right? Right!?

Resources

{
  "EncryptedSuperSecretThing": {
    "Type": "AWS::CloudFormation::CustomResource",
    "Version": "1.0",
    "Properties": {
      "ServiceToken": {"Ref": "LambdaEncryptionResourceArn"},
      "KeyId": {"Ref": "KmsKeyId"},
      "PlainText": {"Ref": "SuperSecretThing"}
    }
  }
}

This template is only meant for demonstrating usage so it only has a single resource. It’s a custom resource backed by our Lambda function from an earlier step. It is this resource that encrypts the value of the “SuperSecretThing” parameter and makes it available for use elsewhere in the template.

In addition to the required “ServiceToken” property that all AWS::CloudFormation::CustomResource types must have, the “KeyId” and “PlainText” properties are also supplied. This is what provides the KMS key id and the value to be encrypted to the Lambda function.

Outputs

{
  "EncryptedSuperSecretThing": {
    "Value": {"Fn::GetAtt": ["EncryptedSuperSecretThing", "CipherText"]},
    "Description": "KMS encrypted value of SuperSecretThing (Base64 encoded)"
  }
}

For ease of use, I’ve put the encrypted value directly in the outputs section, but it could just as easily be used anywhere else in the template (e.g. in another resource’s metadata, E2 user data, etc.). Specifically, the Lamba-backed custom resource supports a call to Fn::GetAtt that returns a “CipherText” attribute. This attribute contains the encrypted, Base64 encoded value that we have been seeking this whole time!

{"Fn::GetAtt": ["EncryptedSuperSecretThing", "CipherText"]}

That’s it! In this example, an API call that describes the stack will contain the encrypted value (available in the stack’s outputs), which can then be decrypted by any entity that has decrypt permissions for the KMS key that was used for encryption. All of the supporting pieces that were created in the previous steps can be reused as-is for any number of stacks and templates, and you have the option of mixing and matching different KMS keys depending on your needs.

Launch the template and give it a whirl!