【问题标题】:Terraform: Dynamically generate REST API endpoints in AWS API Gateway based on user inputTerraform:根据用户输入在 AWS API Gateway 中动态生成 REST API 端点
【发布时间】:2021-07-29 21:16:49
【问题描述】:

我正在构建一个 Terraform 模块,该模块在 AWS API Gateway 中部署一个 REST API。该模块的用户将提供如下输入:

api_resources = {
    resource1 = {
        api_endpoint = "/pets/{petID}"
        http_method = "GET"
    },
    resource2 = {
        api_endpoint = "/pets"
        http_method = "GET"
    },
    resource3 = {
        api_endpoint = "/toys"
        http_method = "GET"
    },
    resource4 = {
        api_endpoint = "/pets"
        http_method = "POST"
    }
}


在我的模块中,此输入将使用aws_api_gateway_resource Terraform 资源进行部署。它采用以下参数:

resource "aws_api_gateway_resource" "resource" {
  rest_api_id = # ID of the parent REST API resource.
  parent_id   = # ID of the immediate parent of this "part" of the API endpoint.
  path_part   = # The rightmost "part" of the endpoint URL.
}

官方文档:Link.


示例:对于输入 /pets/{petID},上面的 path_part 将是 {petID}parent_id 将是创建 pets path_part 的 Terraform 资源的 ID。

所以是这样的:

resource "aws_api_gateway_resource" "pets_resource" {
  rest_api_id = aws_api_gateway_rest_api.rest_api.id
  parent_id   = aws_api_gateway_rest_api.rest_api.root_resource_id
  path_part   = "pets"
}

resource "aws_api_gateway_resource" "petID_resource" {
  rest_api_id = aws_api_gateway_rest_api.rest_api.id
  parent_id   = aws_api_gateway_resource.pets_resource.id
  path_part   = "{petID}"
}

注意:aws_api_gateway_rest_api 已经存在于别处:

resource "aws_api_gateway_rest_api" "rest_api" {
  name = "my-api"
}

为了根据用户输入动态完成所有这些,我有:

  • 从输入中提取所有 API 端点。
  • 遍历它们并为每个资源创建一个aws_api_gateway_resource

像这样:

locals {
  api_endpoints = toset([
    for key, value in var.api_resources :
    trimprefix(value.api_endpoint, "/")
  ])
}

resource "aws_api_gateway_resource" "resource" {
  rest_api_id = aws_api_gateway_rest_api.rest_api.id
  parent_id   = aws_api_gateway_rest_api.rest_api.root_resource_id
  for_each    = local.api_endpoints # pets/{petID}, pets, toys
  path_part   = each.key
}

这适用于顶级资源/pets/toys,如此 Terraform 计划所示:

Terraform will perform the following actions:

  # aws_api_gateway_resource.resource["pets"] will be created
  + resource "aws_api_gateway_resource" "resource" {
      + id          = (known after apply)
      + parent_id   = "e79wlf30x5"
      + path        = (known after apply)
      + path_part   = "pets"
      + rest_api_id = "yrpm6dx4z8"
    }

  # aws_api_gateway_resource.resource["pets/{petID}"] will be created
  + resource "aws_api_gateway_resource" "resource" {
      + id          = (known after apply)
      + parent_id   = "e79wlf30x5"
      + path        = (known after apply)
      + path_part   = "pets/{petID}"
      + rest_api_id = "yrpm6dx4z8"
    }

  # aws_api_gateway_resource.resource["toys"] will be created
  + resource "aws_api_gateway_resource" "resource" {
      + id          = (known after apply)
      + parent_id   = "e79wlf30x5"
      + path        = (known after apply)
      + path_part   = "toys"
      + rest_api_id = "yrpm6dx4z8"
    }

Plan: 3 to add, 0 to change, 0 to destroy.

如何使它适用于像/pets/{petID} 这样的嵌套资源? 在上述计划中创建/pets/{petID} 资源将失败!挑战在于为嵌套资源的aws_api_gateway_resource 设置正确的parent_id。这需要适用于任何级别的嵌套。


注意:有一个数据源可以像这样返回任意 URL 路径的 ID:

data "aws_api_gateway_resource" "pets_resource" {
  rest_api_id = aws_api_gateway_rest_api.rest_api.id
  path        = "/pets"
}

我只是不知道如何把它们放在一起!

【问题讨论】:

    标签: amazon-web-services terraform aws-api-gateway


    【解决方案1】:

    我最终更改了输入格式以使事情变得更容易。最终结果如下:

    用户输入:

    api_endpoints = {
        "/" = { get = "lambda1" }
        "/pets" = {
            get = "lambda2"
            post = "lambda1"
        }
        "/pets/{petID}" = { get = "lambda3" }
        "/toys" = { get = "lambda3" }
    }
    
    lambda_functions = {
        lambda1 = {
            runtime = "nodejs14.x"
            handler = "index.handler"
            zip = "../lambda1.zip"
        }
        lambda2 = {
            runtime = "nodejs14.x"
            handler = "index.handler"
            zip = "../lambda2.zip"
        }
        lambda3 = {
            runtime = "python3.7"
            handler = "index.handler"
            zip = "../lambda3.zip"
        }
    }
    

    我的 Terraform 模块中使用此用户输入的代码如下:

    REST API:

     locals {
      openAPI_spec = {
        for endpoint, spec in var.api_endpoints : endpoint => {
          for method, lambda in spec : method => {
            x-amazon-apigateway-integration = {
              type       = "aws_proxy"
              httpMethod = "POST"
              uri        = "arn:aws:apigateway:${data.aws_region.region.name}:lambda:path/2015-03-31/functions/arn:aws:lambda:${data.aws_region.region.name}:${data.aws_caller_identity.identity.account_id}:function:${lambda}/invocations"
            }
          }
        }
      }
    }
    
    resource "aws_api_gateway_rest_api" "rest_api" {
      name = var.api_name
      endpoint_configuration {
        types = ["REGIONAL"]
      }
      body = jsonencode({
        openapi = "3.0.1"
        paths   = local.openAPI_spec
      })
    }
    

    Lambda 函数:

     module "lambda_function" {
      source                                  = "terraform-aws-modules/lambda/aws"
      for_each                                = var.lambda_functions
      function_name                           = each.key
      runtime                                 = each.value.runtime
      handler                                 = each.value.handler
      create_package                          = false
      local_existing_package                  = each.value.zip
      create_current_version_allowed_triggers = false
      allowed_triggers = {
        api-gateway = {
          service    = "apigateway"
          source_arn = "${aws_api_gateway_rest_api.rest_api.execution_arn}/*/*/*"
        }
      }
    }
    

    更多详情请见my GitHub repo

    【讨论】:

    • 这绝对是金子!
    猜你喜欢
    • 1970-01-01
    • 2020-04-21
    • 2022-10-17
    • 2021-01-17
    • 1970-01-01
    • 2017-04-28
    • 2019-02-20
    • 2019-01-20
    • 1970-01-01
    相关资源
    最近更新 更多