mcpField
G
generate-terraform-provider

generate-terraform-provider

github.com/speakeasy-api云与 DevOps1878 次安装37 周前更新
代码仓库

关于 generate-terraform-provider

使用 Speakeasy CLI 从 OpenAPI 规范生成 Terraform Provider 的技能,适用于构建和维护 Provider 的开发者。它涵盖实体标注、CRUD 映射、生成配置、测试及发布到 Terraform Registry。

主要功能

  1. 01从 OpenAPI 生成 Go Provider
  2. 02用扩展标注实体与操作
  3. 03映射 API 操作为 CRUD
  4. 04配置生成工作流并重新生成
  5. 05支持测试与 Registry 发布流程

适用场景

  1. 01为现有 API 创建 Terraform Provider
  2. 02将 OpenAPI 操作映射为 Terraform 资源
  3. 03更新规范后重新生成 Provider
  4. 04准备 Provider 测试和 Registry 发布

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/speakeasy-api/sdk-generation-skills --skill generate-terraform-provider

安装后在 Claude Code 中输入/generate-terraform-provider确认已加载。

附带文件content/customization.md

详细介绍

generate-terraform-provider

使用 Speakeasy CLI 从 OpenAPI 规范生成 Terraform 提供程序。本技能涵盖完整生命周期:为规范添加实体元数据、映射 CRUD 操作、生成提供程序、配置工作流,以及发布到 Terraform Registry。

内容指南

主题指南
高级自定义content/customization.md

自定义指南涵盖实体映射的放置位置、多操作资源、异步轮询、属性自定义、计划修改、验证和状态升级器。

适用场景

  • 从 OpenAPI 规范生成新的 Terraform 提供程序
  • 使用 x-speakeasy-entity 和 x-speakeasy-entity-operation 为 OpenAPI 规范添加注解
  • 将 API 操作映射到 Terraform CRUD 方法
  • 了解如何根据 OpenAPI 架构推断 Terraform 类型
  • 配置用于生成 Terraform 提供程序的 workflow.yaml
  • 将提供程序发布到 Terraform Registry
  • 用户提到:“terraform provider”、“generate terraform”、“create terraform provider”、“CRUD mapping”、“x-speakeasy-entity”、“terraform resource”或“terraform registry”

输入

输入必需说明
OpenAPI 规范是OpenAPI 3.0 或 3.1 规范(本地文件、URL 或注册表源)
提供程序名称是提供程序的 PascalCase 名称(例如,Petstore)
包名称是小写包标识符(例如,petstore)
实体注解是架构上的 x-speakeasy-entity,操作上的 x-speakeasy-entity-operation

输出

输出位置
工作流配置.speakeasy/workflow.yaml
生成配置gen.yaml
生成的 Go 提供程序输出目录(默认为当前目录)
Terraform 示例examples/ 目录

前提条件

  1. 已安装并完成身份验证的 Speakeasy CLI
  2. 带有实体注解的 OpenAPI 3.0 或 3.1 规范
  3. 已安装 Go(Terraform 提供程序使用 Go 编写)
  4. **身份验证:**设置 SPEAKEASY_API_KEY 环境变量,或运行 speakeasy auth login
export SPEAKEASY_API_KEY="<your-api-key>"

运行 speakeasy auth login 进行交互式身份验证,或设置 SPEAKEASY_API_KEY 环境变量。

命令

首次生成(快速入门)
speakeasy quickstart --skip-interactive --output console \
  -s <spec-path> \
  -t terraform \
  -n <ProviderName> \
  -p <package-name>
更改后重新生成
speakeasy run --output console
重新生成指定目标
speakeasy run -t <target-name> --output console

实体注解

生成之前,使用两个扩展为 OpenAPI 规范添加注解:

1. 将架构标记为实体

在应转换为 Terraform 资源的组件架构中添加 x-speakeasy-entity:

components:
  schemas:
    Pet:
      x-speakeasy-entity: Pet
      type: object
      properties:
        id:
          type: string
          readOnly: true
        name:
          type: string
        price:
          type: number
      required:
        - name
        - price
2. 将操作映射到 CRUD 方法

为每个 API 操作添加 x-speakeasy-entity-operation:

paths:
  /pets:
    post:
      x-speakeasy-entity-operation: Pet#create
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Pet"
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Pet"
  /pets/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    get:
      x-speakeasy-entity-operation: Pet#read
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Pet"
    put:
      x-speakeasy-entity-operation: Pet#update
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Pet"
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Pet"
    delete:
      x-speakeasy-entity-operation: Pet#delete
      responses:
        "204":
          description: Deleted
CRUD 映射摘要
HTTP 方法路径注解用途
POST/resourceEntity#create创建新资源
GET/resource/{id}Entity#read读取单个资源
PUT/resource/{id}Entity#update更新资源
DELETE/resource/{id}Entity#delete删除资源

**数据源(列表):**对于列表端点(GET /resources),请使用单独的复数实体名称并配合 #read(例如,Pets#read)。不要使用 #list——它不是有效的操作类型。

Terraform 类型推断

Speakeasy 会根据 OpenAPI 规范自动推断 Terraform 架构类型:

规则条件Terraform 属性
必需属性在 CREATE 请求正文中为 requiredRequired: true
可选属性在 CREATE 请求正文中不是 requiredOptional: true
计算得出属性出现在响应中,但不在 CREATE 请求中Computed: true
强制重建属性存在于 CREATE 请求中,但不在 UPDATE 请求中ForceNew(强制重建资源)
枚举验证属性定义为枚举添加 Validator 以执行运行时检查

READ、UPDATE 或 DELETE 所需的每个参数,都必须出现在 CREATE 响应中,或在 CREATE 请求中设为必需。

示例

完整工作流:Petstore Provider
# 1. Ensure your spec has entity annotations (see above)

# 2. Generate the provider
speakeasy quickstart --skip-interactive --output console \
  -s ./openapi.yaml \
  -t terraform \
  -n Petstore \
  -p petstore

# 3. Build and test
cd terraform-provider-petstore
go build ./...
go test ./...

# 4. After spec changes, regenerate
speakeasy run --output console

这会生成可按如下方式使用的 Terraform 资源:

resource "petstore_pet" "my_pet" {
  name  = "Buddy"
  price = 1500
}

工作流配置

本地规范
# .speakeasy/workflow.yaml
workflowVersion: 1.0.0
speakeasyVersion: latest
sources:
  my-api:
    inputs:
      - location: ./openapi.yaml
targets:
  my-provider:
    target: terraform
    source: my-api
使用 overlays 的远程规范

对于基于第三方 API 构建的 Provider,请远程获取规范并应用本地 overlays:

# .speakeasy/workflow.yaml
workflowVersion: 1.0.0
speakeasyVersion: latest
sources:
  vendor-api:
    inputs:
      - location: https://api.vendor.com/openapi.yaml
    overlays:
      - location: terraform_overlay.yaml
    output: openapi.yaml
targets:
  vendor-provider:
    target: terraform
    source: vendor-api

使用 speakeasy overlay compare 跟踪上游 API 变更:

speakeasy overlay compare \
  --before https://api.vendor.com/openapi.yaml \
  --after terraform_overlay.yaml \
  --out overlay-diff.yaml

仓库和命名约定

仓库命名

将仓库命名为 terraform-provider-XXX,其中 XXX 是 Provider 类型名称。Provider 类型名称应为小写字母数字字符([a-z][a-z0-9]),但允许使用连字符和下划线。

实体命名

实体名称使用 PascalCase,以便正确转换为 Terraform 的下划线命名形式:

实体名称Terraform 资源
Petpetstore_pet
GatewayControlPlanekonnect_gateway_control_plane
MeshControlPlanekonnect_mesh_control_plane

对于列表数据源,使用 PascalCase 复数形式(例如 Pets)。

资源导入

生成的 Provider 支持将现有资源导入 Terraform 状态。

简单键

对于只有一个 ID 字段的资源:

terraform import petstore_pet.my_pet my_pet_id
复合键

对于有多个 ID 字段的资源,请传入 JSON 编码对象:

terraform import my_test_resource.my_example \
  '{ "primary_key_one": "9cedad30-...", "primary_key_two": "e20c40a0-..." }'

或者使用导入块:

import {
  id = jsonencode({
    primary_key_one: "9cedad30-..."
    primary_key_two: "e20c40a0-..."
  })
  to = my_test_resource.my_example
}

然后生成配置:

terraform plan -generate-config-out=generated.tf

发布到 Terraform Registry

前提条件
  1. 公开仓库,命名为 terraform-provider-{name}(小写)
  2. 用于发布签名的 GPG 签名密钥
  3. GoReleaser 配置
  4. 在 registry.terraform.io 上完成 注册
第 1 步:生成 GPG 密钥
gpg --full-generate-key  # Choose RSA, 4096 bits
gpg --armor --export-secret-keys YOUR_KEY_ID > private.key
gpg --armor --export YOUR_KEY_ID > public.key
第 2 步:配置仓库机密

将以下内容添加到 GitHub 仓库机密:

  • terraform_gpg_secret_key - 私钥内容
  • terraform_gpg_passphrase - 密钥密码
第 3 步:添加发布工作流
# .github/workflows/release.yaml
name: Release
on:
  push:
    tags: ['v*']
permissions:
  contents: write

jobs:
  goreleaser:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-go@v4
        with:
          go-version-file: 'go.mod'
      - uses: crazy-max/ghaction-import-gpg@v5
        id: import_gpg
        with:
          gpg_private_key: ${{ secrets.terraform_gpg_secret_key }}
          passphrase: ${{ secrets.terraform_gpg_passphrase }}
      - uses: goreleaser/goreleaser-action@v6
        with:
          args: release --clean
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
第 4 步:在 Terraform Registry 注册
  1. 前往 registry.terraform.io
  2. 使用 GitHub 登录(需要组织管理员权限)
  3. 选择 Publish → Provider → Select your repository

注册后,推送标签时会自动发布版本。

Beta Provider 模式

对于大型 API,请分别维护 稳定版 和 Beta 版 Provider:

  • 稳定版:terraform-provider-{name},使用语义化版本(x.y.z)
  • Beta 版:terraform-provider-{name}-beta,使用 0.x 版本号

用户可以同时安装两者。Beta 功能成熟后,将其纳入稳定版 Provider。要设置 Beta Provider,请创建单独的 terraform-provider-{name}-beta 仓库,并使用自己的 gen.yaml 和 0.x 版本号,然后与稳定版 Provider 一起发布。

测试 Provider

添加测试依赖项

在 .speakeasy/gen.yaml 中:

terraform:
  additionalDependencies:
    github.com/hashicorp/terraform-plugin-testing: v1.13.3
验收测试结构
// internal/provider/resource_test.go
func TestAccPet_Lifecycle(t *testing.T) {
    t.Parallel()

    resource.Test(t, resource.TestCase{
        PreCheck:                 func() { testAccPreCheck(t) },
        ProtoV6ProviderFactories: testAccProviders(),
        Steps: []resource.TestStep{
            {
                Config: testAccPetConfig("Buddy", 1500),
                Check: resource.ComposeTestCheckFunc(
                    resource.TestCheckResourceAttr("petstore_pet.test", "name", "Buddy"),
                ),
            },
            {
                ResourceName:      "petstore_pet.test",
                ImportState:       true,
                ImportStateVerify: true,
            },
        },
    })
}
运行测试
# Unit tests
go test -v ./...

# Acceptance tests (REQUIRES TF_ACC=1)
TF_ACC=1 go test -v ./internal/provider/... -timeout 30m

**注意:**如果没有 TF_ACC=1,测试会静默跳过,并显示 PASS 状态。

不要做的事

  • 不要将 #list 用作操作类型——只有 create、read、update、delete 有效
  • 不要直接修改生成的 Go 代码——重新生成时更改会被覆盖。请改用覆盖层或钩子
  • 不要省略 CREATE 响应正文——Terraform 需要该响应来填充计算字段(例如 id)
  • 不要在架构中跳过 x-speakeasy-entity——没有它,Speakeasy 就无法识别 Terraform 资源
  • 不要对实体名称使用 camelCase 或 snake_case——请使用 PascalCase,这样 Terraform 才能正确使用下划线命名
  • 不要在 monorepo 模式下生成 Terraform 提供程序——HashiCorp 要求使用专用仓库

故障排除

问题原因解决方案
invalid entity operation type: list使用了 #list 而不是 #read改为 Entity#read;列表端点使用复数实体名称
导入后资源缺少字段READ 操作未返回所有属性确保 GET 端点返回完整的资源架构
意外字段上出现 ForceNew字段存在于 CREATE 中,但不存在于 UPDATE 请求中如果该字段应可修改,请将其添加到 UPDATE 请求正文中
提供程序编译失败缺少 Go 依赖项在提供程序目录中运行 go mod tidy
未填充计算字段CREATE 响应中缺少该字段确保 CREATE 响应返回包含计算字段在内的完整资源
实体未显示为资源缺少 x-speakeasy-entity 注解将 x-speakeasy-entity: EntityName 添加到组件架构中
身份验证不起作用缺少 API 密钥设置 SPEAKEASY_API_KEY 环境变量,或运行 speakeasy auth login

相关技能

  • start-new-sdk-project - 初始项目设置
  • manage-openapi-overlays - 通过覆盖层添加实体注解
  • diagnose-generation-failure - 排查生成错误

评论

支持 Markdown