AWS Lambda to AWS Systems Manager Parameter Store

Sign Up to Build

About this Architecture

Here is some information about this architecture.

How to Build This Solution

Here are the steps you can follow to build this solution on your own.

This project involves integrating AWS Lambda with the Systems Manager Parameter Store. You'll learn how to securely manage, retrieve, and use configuration data within your Lambda functions. This collaboration offers a structured way to manage and control sensitive information like database passwords or API keys.

Get Your AWS Credentials

If you're using the Skillmix Labs feature, open the lab settings (the beaker icon) on the right side of the code editor. Then, click the Start Lab button to start hte lab environment.

Wait for the credentials to load. Then run this in the terminal.

Be sure to enter in your own access key and secret key and name your profile 'smx-lab'.

$ aws configure --profile smx-lab
AWS Access Key ID [None]: 
AWS Secret Access Key [None]: 
Default region name [None]: us-west-2
Default output format [None]: 

Note: If you're using your own AWS account you'll need to ensure that you've created and configured a named AWS CLI profile named smx-lab.

Create the Terraform Config

Next, we'll create the terraform config. This config is used to specify the providers that are required for our Terraform configuration. In this case, we are specifying that we require the aws provider from HashiCorp with a version constraint of ~> 4.22.

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.22"
    }
  }
  required_version = ">= 0.14.9"
}

Create the aws Config

Next, we'll create the aws config which is used to configure the AWS provider in Terraform. This config specifies the AWS profile to use, which is 'smx-lab', and the region to operate in, which is 'us-west-2'.

provider "aws" {
  profile = "smx-lab"
  region  = "us-west-2"
}

Create the ssm_parameter_name Config

Next, we'll create the ssm_parameter_name config. This config is used to define a variable named ssm_parameter_name with a type of string and a default value of ExampleParameterName.

variable {
  ssm_parameter_name = {
    type    = "${string}"
    default = "ExampleParameterName"
  }
}

Create the aws_caller_identity Config

Next, we'll create the aws_caller_identity config. This config is used to retrieve information about the AWS caller identity, such as the AWS account ID and the ARN of the IAM user or role making the request.

data "aws_caller_identity" "current" {
  # No configuration required
}

Create the aws_region Config

Next, we'll create the aws_region config. This config is used to retrieve the current AWS region.

data "aws_region" "current" {
}

Create the aws_partition Config

Next, we'll create the aws_partition config. This config is used to retrieve information about the current AWS partition.

data "aws_partition" "current" {
}

Create the archive_file Config

Next, we'll create the archive_file config. This config is used to create a zip file from the source file app.js located in the current module's directory. The resulting zip file will be named lambda.zip and will also be stored in the current module's directory.

data "archive_file" "lambda_zip_file" {
  type        = "zip"
  source_file = "${path.module}/src/app.js"
  output_path = "${path.module}/lambda.zip"
}

Create the aws_iam_policy Config

Next, we'll create the aws_iam_policy config. This config is used to define an IAM policy for the lambda_basic_execution_role_policy with the name AWSLambdaBasicExecutionRole.

data "aws_iam_policy" "lambda_basic_execution_role_policy" {
  name = "AWSLambdaBasicExecutionRole"
}

Create the aws_iam_policy_document Config

Next, we'll create the aws_iam_policy_document config. This config is used to define an IAM policy document for AWS Lambda.

data "aws_iam_policy_document" "lambda_policy_document" {
  statement {
    effect   = "Allow"
    actions  = ["ssm:GetParameter", "ssm:PutParameter"]
    resources = ["${aws_ssm_parameter.ssm_parameter.arn}"]
  }
}

Create the aws_lambda_function Config

Next, we'll create the aws_lambda_function config. This config is used to define an AWS Lambda function.

resource "aws_lambda_function" "lambda_function" {
  function_name    = "SSMParameterFunction"
  filename         = "${data.archive_file.lambda_zip_file.output_path}"
  source_code_hash = "${data.archive_file.lambda_zip_file.output_base64sha256}"
  handler          = "app.handler"
  role             = "${aws_iam_role.lambda_iam_role.arn}"
  runtime          = "nodejs14.x"

  environment {
    variables = {
      SSMParameterName = "${var.ssm_parameter_name}"
    }
  }
}

Create the aws_iam_role Config

Next, we'll create the aws_iam_role config. This config is used to create an AWS IAM role for a Lambda function. The role will have a name prefix of 'LambdaSSMParameterRole-' and will be associated with two managed policies: 'lambda_basic_execution_role_policy' and 'lambda_policy'. The role will also have an assume role policy that allows the Lambda service to assume this role.

resource "aws_iam_role" "lambda_iam_role" {
  name_prefix   = "LambdaSSMParameterRole-"
  managed_policy_arns = [
    "${data.aws_iam_policy.lambda_basic_execution_role_policy.arn}",
    "${aws_iam_policy.lambda_policy.arn}"
  ]
  assume_role_policy = <<EOF
  {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Action": "sts:AssumeRole",
        "Principal": {
          "Service": "lambda.amazonaws.com"
        },
        "Effect": "Allow",
        "Sid": ""
      }
    ]
  }
  EOF
}

Create the aws_iam_policy Config

Next, we'll create the aws_iam_policy config. This config is used to define an IAM policy in AWS.

resource "aws_iam_policy" "lambda_policy" {
  name_prefix = "lambda_policy-"
  path        = "/"
  policy      = "${data.aws_iam_policy_document.lambda_policy_document.json}"
  lifecycle {
    create_before_destroy = true
  }
}

Create the aws_ssm_parameter Config

Next, we'll create the aws_ssm_parameter config. This config is used to create an AWS Systems Manager Parameter Store parameter.

resource "aws_ssm_parameter" "ssm_parameter" {
  name  = "${var.ssm_parameter_name}"
  type  = "String"
  value = "{\"key1\":\"value1\"}"
}

Create the LambdaFunctionName Config

Next, we'll create the LambdaFunctionName config. This config is used to output the name of the Lambda function.

output "LambdaFunctionName" {
  value       = aws_lambda_function.lambda_function.arn
  description = "Lambda function name."
}

Deploy the Solution

Let's deploy this thing! If you haven't done so, start the Skillmix lab session and get the account credentials. Configure your Terraform environment to use those credentials.

Then, open a terminal or command prompt, navigate to the folder with your Terraform file, and execute these commands:

# initiatlize the project 
$ terraform init 

# show the plan 
$ terraform plan 

# apply the changes 
$ terraform apply

Wait for the changes to be applied before proceeding.

How it works

This pattern creates an AWS Lambda function and an AWS Systems Manager Parameter Store parameter. The parameter is added as a function environment variable named "SSMParameterName". The function execution role only allows Get and Put actions on the parameter. The function supports a JSON event in the following format:

{
  "method": "{GET || PUT}",
  "body": "{NewParameterValue}"
}

The method can be either GET (retrieve current parameter value) or PUT (update parameter with new value). The body of the event is used to define the new value of the parameter.

The function code can easily be modified to support an Amazon API Gateway API event by changing the format for method:

  1. API v1 event: event.httpMethod

  2. API v2 event: event.requestContext.http.method

Testing

Once the application is deployed, navigate to the Lambda function and configure GET and PUT test events. Invoke the function using each test event. Review the Amazon CloudWatch Logs for details on the function invocation. Navigate to the AWS Systems Manager Paramater Store to observe changes to the parameter value after a PUT event.

Example GET test event:

{
  "method": "GET",
  "body": ""
}

Response:

{
  "statusCode": 200,
  "body": "{\"Parameter\":{\"Name\":\"ExampleParameterName\",\"Type\":\"String\",\"Value\":\"{\\\"key1\\\":\\\"value1\\\"}\",\"Version\":5,\"LastModifiedDate\":\"2021-04-02T13:46:55.828Z\",\"ARN\":\"arn:aws:ssm:us-east-2:{AwsAccount}:parameter/ExampleParameterName\",\"DataType\":\"text\"}}"
}

Example PUT test event:

{
  "method": "PUT",
  "body": "{\"key1\":\"value1\", \"key2\":\"value2\"}"
}

Response:

{
  "statusCode": 200,
  "body": "{\"Version\":6,\"Tier\":\"Standard\"}"
}

Source: https://github.com/aws-samples/serverless-patterns/tree/main/lambda-ssm-parameter-terraform