terraform-provider-aws 实战:用 OpenAPI 一键构建端到端 API Gateway REST API 示例
【免费下载链接】terraform-provider-awsThe AWS Provider enables Terraform to manage AWS resources.项目地址: https://gitcode.com/GitHub_Trending/te/terraform-provider-aws
本文以 terraform-provider-aws 仓库中的 examples/api-gateway-rest-api-openapi 示例为主线,完整讲解如何用 Terraform 以 OpenAPI 配置方式声明式地创建一套端到端的 AWS API Gateway REST API:通过body内嵌 OpenAPI 3.0.1 定义实现 HTTP_PROXY 反向代理、自动创建部署与 Stage、开启 CloudWatch 指标、配置带自签名 TLS 证书的 REGIONAL 自定义域名,并输出可直接运行的curl验证命令。读完本文,你将掌握aws_api_gateway_rest_api、aws_api_gateway_deployment、aws_api_gateway_stage、aws_api_gateway_method_settings、aws_api_gateway_domain_name等核心资源在真实示例中的串联用法,以及源码层面的底层实现依据。
示例概览:它做了什么
该示例(见 README.md)演示了如何创建一个完整的 AWS API Gateway REST API 环境:
- 使用OpenAPI 配置(而非逐个声明 Method/Resource)定义 API,代理 AWS IP Address Ranges 的公开 JSON 端点;
- 自动生成Deployment 与 Stage,并开启CloudWatch 指标;
- 配置一个REGIONAL 自定义域名,搭配自签名 TLS 证书,以贴近真实线上端点;
- 最终通过输出的
curl命令验证部署结果。
整套配置由 6 个 Terraform 文件构成,职责清晰:
| 文件 | 职责 |
|---|---|
| main.tf | Terraform 版本与 AWS Provider 声明 |
| rest-api.tf | OpenAPI 定义的 REST API + Deployment |
| stage.tf | Stage 与方法级 CloudWatch 指标设置 |
| domain.tf | 自定义域名与 Base Path Mapping |
| tls.tf | 自签名 TLS 证书(仅用于测试) |
| outputs.tf | 验证用的 curl 命令输出 |
如何运行这个示例
所有可变参数都集中在 variables.tf 中。官方 README 提供了两种传参方式。
方式一:使用 tfvars 文件
将模板复制为terraform.tfvars后按需修改,再执行terraform apply:
cp terraform.template.tfvars terraform.tfvars # 编辑 terraform.tfvars 修改变量 terraform applyterraform.template.tfvars 的内容如下,四个变量都有默认值,直接复制即可运行:
aws_region = "us-west-2" rest_api_domain_name = "example.com" rest_api_name = "api-gateway-rest-api-openapi-example" rest_api_path = "/path1"方式二:命令行变量标志
也可以完全跳过 tfvars 文件,直接通过-var标志传入:
terraform apply -var="aws_region=us-west-2"两种方式等价,未显式指定的变量会回落到 variables.tf 中声明的default值。
全部可配置变量
| 变量 | 默认值 | 说明 |
|---|---|---|
aws_region | us-west-2 | 部署示例 API 的 AWS 区域 |
rest_api_domain_name | example.com | 自定义域名,用于自签名 TLS 证书的 DNS 名称 |
rest_api_name | api-gateway-rest-api-openapi-example | REST API 名称(可用于触发重新部署) |
rest_api_path | /path1 | 在 REST API 中创建的路径(可用于触发重新部署) |
其中 main.tf 仅声明了 Terraform 版本下限(>= 0.12)与awsProvider 的区域配置:
terraform { required_version = ">= 0.12" } provider "aws" { region = var.aws_region }核心一:用 OpenAPI 定义 REST API(rest-api.tf)
这是整个示例的灵魂。rest-api.tf 通过aws_api_gateway_rest_api资源的body参数,将 OpenAPI 3.0.1 文档直接内嵌进 Terraform 配置:
resource "aws_api_gateway_rest_api" "example" { body = jsonencode({ openapi = "3.0.1" info = { title = var.rest_api_name version = "1.0" } paths = { (var.rest_api_path) = { get = { x-amazon-apigateway-integration = { httpMethod = "GET" payloadFormatVersion = "1.0" type = "HTTP_PROXY" uri = "https://ip-ranges.amazonaws.com/ip-ranges.json" } } } } }) name = var.rest_api_name endpoint_configuration { types = ["REGIONAL"] } }要点拆解:
jsonencode动态生成:整个 OpenAPI 文档由 HCL 对象经jsonencode序列化而成,路径${var.rest_api_path}、标题${var.rest_api_name}都是运行时插值——这正是变量描述中“可用于触发重新部署”的原因(见下文 Deployment 机制)。x-amazon-apigateway-integration:OpenAPI 规范中 AWS 的扩展字段,声明该 GET 方法对接的集成。此处使用type = "HTTP_PROXY",即把请求原样转发到上游https://ip-ranges.amazonaws.com/ip-ranges.json,配合httpMethod = "GET"与payloadFormatVersion = "1.0"(对应 REST API 的 HTTP 代理集成格式)。endpoint_configuration.types = ["REGIONAL"]:API 端点类型为区域级(非 EDGE、非 PRIVATE),与后文自定义域名同样使用 REGIONAL 类型保持一致。
从 provider 源码看,body是aws_api_gateway_rest_api的标准可选属性,定义于 internal/service/apigateway/rest_api.go:
"body": { Type: schema.TypeString, Optional: true, },当body变化时,资源会走 PUT 全量更新逻辑(见 rest_api.go 中d.HasChanges("body", names.AttrParameters)的分支判断),底层对应 API Gateway 的PutRestApi操作。
提示:把 OpenAPI 定义内嵌到
body是“文档即配置”的推荐做法,相比逐个声明aws_api_gateway_resource、aws_api_gateway_method、aws_api_gateway_integration,它可以一次导入整个 API 定义,配置量与维护成本都显著更低。如果希望把 OpenAPI 文件独立存放,也可以使用body = file("openapi.yaml")读取外部文件。
核心二:Deployment 与"自动重新部署"触发器
REST API 定义本身并不对外提供服务,必须创建 Deployment 才能把定义发布到 Stage。rest-api.tf 中的部署资源:
resource "aws_api_gateway_deployment" "example" { rest_api_id = aws_api_gateway_rest_api.example.id triggers = { redeployment = sha1(jsonencode(aws_api_gateway_rest_api.example.body)) } lifecycle { create_before_destroy = true } }这里有两个非常值得复用的工程技巧:
用
triggers+sha1嗅探 API 定义变化:aws_api_gateway_deployment是一个无状态资源,OpenAPI 定义更新后它不会自动重建。通过把redeployment触发器绑定为sha1(jsonencode(...body)),只要body内容发生变化,哈希值就会改变,进而强制触发新的 Deployment——这正是 variables.tf 中rest_api_name与rest_api_path描述“can be used to trigger redeployments”的含义。该模式同样出现在 provider 自己的测试夹具中,例如 internal/service/apigateway/deployment_test.go:redeployment = sha1(jsonencode(aws_api_gateway_integration.test)) ... create_before_destroy = truecreate_before_destroy = true:新 Deployment 先创建、旧 Deployment 后销毁,保证更新过程中 Stage 始终有可用版本,避免因删除旧部署导致短暂的 5xx 空窗。
核心三:Stage 与 CloudWatch 指标(stage.tf)
stage.tf 把上面创建的 Deployment 挂到名为example的 Stage,并用aws_api_gateway_method_settings开启方法级 CloudWatch 指标:
resource "aws_api_gateway_stage" "example" { deployment_id = aws_api_gateway_deployment.example.id rest_api_id = aws_api_gateway_rest_api.example.id stage_name = "example" } resource "aws_api_gateway_method_settings" "example" { rest_api_id = aws_api_gateway_rest_api.example.id stage_name = aws_api_gateway_stage.example.stage_name method_path = "*/*" settings { metrics_enabled = true } }stage_name = "example"决定调用 URL 中的路径段,最终调用地址形如https://<api-id>.execute-api.<region>.amazonaws.com/example/path1(见后文 outputs)。method_path = "*/*"表示匹配该 Stage 下所有资源的所有 HTTP 方法,即整个 API 全量开启指标。settings.metrics_enabled = true打开 CloudWatch 指标,之后可在 CloudWatch 控制台按ApiGateway命名空间查看 4xx/5xx、延迟、计数等指标,用于监控与告警。
核心四:自定义域名 + 自签名 TLS 证书(domain.tf 与 tls.tf)
为了让 API 更接近真实线上端点,示例还配置了自定义域名。domain.tf:
resource "aws_api_gateway_domain_name" "example" { domain_name = aws_acm_certificate.example.domain_name regional_certificate_arn = aws_acm_certificate.example.arn endpoint_configuration { types = ["REGIONAL"] } } resource "aws_api_gateway_base_path_mapping" "example" { api_id = aws_api_gateway_rest_api.example.id domain_name = aws_api_gateway_domain_name.example.domain_name stage_name = aws_api_gateway_stage.example.stage_name }aws_api_gateway_domain_name创建 REGIONAL 类型的自定义域名,并把 ACM 证书(regional_certificate_arn)绑定到该域名。由于域名为 REGIONAL 类型,需使用regional_certificate_arn而非边缘证书参数。aws_api_gateway_base_path_mapping把 REST API 的exampleStage 映射到该域名根路径,使https://<regional-domain>/<path>可以直接访问 API。
对应 provider 实现位于 internal/service/apigateway/domain_name.go 与 internal/service/apigateway/base_path_mapping.go。
自签名证书部分在 tls.tf,完全用 Terraform 生态的hashicorp/tlsProvider 本地生成,无需访问 CA:
resource "tls_private_key" "example" { algorithm = "RSA" } resource "tls_self_signed_cert" "example" { allowed_uses = [ "key_encipherment", "digital_signature", "server_auth", ] dns_names = [var.rest_api_domain_name] private_key_pem = tls_private_key.example.private_key_pem validity_period_hours = 12 subject { common_name = var.rest_api_domain_name organization = "ACME Examples, Inc" } } resource "aws_acm_certificate" "example" { certificate_body = tls_self_signed_cert.example.cert_pem private_key = tls_private_key.example.private_key_pem }要点:
- 证书的
dns_names与common_name都取自var.rest_api_domain_name(默认example.com); allowed_uses声明了server_auth(服务器认证)等用途;validity_period_hours = 12:自签名证书只有 12 小时有效期,明确是测试用途,生产中必须使用受信任 CA 签发的证书(或通过aws_acm_certificate的 DNS/Email 验证流程申请托管证书);aws_acm_certificate以certificate_body+private_key方式直接导入自签名证书(不涉及 ACM 的公有验证流程),这是验证/沙箱环境的常见做法。
核心五:输出 curl 验证命令(outputs.tf)
部署完成后,outputs.tf 会直接输出两条立即可复制的curl命令:
output "curl_domain_url" { depends_on = [aws_api_gateway_base_path_mapping.example] description = "API Gateway Domain URL (self-signed certificate)" value = "curl -H 'Host: ${var.rest_api_domain_name}' https://${aws_api_gateway_domain_name.example.regional_domain_name}${var.rest_api_path} # may take a minute to become available on initial deploy" } output "curl_stage_invoke_url" { description = "API Gateway Stage Invoke URL" value = "curl ${aws_api_gateway_stage.example.invoke_url}${var.rest_api_path}" }curl_stage_invoke_url:通过 API Gateway 默认的invoke_url(https://<api-id>.execute-api.<region>.amazonaws.com/example)直接访问,是最快的验证方式。curl_domain_url:访问自定义域名的区域端点(regional_domain_name),并用-H 'Host: ...'伪造 Host 头以匹配自签名证书的 DNS 名称——这是自签名证书场景下绕过 DNS 解析的常用技巧。由于自签名证书不受信任,实际执行时通常还需要追加-k(忽略证书校验)参数;输出注释也提醒:首次部署后该域名可能需等待约一分钟才能生效。- 两条命令都拼接了
${var.rest_api_path}(默认/path1),命中 OpenAPI 中定义的 GET 方法,最终返回 AWS IP 地址范围 JSON。
源码佐证与延伸阅读
如果想深入理解本示例所用资源的底层实现,可以继续查看 provider 源码:
- internal/service/apigateway/rest_api.go:
aws_api_gateway_rest_api的资源实现,body属性的 Schema 定义位于第 90–93 行,PutRestApi更新逻辑位于第 602 行附近; - internal/service/apigateway/rest_api_put.go:Framework 风格的 REST API 定义(
body、triggers等属性,见第 229、234 行),其测试夹具同样使用sha1(...)触发重新部署(rest_api_put_test.go); - internal/service/apigateway/deployment_test.go:Deployment 的
triggers+create_before_destroy模式官方测试用例; - internal/service/apigateway/domain_name.go、internal/service/apigateway/base_path_mapping.go、internal/service/apigateway/stage.go、internal/service/apigateway/method_settings.go:域名、路径映射、Stage 与方法设置的实现;
- docs/add-a-new-resource.md 与 docs/resource-name-generation.md:若你想基于该示例扩展新资源,可参考 provider 的资源开发规范。
小结
这个示例的价值在于:它用最少量的 Terraform 配置,覆盖了 API Gateway REST API 从"定义"到"可访问"的完整链路——OpenAPI 声明式定义、基于sha1的自动重新部署、Stage 挂载、CloudWatch 指标、REGIONAL 自定义域名与自签名证书、以及开箱即用的 curl 验证输出。其中的body + jsonencode + triggers + create_before_destroy组合模式,在任何"以 OpenAPI 驱动 API Gateway"的 Terraform 项目中都值得直接复用;唯一的测试性取舍是 12 小时有效期的自签名证书,生产环境请替换为 ACM 托管证书或受信任 CA 证书。
【免费下载链接】terraform-provider-awsThe AWS Provider enables Terraform to manage AWS resources.项目地址: https://gitcode.com/GitHub_Trending/te/terraform-provider-aws
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考