Terraform: Document how to use a LAMBDA_PROXY

Created on 2 Dec 2016  ยท  10Comments  ยท  Source: hashicorp/terraform

I'm trying to setup a Slack 2 Lambda integration following the guide at https://aws.amazon.com/blogs/aws/new-slack-integration-blueprints-for-aws-lambda/.

Doing this by hand (following the slack-echo-command blueprint) showed that the Integration Request part became a LAMBDA_PROXY which TF don't seem to support (according to the documentation at https://www.terraform.io/docs/providers/aws/r/api_gateway_integration.html).

screenshot 2016-12-02 14 22 50

screenshot 2016-12-02 14 25 11

documentation provideaws

Most helpful comment

I came across this problem too, and found https://github.com/hashicorp/terraform/issues/10157 which solved it for me. I then came searching to see why the solution didn't make it into the documentation.

Unless I'm missing something.. the pull request referred to above (and the current live documentation) doesn't include the full solution: the aws_api_gateway_integration_response and aws_api_gateway_method_response resources are missing.

The confusion when putting this together appears to stem from the fact that the AWS web console says, for _Integration Response_: 'Proxy integrations cannot be configured to transform responses', but if all you add is a method response via Terraform, the console says under _Method Response_: 'Create an integration response'. So I assume, when setting up a Lambda proxy integration through the web console, that an integration response is still created in the background.

Following the solution at https://github.com/hashicorp/terraform/issues/10157#issuecomment-263560025 resolved the issue for me; a similar solution is also included in @FransUrbo's solution above but appears it didn't make it through to the documentation.

FYI @stack72 - would you like me to submit an additional pull request? I am not sure if I have misunderstood, hence my request for clarification. Thanks!

All 10 comments

This is as far as I've gotten with this in TF:

resource "aws_api_gateway_rest_api" "slack-commands" {
  name                        = "slack-commands"
  description                 = "API gateway for Lambda/Slack slash commands integration."
}

resource "aws_api_gateway_resource" "slack-commands" {
  rest_api_id                 = "${aws_api_gateway_rest_api.slack-commands.id}"
  parent_id                   = "${aws_api_gateway_rest_api.slack-commands.root_resource_id}"

  path_part                   = "slack"
}

resource "aws_api_gateway_method" "slack-commands" {
  rest_api_id                 = "${aws_api_gateway_rest_api.slack-commands.id}"
  resource_id                 = "${aws_api_gateway_resource.slack-commands.id}"

  http_method                 = "ANY"
  authorization               = "AWS_IAM"
}

resource "aws_api_gateway_method_response" "slack-commands-200" {
  rest_api_id                 = "${aws_api_gateway_rest_api.slack-commands.id}"
  resource_id                 = "${aws_api_gateway_resource.slack-commands.id}"
  http_method                 = "${aws_api_gateway_method.slack-commands.http_method}"

  status_code                 = "200"
}

resource "aws_api_gateway_deployment" "slack-commands" {
  depends_on                  = ["aws_api_gateway_method.slack-commands"]
  rest_api_id                 = "${aws_api_gateway_rest_api.slack-commands.id}"

  stage_name                  = "live"
}

resource "aws_api_gateway_integration" "slack-commands" {
  rest_api_id                 = "${aws_api_gateway_rest_api.slack-commands.id}"
  resource_id                 = "${aws_api_gateway_resource.slack-commands.id}"
  http_method                 = "${aws_api_gateway_method.slack-commands.http_method}"

#  type                        = "LAMBDA_PROXY"
  type                        = "MOCK"
}

resource "aws_api_gateway_base_path_mapping" "slash-commands" {
  api_id                       = "${aws_api_gateway_rest_api.slack-commands.id}"
  stage_name                   = "${aws_api_gateway_deployment.slack-commands.stage_name}"
  domain_name                  = "${aws_api_gateway_domain_name.slack-commands.domain_name}"
  base_path                    = "${aws_api_gateway_resource.slack-commands.path_part}"
}

resource "aws_api_gateway_domain_name" "slack-commands" {
  domain_name                 = "api.${var.dns_zone}"

  certificate_name            = "api.${var.dns_zone}"
  certificate_chain           = "${file("${path.module}/cacert.pem")}"
  certificate_body            = "${file("${path.module}/api.${var.dns_zone}.pub")}"
  certificate_private_key     = "${file("${path.module}/api.${var.dns_zone}.prv")}"
}

resource "aws_route53_record" "slack-commands" {
  zone_id                     = "${aws_route53_zone.main.id}"
  name                        = "${aws_api_gateway_domain_name.slack-commands.domain_name}"
  type                        = "A"
  alias {
    name                      = "${aws_api_gateway_domain_name.slack-commands.cloudfront_domain_name}"
    zone_id                   = "${aws_api_gateway_domain_name.slack-commands.cloudfront_zone_id}"
    evaluate_target_health    = true
  }
}

The problem that's remaining is the aws_api_gateway_integration.slack-commands resource..

Hi @FransUrbo,

I think you have the same problem than me and I finally got the way to do that, try this #10157

That worked for me as well, thanx!

Actually, I'm reopening this and modifying the Subject to indicate that it's a documentation issue.

For completeness, this is what my manifest looks like now:

The Lambda function setup

# https://aws.amazon.com/blogs/aws/new-slack-integration-blueprints-for-aws-lambda/
variable "asg_lambda_file-slack-slash" {
  description            = "File to use for the ASG Lambda function"
  default                = "./core/main/asg_lambda-slack-slash.zip"
}

resource "aws_lambda_function" "slack-slash-commands" {
  function_name          = "slack-slash-commands"
  description            = "A Slack slash commands integration function."

  filename               = "${var.asg_lambda_file-slack-slash}"
  source_code_hash       = "${base64sha256(file("${var.asg_lambda_file-slack-slash}"))}"
  handler                = "asg_lambda-slack-slash.handler"

  kms_key_arn            = "${aws_kms_key.lambda-slack.arn}"
  environment {
    variables = {
      kmsEncryptedToken  = "SecretHash"
    }
  }
  runtime                = "nodejs4.3"
  memory_size            = 128
  timeout                = 5

  role                   = "${aws_iam_role.ASGNotify.arn}"

  vpc_config {
    subnet_ids           = [
      "${aws_subnet.main_0.id}",
      "${aws_subnet.main_1.id}",
      "${aws_subnet.main_2.id}"
    ]

    security_group_ids     = [
      "${aws_security_group.main-all.id}",
      "${aws_default_security_group.main_default.id}"
    ]
  }
}

resource "aws_lambda_alias" "asg_event-slack-slash" {
    name                 = "asg_event-slack-slash"
    description          = "Autoscaling event to Slack"

    function_name        = "${aws_lambda_function.slack-slash-commands.arn}"
    function_version     = "$LATEST"
}

resource "aws_lambda_permission" "asg_event-slack-slash" {
    function_name        = "${aws_lambda_function.slack-slash-commands.arn}"

    statement_id         = "allow-api-gateway-parent-resource-get"
    action               = "lambda:InvokeFunction"
    principal            = "apigateway.amazonaws.com"

    source_arn           = "arn:aws:execute-api:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:${aws_api_gateway_rest_api.slack-commands.id}/*/${aws_api_gateway_method.slack-commands.http_method}${aws_api_gateway_resource.slack-commands.path}"
}

The API setup

resource "aws_api_gateway_rest_api" "slack-commands" {
  name                        = "slack-commands"
  description                 = "API gateway for Lambda/Slack slash commands integration."
}

resource "aws_api_gateway_resource" "slack-commands" {
  rest_api_id                 = "${aws_api_gateway_rest_api.slack-commands.id}"
  parent_id                   = "${aws_api_gateway_rest_api.slack-commands.root_resource_id}"

  path_part                   = "slack"
}

resource "aws_api_gateway_method" "slack-commands" {
  rest_api_id                 = "${aws_api_gateway_rest_api.slack-commands.id}"
  resource_id                 = "${aws_api_gateway_resource.slack-commands.id}"

  http_method                 = "ANY"
  authorization               = "AWS_IAM"
}

resource "aws_api_gateway_method_response" "slack-commands-200" {
  rest_api_id                 = "${aws_api_gateway_rest_api.slack-commands.id}"
  resource_id                 = "${aws_api_gateway_resource.slack-commands.id}"
  http_method                 = "${aws_api_gateway_method.slack-commands.http_method}"

  status_code                 = "200"
}

resource "aws_api_gateway_deployment" "slack-commands" {
  depends_on                  = ["aws_api_gateway_method.slack-commands"]
  rest_api_id                 = "${aws_api_gateway_rest_api.slack-commands.id}"

  stage_name                  = "live"
}

resource "aws_api_gateway_integration" "slack-commands" {
  rest_api_id                 = "${aws_api_gateway_rest_api.slack-commands.id}"
  resource_id                 = "${aws_api_gateway_resource.slack-commands.id}"
  http_method                 = "${aws_api_gateway_method.slack-commands.http_method}"

  type                        = "AWS_PROXY"
  uri                         = "arn:aws:apigateway:${data.aws_region.current.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.slack-slash-commands.arn}/invocations"
  integration_http_method     = "POST"
}

resource "aws_api_gateway_integration_response" "slack-commands" {
  rest_api_id                 = "${aws_api_gateway_rest_api.slack-commands.id}"
  resource_id                 = "${aws_api_gateway_resource.slack-commands.id}"
  http_method                 = "${aws_api_gateway_method.slack-commands.http_method}"

  status_code                 = "${aws_api_gateway_method_response.slack-commands-200.status_code}"

  response_templates          = {
    "application/json"        = ""
  }
}

resource "aws_api_gateway_base_path_mapping" "slash-commands" {
  api_id                       = "${aws_api_gateway_rest_api.slack-commands.id}"
  stage_name                   = "${aws_api_gateway_deployment.slack-commands.stage_name}"
  domain_name                  = "${aws_api_gateway_domain_name.slack-commands.domain_name}"
  base_path                    = "${aws_api_gateway_resource.slack-commands.path_part}"
}

resource "aws_api_gateway_domain_name" "slack-commands" {
  domain_name                 = "api.${var.dns_zone}"

  certificate_name            = "api.${var.dns_zone}"
  certificate_chain           = "${file("${path.module}/cacert.pem")}"
  certificate_body            = "${file("${path.module}/api.${var.dns_zone}.pub")}"
  certificate_private_key     = "${file("${path.module}/api.${var.dns_zone}.prv")}"
}

resource "aws_route53_record" "slack-commands" {
  zone_id                     = "${aws_route53_zone.main.id}"
  name                        = "${aws_api_gateway_domain_name.slack-commands.domain_name}"
  type                        = "A"
  alias {
    name                      = "${aws_api_gateway_domain_name.slack-commands.cloudfront_domain_name}"
    zone_id                   = "${aws_api_gateway_domain_name.slack-commands.cloudfront_zone_id}"
    evaluate_target_health    = true
  }
}

I don't know how to create the "API Gateway > Lambda" trigger though..

Made https://github.com/hashicorp/terraform/pull/10772 in order to improve the documentation, tell me if it's better or not!

Thanks!

Closed via #10772 - thanks @Ninir - @FransUrbo please let us know if this isn't the case

I came across this problem too, and found https://github.com/hashicorp/terraform/issues/10157 which solved it for me. I then came searching to see why the solution didn't make it into the documentation.

Unless I'm missing something.. the pull request referred to above (and the current live documentation) doesn't include the full solution: the aws_api_gateway_integration_response and aws_api_gateway_method_response resources are missing.

The confusion when putting this together appears to stem from the fact that the AWS web console says, for _Integration Response_: 'Proxy integrations cannot be configured to transform responses', but if all you add is a method response via Terraform, the console says under _Method Response_: 'Create an integration response'. So I assume, when setting up a Lambda proxy integration through the web console, that an integration response is still created in the background.

Following the solution at https://github.com/hashicorp/terraform/issues/10157#issuecomment-263560025 resolved the issue for me; a similar solution is also included in @FransUrbo's solution above but appears it didn't make it through to the documentation.

FYI @stack72 - would you like me to submit an additional pull request? I am not sure if I have misunderstood, hence my request for clarification. Thanks!

I'm going to lock this issue because it has been closed for _30 days_ โณ. This helps our maintainers find and focus on the active issues.

If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

Was this page helpful?
0 / 5 - 0 ratings