terraform-aws-modules / terraform-aws-apigateway-v2

Terraform module to create AWS API Gateway v2 (HTTP/WebSocket) 🇺🇦
https://registry.terraform.io/modules/terraform-aws-modules/apigateway-v2/aws
Apache License 2.0
144 stars 188 forks source link

Example `complete-http` doesn't work #5

Closed embaya closed 3 years ago

embaya commented 3 years ago

Hello I used the complete-http example to create an api gatway to trigger my lambda. I copied and pasted the same code, it creates the lambda and the apigatway, but the lambda has no layer, so the lambda is not accessible. So I have to link it via the console using the following command:

aws lambda add-permission \ 
 --statement-id 5282ce2a-89af-58f2-nv456-a2a98f5bfe92 \  
 --action lambda:InvokeFunction \  
 --function-name "arn:aws:lambda:eu-west-3:************:function:ample-mink-lambda" \  
 --principal apigateway.amazonaws.com \  
 --source-arn "arn:aws:execute-api:eu-west-3:************:qvtfwsdifj/*/*/"

At first glance, I have the impression that the order in which the resources are created is not good. Can you help me please, below my code:

###################
# HTTP API Gateway
###################

module "api_gateway" {
  source = "terraform-aws-modules/apigateway-v2/aws"

  name          = "${random_pet.this.id}-http"
  description   = "My awesome HTTP API Gateway"
  protocol_type = "HTTP"

  cors_configuration = {
    allow_headers = ["content-type", "x-amz-date", "authorization", "x-api-key", "x-amz-security-token", "x-amz-user-agent"]
    allow_methods = ["*"]
    allow_origins = ["*"]
  }

  domain_name                 = var.http_01_domain_name
  domain_name_certificate_arn = data.aws_acm_certificate.certificate.arn

  default_stage_access_log_destination_arn = aws_cloudwatch_log_group.logs.arn
  default_stage_access_log_format          = "$context.identity.sourceIp - - [$context.requestTime] \"$context.httpMethod $context.routeKey $context.protocol\" $context.status $context.responseLength $context.requestId $context.integrationErrorMessage"

  integrations = {
    "ANY /" = {
      lambda_arn             = module.lambda_function.this_lambda_function_arn
      payload_format_version = "2.0"
      timeout_milliseconds   = 12000
    }

    "$default" = {
      lambda_arn = module.lambda_function.this_lambda_function_arn
    }

  }

  tags = {
    Name = "dev-api-new"
  }
}
data "aws_acm_certificate" "certificate" {
  domain = var.https_certificate_domain_name
}

# Domain name in front of API Gateway
# In a real project, this might go to another layer (or to sysadmin/terraform/90_dns)

data "aws_route53_zone" "this" {
  name = var.dns_zone_name
}

##########
# Route53
##########

resource "aws_route53_record" "api" {
  zone_id = data.aws_route53_zone.this.zone_id
  name    = var.http_01_domain_name
  type    = "A"

  alias {
    name                   = module.api_gateway.this_apigatewayv2_domain_name_configuration.0.target_domain_name
    zone_id                = module.api_gateway.this_apigatewayv2_domain_name_configuration.0.hosted_zone_id
    evaluate_target_health = false
  }
}

##################
# Extra resources
##################

resource "random_pet" "this" {
  length = 2
}

resource "aws_cloudwatch_log_group" "logs" {
  name = random_pet.this.id
}

#############################################
# Using packaged function from Lambda module
#############################################

locals {
  package_url = "https://raw.githubusercontent.com/terraform-aws-modules/terraform-aws-lambda/master/examples/fixtures/python3.8-zip/existing_package.zip"
  downloaded  = "downloaded_package_${md5(local.package_url)}.zip"
}

resource "null_resource" "download_package" {
  triggers = {
    downloaded = local.downloaded
  }

  provisioner "local-exec" {
    command = "curl -L -o ${local.downloaded} ${local.package_url}"
  }
}

data "null_data_source" "downloaded_package" {
  inputs = {
    id       = null_resource.download_package.id
    filename = local.downloaded
  }
}

module "lambda_function" {
  source  = "terraform-aws-modules/lambda/aws"
  version = "~> 1.0"

  function_name = "${random_pet.this.id}-lambda"
  description   = "My awesome lambda function"
  handler       = "index.lambda_handler"
  runtime       = "python3.8"

  publish = true

  create_package         = false
  local_existing_package = data.null_data_source.downloaded_package.outputs["filename"]

  allowed_triggers = {
    AllowExecutionFromAPIGateway = {
      service = "apigateway"
      arn     = module.api_gateway.this_apigatewayv2_api_execution_arn
    }
  }
}
antonbabenko commented 3 years ago

Lambda layers are optional and can be used inside of Lambda functions.

Update arn inside of allowed_triggers to match --source-arn "arn:aws:execute-api:eu-west-3:************:qvtfwsdifj/*/*/" and it will create the same permission as you get when running AWS CLI command.

See this for example.

embaya commented 3 years ago

Thank you for your answer, but even with your proposal it still doesn't work. here's what I did:

  allowed_triggers = {
    AllowExecutionFromAPIGateway = {
      service    = "apigateway"
      source_arn = "${module.api_gateway.this_apigatewayv2_api_execution_arn}/*/*"
    }
  }

After an application, looking in the aws console I got this.

{
  "Version": "2012-10-17",
  "Id": "default",
  "Statement": [
    {
      "Sid": "AllowExecutionFromAPIGateway",
      "Effect": "Allow",
      "Principal": {
        "Service": "apigateway.amazonaws.com"
      },
      "Action": "lambda:InvokeFunction",
      "Resource": "arn:aws:lambda:eu-west-3:123456789012:function:more-pigeon-lambda",
      "Condition": {
        "ArnLike": {
          "AWS:SourceArn": "arn:aws:execute-api:eu-west-3::il2yc9qa4g/*/*"
        }
      }
    }
  ]
}

I tried to tinker a bit to format the policy, and finally it worked but adding the account id (123456789012) like this:

- source_arn = "${module.api_gateway.this_apigatewayv2_api_execution_arn}/*/*"
+ source_arn = "arn:aws:execute-api:eu-west-3:123456789012:${module.api_gateway.this_apigatewayv2_api_id}/*/*/"

is it a bug or am I the one who's crashing somewhere?

antonbabenko commented 3 years ago

Make sure that your provider block does not have skip_requesting_account_id = true as explained here - https://github.com/antonbabenko/serverless.tf-playground/blob/master/terraform/main.tf#L10-L11

github-actions[bot] commented 1 year ago

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.