generate-terraform-provider
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesegenerate-terraform-provider
生成Terraform Provider
Generate a Terraform provider from an OpenAPI specification using the Speakeasy CLI. This skill covers the full lifecycle: annotating your spec with entity metadata, mapping CRUD operations, generating the provider, configuring workflows, and publishing to the Terraform Registry.
使用Speakeasy CLI从OpenAPI规范生成Terraform Provider。本技能涵盖完整生命周期:为规范添加实体元数据注解、映射CRUD操作、生成Provider、配置工作流以及发布到Terraform Registry。
Content Guides
内容指南
| Topic | Guide |
|---|---|
| Advanced Customization | content/customization.md |
The customization guide covers entity mapping placement, multi-operation resources, async polling, property customization, plan modification, validation, and state upgraders.
| 主题 | 指南 |
|---|---|
| 高级自定义 | content/customization.md |
自定义指南涵盖实体映射位置、多操作资源、异步轮询、属性自定义、计划修改、验证以及状态升级器。
When to Use
适用场景
- Generating a new Terraform provider from an OpenAPI spec
- Annotating an OpenAPI spec with and
x-speakeasy-entityx-speakeasy-entity-operation - Mapping API operations to Terraform CRUD methods
- Understanding Terraform type inference from OpenAPI schemas
- Configuring for Terraform provider generation
workflow.yaml - Publishing a provider to the Terraform Registry
- User says: "terraform provider", "generate terraform", "create terraform provider", "CRUD mapping", "x-speakeasy-entity", "terraform resource", "terraform registry"
- 从OpenAPI规范生成新的Terraform Provider
- 为OpenAPI规范添加和
x-speakeasy-entity注解x-speakeasy-entity-operation - 将API操作映射到Terraform CRUD方法
- 理解从OpenAPI Schema到Terraform的类型推断规则
- 配置以生成Terraform Provider
workflow.yaml - 将Provider发布到Terraform Registry
- 用户提及以下关键词时:"terraform provider"、"generate terraform"、"create terraform provider"、"CRUD mapping"、"x-speakeasy-entity"、"terraform resource"、"terraform registry"
Inputs
输入参数
| Input | Required | Description |
|---|---|---|
| OpenAPI spec | Yes | OpenAPI 3.0 or 3.1 specification (local file, URL, or registry source) |
| Provider name | Yes | PascalCase name for the provider (e.g., |
| Package name | Yes | Lowercase package identifier (e.g., |
| Entity annotations | Yes | |
| 输入 | 是否必填 | 描述 |
|---|---|---|
| OpenAPI规范 | 是 | OpenAPI 3.0或3.1规范(本地文件、URL或注册表源) |
| Provider名称 | 是 | PascalCase格式的Provider名称(例如: |
| 包名称 | 是 | 小写格式的包标识符(例如: |
| 实体注解 | 是 | 在Schema上添加 |
Outputs
输出结果
| Output | Location |
|---|---|
| Workflow config | |
| Generation config | |
| Generated Go provider | Output directory (default: current dir) |
| Terraform examples | |
| 输出 | 位置 |
|---|---|
| 工作流配置 | |
| 生成配置 | |
| 生成的Go语言Provider | 输出目录(默认:当前目录) |
| Terraform示例 | |
Prerequisites
前置条件
- Speakeasy CLI installed and authenticated
- OpenAPI 3.0 or 3.1 specification with entity annotations
- Go installed (Terraform providers are written in Go)
- Authentication: Set env var or run
SPEAKEASY_API_KEYspeakeasy auth login
bash
export SPEAKEASY_API_KEY="<your-api-key>"Run to authenticate interactively, or set the environment variable.
speakeasy auth loginSPEAKEASY_API_KEY- Speakeasy CLI已安装并完成认证
- OpenAPI 3.0或3.1规范,且已添加实体注解
- Go语言已安装(Terraform Provider基于Go语言开发)
- 认证:设置环境变量或运行
SPEAKEASY_API_KEYspeakeasy auth login
bash
export SPEAKEASY_API_KEY="<your-api-key>"运行进行交互式认证,或设置环境变量。
speakeasy auth loginSPEAKEASY_API_KEYCommand
命令
First-time generation (quickstart)
首次生成(快速开始)
bash
speakeasy quickstart --skip-interactive --output console \
-s <spec-path> \
-t terraform \
-n <ProviderName> \
-p <package-name>bash
speakeasy quickstart --skip-interactive --output console \
-s <spec-path> \
-t terraform \
-n <ProviderName> \
-p <package-name>Regenerate after changes
变更后重新生成
bash
speakeasy run --output consolebash
speakeasy run --output consoleRegenerate a specific target
重新生成指定目标
bash
speakeasy run -t <target-name> --output consolebash
speakeasy run -t <target-name> --output consoleEntity Annotations
实体注解
Before generating, annotate your OpenAPI spec with two extensions:
生成前,需为OpenAPI规范添加两个扩展字段:
1. Mark schemas as entities
1. 将Schema标记为实体
Add to component schemas that should become Terraform resources:
x-speakeasy-entityyaml
components:
schemas:
Pet:
x-speakeasy-entity: Pet
type: object
properties:
id:
type: string
readOnly: true
name:
type: string
price:
type: number
required:
- name
- price为需要转换为Terraform资源的组件Schema添加:
x-speakeasy-entityyaml
components:
schemas:
Pet:
x-speakeasy-entity: Pet
type: object
properties:
id:
type: string
readOnly: true
name:
type: string
price:
type: number
required:
- name
- price2. Map operations to CRUD methods
2. 将操作映射到CRUD方法
Add to each API operation:
x-speakeasy-entity-operationyaml
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为每个API操作添加:
x-speakeasy-entity-operationyaml
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: DeletedCRUD Mapping Summary
CRUD映射总结
| HTTP Method | Path | Annotation | Purpose |
|---|---|---|---|
| | | Create a new resource |
| | | Read a single resource |
| | | Update a resource |
| | | Delete a resource |
Data sources (list): For list endpoints (), use a separate plural entity name with (e.g., ). Do NOT use -- it is not a valid operation type.
GET /resources#readPets#read#list| HTTP方法 | 路径 | 注解 | 用途 |
|---|---|---|---|
| | | 创建新资源 |
| | | 读取单个资源 |
| | | 更新资源 |
| | | 删除资源 |
数据源(列表):对于列表端点(),使用复数形式的实体名称并搭配(例如:)。请勿使用——这不是有效的操作类型。
GET /resources#readPets#read#listTerraform Type Inference
Terraform类型推断
Speakeasy infers Terraform schema types from the OpenAPI spec automatically:
| Rule | Condition | Terraform Attribute |
|---|---|---|
| Required | Property is | |
| Optional | Property is not | |
| Computed | Property appears in response but not in CREATE request | |
| ForceNew | Property exists in CREATE request but not in UPDATE request | |
| Enum validation | Property defined as enum | |
Every parameter needed for READ, UPDATE, or DELETE must either appear in the CREATE response or be required in the CREATE request.
Speakeasy会自动从OpenAPI规范推断Terraform Schema类型:
| 规则 | 条件 | Terraform属性 |
|---|---|---|
| 必填 | 属性在CREATE请求体中标记为 | |
| 可选 | 属性在CREATE请求体中未标记为 | |
| 计算值 | 属性出现在响应中但未出现在CREATE请求中 | |
| 强制重建 | 属性存在于CREATE请求中但不存在于UPDATE请求中 | |
| 枚举验证 | 属性定义为枚举类型 | 添加 |
READ、UPDATE或DELETE所需的每个参数必须出现在CREATE响应中,或者在CREATE请求中标记为必填。
Example
示例
Full workflow: Petstore provider
完整工作流:Petstore Provider
bash
undefinedbash
undefined1. Ensure your spec has entity annotations (see above)
1. 确保你的规范已添加实体注解(见上文)
2. Generate the provider
2. 生成Provider
speakeasy quickstart --skip-interactive --output console
-s ./openapi.yaml
-t terraform
-n Petstore
-p petstore
-s ./openapi.yaml
-t terraform
-n Petstore
-p petstore
speakeasy quickstart --skip-interactive --output console
-s ./openapi.yaml
-t terraform
-n Petstore
-p petstore
-s ./openapi.yaml
-t terraform
-n Petstore
-p petstore
3. Build and test
3. 构建并测试
cd terraform-provider-petstore
go build ./...
go test ./...
cd terraform-provider-petstore
go build ./...
go test ./...
4. After spec changes, regenerate
4. 规范变更后,重新生成
speakeasy run --output console
This produces a Terraform resource usable as:
```hcl
resource "petstore_pet" "my_pet" {
name = "Buddy"
price = 1500
}speakeasy run --output console
生成的Terraform资源可按如下方式使用:
```hcl
resource "petstore_pet" "my_pet" {
name = "Buddy"
price = 1500
}Workflow Configuration
工作流配置
Local spec
本地规范
yaml
undefinedyaml
undefined.speakeasy/workflow.yaml
.speakeasy/workflow.yaml
workflowVersion: 1.0.0
speakeasyVersion: latest
sources:
my-api:
inputs:
- location: ./openapi.yaml
targets:
my-provider:
target: terraform
source: my-api
undefinedworkflowVersion: 1.0.0
speakeasyVersion: latest
sources:
my-api:
inputs:
- location: ./openapi.yaml
targets:
my-provider:
target: terraform
source: my-api
undefinedRemote spec with overlays
带覆盖层的远程规范
For providers built against third-party APIs, fetch the spec remotely and apply local overlays:
yaml
undefined针对第三方API构建Provider时,可远程获取规范并应用本地覆盖层:
yaml
undefined.speakeasy/workflow.yaml
.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
Use `speakeasy overlay compare` to track upstream API changes:
```bash
speakeasy overlay compare \
--before https://api.vendor.com/openapi.yaml \
--after terraform_overlay.yaml \
--out overlay-diff.yamlworkflowVersion: 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变更:
```bash
speakeasy overlay compare \
--before https://api.vendor.com/openapi.yaml \
--after terraform_overlay.yaml \
--out overlay-diff.yamlRepository and Naming Conventions
仓库与命名规范
Repository naming
仓库命名
Name the repository , where is the provider type name. The provider type name should be lowercase alphanumeric (), though hyphens and underscores are permitted.
terraform-provider-XXXXXX[a-z][a-z0-9]仓库名称需为,其中为Provider类型名称。Provider类型名称应为小写字母数字(),允许使用连字符和下划线。
terraform-provider-XXXXXX[a-z][a-z0-9]Entity naming
实体命名
Use PascalCase for entity names so they translate correctly to Terraform's underscore naming:
| Entity Name | Terraform Resource |
|---|---|
| |
| |
| |
For list data sources, use the plural PascalCase form (e.g., ).
Pets实体名称需使用PascalCase格式,以确保正确转换为Terraform的下划线命名规则:
| 实体名称 | Terraform资源 |
|---|---|
| |
| |
| |
对于列表数据源,使用复数形式的PascalCase名称(例如:)。
PetsResource Importing
资源导入
Generated providers support importing existing resources into Terraform state.
生成的Provider支持将现有资源导入Terraform状态。
Simple keys
简单键
For resources with a single ID field:
bash
terraform import petstore_pet.my_pet my_pet_id对于仅含单个ID字段的资源:
bash
terraform import petstore_pet.my_pet my_pet_idComposite keys
复合键
For resources with multiple ID fields, pass a JSON-encoded object:
bash
terraform import my_test_resource.my_example \
'{ "primary_key_one": "9cedad30-...", "primary_key_two": "e20c40a0-..." }'Or use an import block:
hcl
import {
id = jsonencode({
primary_key_one: "9cedad30-..."
primary_key_two: "e20c40a0-..."
})
to = my_test_resource.my_example
}Then generate configuration:
bash
terraform plan -generate-config-out=generated.tf对于含多个ID字段的资源,传入JSON编码的对象:
bash
terraform import my_test_resource.my_example \
'{ "primary_key_one": "9cedad30-...", "primary_key_two": "e20c40a0-..." }'或使用导入块:
hcl
import {
id = jsonencode({
primary_key_one: "9cedad30-..."
primary_key_two: "e20c40a0-..."
})
to = my_test_resource.my_example
}然后生成配置:
bash
terraform plan -generate-config-out=generated.tfPublishing to the Terraform Registry
发布到Terraform Registry
Prerequisites
前置条件
- Public repository named (lowercase)
terraform-provider-{name} - GPG signing key for release signing
- GoReleaser configuration
- Registration at registry.terraform.io
- 公共仓库,名称为(小写)
terraform-provider-{name} - GPG签名密钥,用于发布签名
- GoReleaser配置
- 在registry.terraform.io完成注册
Step 1: Generate GPG Key
步骤1:生成GPG密钥
bash
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.keybash
gpg --full-generate-key # 选择RSA,4096位
gpg --armor --export-secret-keys YOUR_KEY_ID > private.key
gpg --armor --export YOUR_KEY_ID > public.keyStep 2: Configure Repository Secrets
步骤2:配置仓库密钥
Add to GitHub repository secrets:
- - Private key content
terraform_gpg_secret_key - - Key passphrase
terraform_gpg_passphrase
添加至GitHub仓库密钥:
- - 私钥内容
terraform_gpg_secret_key - - 密钥密码
terraform_gpg_passphrase
Step 3: Add Release Workflow
步骤3:添加发布工作流
yaml
undefinedyaml
undefined.github/workflows/release.yaml
.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 }}
undefinedname: 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 }}
undefinedStep 4: Register with Terraform Registry
步骤4:在Terraform Registry注册
- Go to registry.terraform.io
- Sign in with GitHub (org admin required)
- Publish → Provider → Select your repository
After registration, releases auto-publish when tags are pushed.
- 访问registry.terraform.io
- 使用GitHub登录(需组织管理员权限)
- 发布 → Provider → 选择你的仓库
注册完成后,推送标签时发布版本将自动同步到Registry。
Beta Provider Pattern
Beta Provider模式
For large APIs, maintain separate stable and beta providers:
- Stable: with semver (
terraform-provider-{name})x.y.z - Beta: with
terraform-provider-{name}-betaversioning0.x
Users can install both simultaneously. When beta features mature, graduate them to the stable provider. To set up a beta provider, create a separate repository with its own using versioning, and publish it alongside the stable provider.
terraform-provider-{name}-betagen.yaml0.x对于大型API,可维护独立的稳定版和Beta版Provider:
- 稳定版:,使用语义化版本(
terraform-provider-{name})x.y.z - Beta版:,使用
terraform-provider-{name}-beta版本号0.x
用户可同时安装两个版本。当Beta功能成熟后,可将其迁移至稳定版Provider。如需设置Beta Provider,需创建独立的仓库,使用自己的并配置版本号,与稳定版Provider一同发布。
terraform-provider-{name}-betagen.yaml0.xTesting the Provider
测试Provider
Add Test Dependency
添加测试依赖
In :
.speakeasy/gen.yamlyaml
terraform:
additionalDependencies:
github.com/hashicorp/terraform-plugin-testing: v1.13.3在中:
.speakeasy/gen.yamlyaml
terraform:
additionalDependencies:
github.com/hashicorp/terraform-plugin-testing: v1.13.3Acceptance Test Structure
验收测试结构
go
// 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,
},
},
})
}go
// 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,
},
},
})
}Running Tests
运行测试
bash
undefinedbash
undefinedUnit tests
单元测试
go test -v ./...
go test -v ./...
Acceptance tests (REQUIRES TF_ACC=1)
验收测试(必须设置TF_ACC=1)
TF_ACC=1 go test -v ./internal/provider/... -timeout 30m
**Note:** Without `TF_ACC=1`, tests silently skip with PASS status.TF_ACC=1 go test -v ./internal/provider/... -timeout 30m
**注意**:如果未设置`TF_ACC=1`,测试将自动跳过并返回PASS状态。What NOT to Do
禁忌事项
- Do NOT use as an operation type -- only
#list,create,read,updateare validdelete - Do NOT modify generated Go code directly -- changes are overwritten on regeneration. Use overlays or hooks instead
- Do NOT omit the CREATE response body -- Terraform needs the response to populate computed fields (e.g., )
id - Do NOT skip on schemas -- without it, Speakeasy cannot identify Terraform resources
x-speakeasy-entity - Do NOT use camelCase or snake_case for entity names -- use PascalCase so Terraform underscore naming works
- Do NOT generate Terraform providers in monorepo mode -- HashiCorp requires a dedicated repository
- 请勿使用作为操作类型——仅
#list、create、read、update为有效类型delete - 请勿直接修改生成的Go代码——变更会在重新生成时被覆盖,请使用覆盖层或钩子
- 请勿省略CREATE响应体——Terraform需要响应体来填充计算字段(例如:)
id - 请勿在Schema上省略注解——没有该注解,Speakeasy无法识别Terraform资源
x-speakeasy-entity - 请勿使用camelCase或snake_case作为实体名称——请使用PascalCase以适配Terraform的下划线命名规则
- 请勿在单体仓库模式下生成Terraform Provider——HashiCorp要求使用独立仓库
Troubleshooting
故障排除
| Problem | Cause | Solution |
|---|---|---|
| Used | Change to |
| Resource missing fields after import | READ operation does not return all attributes | Ensure the GET endpoint returns the complete resource schema |
| Field exists in CREATE but not UPDATE request | Add the field to the UPDATE request body if it should be mutable |
| Provider fails to compile | Missing Go dependencies | Run |
| Computed field not populated | Field absent from CREATE response | Ensure the CREATE response returns the full resource including computed fields |
| Entity not appearing as resource | Missing | Add |
| Auth not working | Missing API key | Set |
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 使用了 | 改为 |
| 导入后资源缺失字段 | READ操作未返回所有属性 | 确保GET端点返回完整的资源Schema |
意外字段被标记为 | 字段存在于CREATE请求但不存在于UPDATE请求中 | 如果字段需要可修改,请将其添加至UPDATE请求体 |
| Provider编译失败 | 缺少Go依赖 | 在Provider目录中运行 |
| 计算字段未被填充 | 字段未出现在CREATE响应中 | 确保CREATE响应返回完整资源,包括计算字段 |
| 实体未显示为资源 | 缺少 | 为组件Schema添加 |
| 认证失败 | 缺少API密钥 | 设置 |
Related Skills
相关技能
- - Initial project setup
start-new-sdk-project - - Add entity annotations via overlay
manage-openapi-overlays - - Troubleshoot generation errors
diagnose-generation-failure
- - 初始化项目设置
start-new-sdk-project - - 通过覆盖层添加实体注解
manage-openapi-overlays - - 排查生成错误
diagnose-generation-failure