Terraform:根据用户输入在 AWS API Gateway 中动态生成 REST API 端点

Har*_* KM 4 amazon-web-services terraform aws-api-gateway

我正在构建一个 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"
    }
}

Run Code Online (Sandbox Code Playgroud)

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

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.
}
Run Code Online (Sandbox Code Playgroud)

官方文档:链接.


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

所以像这样:

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}"
}
Run Code Online (Sandbox Code Playgroud)

注意:aws_api_gateway_rest_api其他地方已经存在:

resource "aws_api_gateway_rest_api" "rest_api" {
  name = "my-api"
}
Run Code Online (Sandbox Code Playgroud)

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

  • 从输入中提取所有 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
}
Run Code Online (Sandbox Code Playgroud)

这对于顶级资源非常有效/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.
Run Code Online (Sandbox Code Playgroud)

我怎样才能让它适用于像这样的嵌套资源/pets/{petID}/pets/{petID}上述计划中创建资源将会失败!挑战在于parent_idaws_api_gateway_resource嵌套资源设置正确的值。这需要适用于任何级别的嵌套。


注意:存在一个数据源可以返回任何 URL 路径的 ID,如下所示:

data "aws_api_gateway_resource" "pets_resource" {
  rest_api_id = aws_api_gateway_rest_api.rest_api.id
  path        = "/pets"
}
Run Code Online (Sandbox Code Playgroud)

我只是不知道如何将它们组合在一起!

Har*_* KM 5

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

用户输入:

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"
    }
}
Run Code Online (Sandbox Code Playgroud)

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

其余 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
  })
}
Run Code Online (Sandbox Code Playgroud)

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}/*/*/*"
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

更多详细信息请参见我的 GitHub 存储库