diff --git a/CHANGELOG.md b/CHANGELOG.md index b155594..df7eff0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## v1.1.0 - [Add layers attribute](https://github.com/babbel/terraform-aws-lambda-with-inline-code/pull/7) +- [Add secret environment variables attribute](https://github.com/babbel/terraform-aws-lambda-with-inline-code/pull/8) ## v1.0.0 diff --git a/main.tf b/main.tf index 85581d6..cb9a24c 100644 --- a/main.tf +++ b/main.tf @@ -13,7 +13,7 @@ resource "aws_lambda_function" "this" { 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 @@ -25,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" { @@ -80,3 +83,58 @@ data "aws_iam_policy_document" "cloudwatch-log-group" { resources = ["${aws_cloudwatch_log_group.this.arn}:*"] } } + + +# Secret environment variables + +locals { + environment_variables = merge(var.environment_variables, local.secret_environment_variables) + + secret_environment_variables = { + for k, v in var.secret_environment_variables : join("", [k, "_SECRET_ARN"]) => v + } +} + +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 +# it 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 05e48cf..6deb4a6 100644 --- a/variables.tf +++ b/variables.tf @@ -65,6 +65,19 @@ 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 = <