From 84da09a6fb97a599f35c59ef4c71dd40a134cc20 Mon Sep 17 00:00:00 2001 From: jtsaito Date: Tue, 21 Sep 2021 16:33:04 +0200 Subject: [PATCH 1/2] Add support for secret environment variables --- main.tf | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++-- variables.tf | 21 ++++++++++++++ 2 files changed, 97 insertions(+), 2 deletions(-) diff --git a/main.tf b/main.tf index 44127b3..5cbf8ed 100644 --- a/main.tf +++ b/main.tf @@ -8,10 +8,12 @@ resource "aws_lambda_function" "this" { timeout = var.timeout reserved_concurrent_executions = var.reserved_concurrent_executions + layers = local.secrets_wrapper_layers + role = aws_iam_role.this.arn dynamic "environment" { - for_each = var.environment_variables != null ? [{ variables = var.environment_variables }] : [] + for_each = local.environment_variables != null ? [{ variables = local.environment_variables }] : [] content { variables = environment.value.variables @@ -23,7 +25,10 @@ resource "aws_lambda_function" "this" { tags = var.tags - depends_on = [aws_cloudwatch_log_group.this] + depends_on = [ + aws_cloudwatch_log_group.this, + null_resource.watch_iam_role_policy_secretsmanager_get_secret_value, + ] } data "archive_file" "this" { @@ -78,3 +83,72 @@ data "aws_iam_policy_document" "cloudwatch-log-group" { resources = ["${aws_cloudwatch_log_group.this.arn}:*"] } } + + +# Secret environment variables + +locals { + environment_variables = merge(local.lambda_exec_wrapper, var.environment_variables, local.secret_environment_variables) + + lambda_exec_wrapper = length(var.secret_environment_variables) == 0 ? {} : { + AWS_LAMBDA_EXEC_WRAPPER = "/opt/main" + } + + secret_environment_variables = { + for k, v in var.secret_environment_variables : join("", [k, "_SECRET_ARN"]) => v + } + + secrets_wrapper_layers = length(var.secret_environment_variables) <= 0 ? [] : [ + # sets the number suffix of lambda-layer version secrets.wrapper.lambda-layer. + # cf. https://github.com/lessonnine/secrets.wrapper.lambda-layer#readme + "arn:aws:lambda:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:layer:secrets_wrapper:${var.secrets_wrapper_lambda_layer_version_number}" + ] +} + +data "aws_caller_identity" "current" {} + +data "aws_region" "current" {} + +resource "aws_iam_role_policy" "secretsmanager-get-secret-value" { + count = length(var.secret_environment_variables) > 0 ? 1 : 0 + + name = "secretsmanager-get-secret-value-${md5(data.aws_iam_policy_document.secretsmanager-get-secret-value[count.index].json)}" + role = aws_iam_role.this.name + policy = data.aws_iam_policy_document.secretsmanager-get-secret-value[count.index].json + + lifecycle { + create_before_destroy = true + } +} + +# Whenever a change is made to the secrets, the role policy must be updated +# first, then we need to wait for a few seconds and only then update the lambda function itself. +# Waiting is necessary because otherwise the during initialization of the lambda function the +# secrets-fetcher wrapper will not yet have the permissions for fetching the secret values. +resource "null_resource" "watch_iam_role_policy_secretsmanager_get_secret_value" { + count = length(var.secret_environment_variables) > 0 ? 1 : 0 + + # null_resource is replaced every time the policy changes + triggers = { + secretsmanager_get_secret_value_policy = aws_iam_role_policy.secretsmanager-get-secret-value[count.index].policy + } + + provisioner "local-exec" { + command = "sleep 15" + } +} + +data "aws_iam_policy_document" "secretsmanager-get-secret-value" { + count = length(var.secret_environment_variables) > 0 ? 1 : 0 + + statement { + actions = ["secretsmanager:GetSecretValue"] + + # select secret arns and cut off trailing json keys + # https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data-secrets.html + resources = [ + for k, extended_secret_arn in local.secret_environment_variables : + join(":", slice(split(":", extended_secret_arn), 0, 7)) if length(regexall("_SECRET_ARN$", k)) == 1 + ] + } +} diff --git a/variables.tf b/variables.tf index 97d886c..f0c8e27 100644 --- a/variables.tf +++ b/variables.tf @@ -58,6 +58,27 @@ variable "runtime" { description = "The identifier of the Lambda function [runtime](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html)." } +variable "secret_environment_variables" { + type = map(string) + default = {} + + description = < Date: Tue, 21 Sep 2021 16:42:52 +0200 Subject: [PATCH 2/2] Update change log --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7596960..68ba893 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,3 +3,7 @@ ## v1.0.0 - [Initial version](https://github.com/babbel/terraform-aws-lambda-with-inline-code/pull/1) + +## v1.1.0 + +- [Added support for secret environment variables](https://github.com/babbel/terraform-aws-lambda-with-inline-code/pull/6)