Search code examples
terraformterraform-provider-awsterraform-template-file

Unable to pass variable from .tf file to .json policy template


I'm a newbe to terraform world and experiencing some tough time around passing variables from .tf file to .json My sample tf lambda function is as follows

data "template_file" "task" {
  template = file("./iam/grange_rest_dynlambda_policy.json")
  vars = {
    resource="${var.stage}_grange_dynamodb"
  }
}

resource "aws_lambda_function" "grange_rest_dynlambda" {
  function_name                  = "${var.stage}_grange_rest_dynlambda"
  handler                        = "lambda/src/index.handler"
  memory_size                    = "256"
  timeout                        = 10
  reserved_concurrent_executions = "-1"
  filename                       = "${path.module}/../dist/lambda.zip"
  role    = aws_iam_role.grange_rest_dynlambda_iam_role.arn
  runtime = "nodejs14.x"
  publish = true
}

resource "aws_lambda_alias" "grange_rest_dynlambda_alias" {
  depends_on       = ["aws_lambda_function.grange_rest_dynlambda"]
  name             = var.stage
  description      = var.stage
  function_name    = aws_lambda_function.grange_rest_dynlambda.arn
  function_version = aws_lambda_function.grange_rest_dynlambda.version
}

// Enable cloudwatch for lambda
resource "aws_cloudwatch_log_group" "example" {
  name              = "/aws/lambda/${var.stage}_grange_rest_dynlambda"
  retention_in_days = 14
}

# See also the following AWS managed policy: AWSLambdaBasicExecutionRole
resource "aws_iam_policy" "lambda_logging" {
  name        = "lambda_logging"
  path        = "/"
  description = "IAM policy for logging from a lambda"
  policy = file("./iam/grange_rest_dynlambda_logging_policy.json")
}

// Lambda + DynamoDB
resource "aws_iam_role" "grange_rest_dynlambda_iam_role" {
  name               = "grange_rest_dynlambda_iam_role"
  assume_role_policy = file("./iam/grange_rest_dynlambda_assume_policy.json")
}

resource "aws_iam_role_policy" "grange_rest_dynlambda_iam_policy" {
  policy = file("./iam/grange_rest_dynlambda_policy.json")
  role   = aws_iam_role.grange_rest_dynlambda_iam_role.id
}

resource "aws_iam_role_policy_attachment" "lambda_logs" {
  role       = aws_iam_role.grange_rest_dynlambda_iam_role.name
  policy_arn = aws_iam_policy.lambda_logging.arn
}

// API Gateway + Lambda
resource "aws_api_gateway_resource" "grange_rest_dynlambda_api" {
  parent_id   = aws_api_gateway_rest_api.grange_rest_api_gateway.root_resource_id
  path_part   = "grange_rest_dynlambda_api"
  rest_api_id = aws_api_gateway_rest_api.grange_rest_api_gateway.id
}

resource "aws_api_gateway_method" "grange_rest_dynlambda_api_get" {
  authorization = "NONE"
  http_method   = "GET"
  resource_id   = aws_api_gateway_resource.grange_rest_dynlambda_api.id
  rest_api_id   = aws_api_gateway_rest_api.grange_rest_api_gateway.id
}

resource "aws_api_gateway_method" "grange_rest_dynlambda_api_post" {
  authorization = "NONE"
  http_method   = "POST"
  resource_id   = aws_api_gateway_resource.grange_rest_dynlambda_api.id
  rest_api_id   = aws_api_gateway_rest_api.grange_rest_api_gateway.id
}

resource "aws_lambda_permission" "apigw" {
  action        = "lambda:InvokeFunction"
  statement_id  = "AllowExecutionFromAPIGateway"
  function_name = aws_lambda_function.grange_rest_dynlambda.function_name
  principal     = "apigateway.amazonaws.com"
  source_arn    = "${aws_api_gateway_rest_api.grange_rest_api_gateway.execution_arn}/*/*"
}

output "base_url" {
  value = aws_api_gateway_deployment.apigwdeployment.invoke_url
}

I inject policy from a JSON file and expect "resource" variable to be passed into JSON. But, that's not how it works

{
  "Version": "2012-10-17",
  "Statement":[{
    "Effect": "Allow",
    "Action": [
      "dynamodb:BatchGetItem",
      "dynamodb:GetItem",
      "dynamodb:Query",
      "dynamodb:Scan",
      "dynamodb:BatchWriteItem",
      "dynamodb:PutItem",
      "dynamodb:UpdateItem"
    ],
    "Resource": "arn:aws:dynamodb:us-east-2:741573820784:table/${resource}"
  }
  ]
}

What am I missing?


Solution

  • The template_file data source does not replace the variables in the actual file. It just reads the file and provides the "rendered" output directly to your Terraform.

    Therefore, you need to change your Terraform where you want to consume the "rendered" output:

    Before:

    resource "aws_iam_role_policy" "grange_rest_dynlambda_iam_policy" {
      policy = file("./iam/grange_rest_dynlambda_policy.json")
      role   = aws_iam_role.grange_rest_dynlambda_iam_role.id
    }
    

    After:

    resource "aws_iam_role_policy" "grange_rest_dynlambda_iam_policy" {
      policy = data.template_file.task.rendered
      role   = aws_iam_role.grange_rest_dynlambda_iam_role.id
    }
    

    You need to access the rendered property of the template_file data source:

    data.template_file.task.rendered
    

    This will replace ${resource} with the value of "${var.stage}_grange_dynamodb".

    Please note, that the documentation recommends to use the templatefile function instead of this data source.