新闻详情

新闻详情

首页 / 资讯中心 / 详情

Teleport Terraform Provider 资源开发指南:从新增资源到 legacy 迁移的完整实践

发布时间:2026/9/21 2:19:04来源:尧图网络
Teleport Terraform Provider 资源开发指南:从新增资源到 legacy 迁移的完整实践
网络安全认证鉴权运维后端【免费下载链接】teleportThe easiest, and most secure way to access and protect all of your infrastructure.项目地址https://gitcode.com/gh_mirrors/tel/teleport点击查看免费下载本指南以 integrations/terraform/CONTRIBUTING.md 为主线系统讲解如何在 Teleport 的 Terraform Provider 中新增一个资源resource或数据源data source以及如何把遗留legacy生成式资源平滑迁移到通用驱动generic driver架构。读者将掌握完整的六步新增流程、五步迁移路径、标识符策略选型与评审清单并了解底层 tfdriver 驱动的生命周期行为可直接用于向当前仓库提交新资源支持。背景为什么需要通用驱动Teleport Terraform Provider 构建在 HashiCorp Terraform Plugin Framework 之上入口见 provider/provider.go。早期资源由代码生成器批量产出每个资源都携带大量重复的 Terraform 生命周期样板代码后续演进出的通用驱动generic driver把Terraform 生命周期机制与资源专属的 Teleport API 调用彻底分离从而让新增一个资源只需要编写很少的手写代码。两者的架构关系见 ARCHITECTURE.mdprovider 的注册表被拆成两组——provider/internal/legacy遗留生成式实现与provider/internal/resources通用驱动实现。GetResources与GetDataSources先取 legacy 注册表再把通用驱动资源插入其中见 provider.go 的 GetResources/GetDataSources这样迁移后的资源可以无缝替换同名注册对外暴露的 Terraform 资源名完全不变。通用驱动是新增资源的唯一推荐方式legacy 生成器已被标记为废弃将在所有存量资源迁移完成后移除。开始前的准备按文档要求开始动手前需确认以下几点除非特别说明所有命令都在integrations/terraform目录下执行先查阅对应的 Teleport 资源 API涉及 RFD 153 的资源遵循 rfd/0153-resource-guidelines.md迁移存量资源时必须保留公开的 Terraform 名称、字段路径、import ID 与 state 行为保持生成代码的生成物属性除了文档命令产生的生成输出外不要手改生成文件。常用命令速查# 重新生成 Terraform schema/copy 代码与 legacy 生成文件 make gen-tfschema # 构建/安装 provider并在 testlib 中运行 Terraform 验收风格测试 make test # 从测试套件中运行一个聚焦用例 make test TEST_ARGS-run TestTerraformOSS/TestApp # 重新生成面向用户的 Terraform Provider 参考文档 make docs关于make test有一点需要留意测试目标依赖本机安装 Terraform v1.4Makefile 中会检测terraform -version不满足直接报错退出并通过gotestsum把结果输出到test-logs/unit-tests-terraform.xmlmake test-ent则会先由go generate testlib/plugin_test.go生成企业版测试文件再跑完整套件。新增一个通用驱动资源六步全流程第一步生成或更新 Terraform schema 代码资源的 Terraform schema 与 copy转换函数统一存放在integrations/terraform/tfschema。如果目标资源的GenSchemaXXX/CopyXXXToTerraform/CopyXXXFromTerraform已经存在直接复用即可。若不存在则按以下步骤补齐新增或更新一个protoc-gen-terraform-*.yaml配置文件仓库根目录下已有一批现成示例如 protoc-gen-terraform-accesslist.yaml、protoc-gen-terraform-loginrule.yaml 等在 Makefile 的gen-tfschema目标中新增对应的protoc调用与mv步骤执行make gen-tfschema。从 Makefile 的 gen-tfschema 目标可以看到该目标的真实工作方式它通过go list -m定位 go mod cache 中的 gogo/protobuf 路径对每个 proto 文件执行一次protoc --terraform_outconfigprotoc-gen-terraform-XXX.yaml:./tfschema再把产物从tfschema/github.com/gravitational/teleport/...逐目录mv到扁平结构最后调用go run ./gen/main.go重新生成 legacy 代码。凡是 proto 变更影响到 Terraform 层都必须在integrations/terraform下运行该目标——这也呼应了不要手改生成文件的纪律。第二步编写 Teleport API 适配层创建provider/internal/teleport/resource.go。适配层应当包装*client.Client对托管资源实现tfdriver.ResourceClient[T, I]Get/Create/Upsert/Delete对数据源实现tfdriver.DataSourceClient[T, I]只需Get。该层只负责 Teleport API 调用不得依赖 Terraform 的 schema、plan、state 或 diagnostics。文档给出的骨架package teleport import ( context github.com/gravitational/trace github.com/gravitational/teleport/api/client apitypes github.com/gravitational/teleport/api/types github.com/gravitational/teleport/integrations/terraform/provider/internal/tfdriver ) func NewFooClient(c *client.Client) FooClient { return FooClient{client: c} } type FooClient struct { client *client.Client } func (c FooClient) Get(ctx context.Context, id tfdriver.NameIdentifier) (*apitypes.FooV1, error) { foo, err : c.client.GetFoo(ctx, id.Name) if err ! nil { return nil, trace.Wrap(err) } return foo, nil } func (c FooClient) Create(ctx context.Context, foo *apitypes.FooV1) error { return trace.Wrap(c.client.CreateFoo(ctx, foo)) } func (c FooClient) Upsert(ctx context.Context, foo *apitypes.FooV1) error { return trace.Wrap(c.client.UpsertFoo(ctx, foo)) } func (c FooClient) Delete(ctx context.Context, id tfdriver.NameIdentifier) error { return trace.Wrap(c.client.DeleteFoo(ctx, id.Name)) }两点进阶要求API 返回接口类型时在适配层做类型断言遇到意外类型返回可读的错误更新需要保留服务端字段时让适配层实现tfdriver.UpdatePreparer[T]。真实例子见 provider/internal/teleport/access_list.goPrepareUpdate会把旧资源的Spec.Audit.NextAuditDate服务端计算字段拷贝到新值上避免更新时被重置驱动在 resource.go 的 Update 流程中会自动检测该接口并调用。第三步添加资源与数据源描述符创建provider/internal/resources/resource.go。描述符descriptor负责把 API 适配层、生成的 schema/copy 函数、标识符策略、normalizer 与 revision 提取逻辑接在一起package resources import ( github.com/hashicorp/terraform-plugin-framework/path github.com/hashicorp/terraform-plugin-framework/tfsdk apitypes github.com/gravitational/teleport/api/types github.com/gravitational/teleport/integrations/terraform/provider/internal/teleport github.com/gravitational/teleport/integrations/terraform/provider/internal/tfdriver github.com/gravitational/teleport/integrations/terraform/tfschema ) func NewFooDataSourceType() tfdriver.DataSourceType[apitypes.FooV1, tfdriver.NameIdentifier] { return tfdriver.DataSourceType[apitypes.FooV1, tfdriver.NameIdentifier]{ NewDataSourceClient: func(p tfsdk.Provider) tfdriver.DataSourceClient[apitypes.FooV1, tfdriver.NameIdentifier] { return teleport.NewFooClient(clientFromProvider(p)) }, Kind: apitypes.KindFoo, Codec: tfdriver.DataSourceCodecFuncs[apitypes.FooV1]{ SchemaFunc: tfschema.GenSchemaFooV1, ToStateFunc: tfschema.CopyFooV1ToTerraform, }, Identifier: tfdriver.NameIdentifierFromPath(path.Root(metadata).AtName(name)), } } func NewFooResourceType() tfdriver.ResourceType[apitypes.FooV1, tfdriver.NameIdentifier] { return tfdriver.ResourceType[apitypes.FooV1, tfdriver.NameIdentifier]{ NewResourceClient: func(p tfsdk.Provider) tfdriver.ResourceClient[apitypes.FooV1, tfdriver.NameIdentifier] { return teleport.NewFooClient(clientFromProvider(p)) }, Kind: apitypes.KindFoo, Codec: tfdriver.ResourceCodecFuncs[apitypes.FooV1]{ SchemaFunc: tfschema.GenSchemaFooV1, FromPlanFunc: tfschema.CopyFooV1FromTerraform, ToStateFunc: tfschema.CopyFooV1ToTerraform, }, Normalizer: tfdriver.CheckAndSetDefaults[apitypes.FooV1](), Identifier: tfdriver.NameIdentifierPolicy( path.Root(metadata).AtName(name), func(foo *apitypes.FooV1) string { return foo.GetMetadata().Name }, ), ResourceRevision: func(foo *apitypes.FooV1) string { return foo.GetMetadata().Revision }, } }描述符中值得深入理解的两个概念标识符策略IdentifierPolicy标识符负责把 Terraform 侧的对象映射到 Teleport 集群中唯一的资源同时决定了 import ID 的解析格式。按 identifier.go 的实现常用策略有四种策略适用场景import ID 形态NameIdentifierPolicy以metadata.Name唯一定位的大多数资源纯名称如my-roleScopeQualifiedNameIdentifierPolicy以(name, scope)定位的 scoped 资源scope 限定的限定名通过lib/scopes的QualifiedName解析并做强校验CompositeIdentifierPolicy双段 ID如 Access List Member 需要列表名 成员名prefix/name两段式SingletonIdentifierPolicy集群级单例资源固定名称import 时 ID 必须与固定名完全一致此外还有ScopeQualifiedCompositeIdentifierPolicyprefix 与 name 各自都可能带 scope与可能不带 scope的变体。每种策略都内置了FromState、FromResource、FromImportID三套提取逻辑并负责把标识符渲染成 Terraform import ID。Normalizer规范化器在调用 Teleport API 之前强制资源不变量。内置实现见 normalize.gotfdriver.CheckAndSetDefaults[T]()调用资源类型的CheckAndSetDefaults()方法补齐默认值tfdriver.ForceKindT当 API 类型需要设置 kind、但 Terraform 不应要求用户配置时通过SetKind强制写入tfdriver.ResourceNormalizers[T]按序组合多个 normalizer。架构文档的忠告是优先用 normalizer 表达默认值逻辑而不是把默认值逻辑重复塞进 API 适配层。第四步注册资源在 provider/provider.go 中完成注册。把资源加入GetResources的genericResourceTypesteleport_foo: resources.NewFooResourceType(),如果同时有数据源加入GetDataSources的genericDataSourceTypesteleport_foo: resources.NewFooDataSourceType(),从当前源码看genericResourceTypes已覆盖teleport_access_list、teleport_role、teleport_database、teleport_user、teleport_workload_identity等三十个资源genericDataSourceTypes与之基本一一对应这些 map 会通过maps.Insert合并进 legacy 注册表同名资源由通用驱动实现覆盖 legacy 实现。注意一个资源只能注册在一个活跃位置generic map 或 legacy registry绝不允许两边同时注册同名资源。第五步编写测试与 fixture在testlib/fixtures下添加 Terraform fixture通常包括resource_0_create.tf创建resource_1_update.tf更新resource_data_source.tf数据源如适用当前仓库的 fixture 目录testlib/fixtures展示了完整命名惯例例如app_0_create.tf、app_1_update.tf、classifier_data_source.tf以及针对 cache 行为的app_0_create_with_cache.tf、针对默认值的access_list_defaults.tf等。在testlib/resource_test.go中覆盖以下场景create / read / update / delete 全生命周期create 与 update 后的 plan 稳定性plan-only 检查import 状态数据源行为若存在如果该资源已知会与缓存读交互覆盖 cache 启用时的行为。对含密钥或 write-only 字段的资源还需断言敏感值不会泄漏进 state除非 schema 有意存储。第六步更新文档如果资源改变了 Provider 的公开面运行make docs该目标依赖gen-tfschema、本地安装 provider 与terraform fmt最终通过./gen/docs.sh渲染。参考文档的生成机制见 integrations/terraform/DOCS.md默认所有资源共用模板 templates/resources.md.tmpl并自动引用examples/resources/teleport_resource-name/resource.tf如需自定义说明或多种示例可把默认模板复制为资源专属模板templates/resources/resource_name.md.tmpl再用{{tffile ./examples/resources/...}}函数嵌入代码示例。把 legacy 资源迁移到通用驱动五步路径迁移路径与新增资源类似但兼容性是第一优先级。第一步先摸清当前行为动手改代码前检查provider/internal/legacy下的现有实现与testlib中既有测试逐一记录Terraform 资源名与数据源名schema 路径及各字段 required/optional/computed/sensitive 标志import ID 格式写入 Terraform state 的 IDcreate/update 使用的方法Create 还是 Upsert默认值、强制 kind/version 行为update 时从旧 state 或远端 state 拷贝的字段write-only/密钥字段的特殊处理重试与轮询行为数据源的怪癖quirks。通用实现必须复刻这些行为除非是有意为之且有文档记录的变更。第二步补齐通用实现按新增资源的第二、三步添加provider/internal/teleport/resource.go与provider/internal/resources/resource.go。schema 与 copy 函数尽量复用 legacy 资源已经在用的tfschema生成代码——这是保持 Terraform 字段兼容稳定的关键。第三步切换注册把资源与数据源从provider/internal/legacy/registry.go移除并在provider/provider.go的 generic map 中加入同名条目。例如迁移teleport_foo// provider/provider.go teleport_foo: resources.NewFooResourceType(),如有数据源teleport_foo: resources.NewFooDataSourceType(),迁移过程中不得改变公开的 Terraform 类型名——这正是GetResources先加载 legacy 再插入 generic 的合并设计所保证的见 provider.go。第四步处置 legacy 生成文件部分 legacy 文件由gen/main.go依据文件内嵌的 payload 生成make gen-tfschema末尾会先 grep 掉带Code generated by _gen/main.go DO NOT EDIT标记的旧文件再重新生成见 Makefile。被转换的资源一旦不再注册旧生成文件可能仍能编译但已无人使用。安全的情况下优先删除 legacy 生成器 payload 与对应生成文件若生成的 schema/copy 函数仍需使用保留protoc-gen-terraform-*.yaml与make gen-tfschema条目——它们与 legacy provider 生成是相互独立的两条链路。第五步围绕兼容性强化测试先原封不动地运行既有资源测试再针对迁移敏感行为增补用例用旧的 import ID 格式导入一个已存在的 Teleport 资源先 apply 旧 fixture再 apply 新 fixture验证不会触发无谓的资源替换create/update 后的 plan-only 检查用户已在依赖的最小配置下的数据源读取敏感/write-only 字段的 state 表现Teleport 返回 not found 时的行为。迭代阶段用make test TEST_ARGS-run ...聚焦运行提交前务必跑完整 provider 测试目标。驱动层如何实现这些生命周期约定理解驱动层的通用行为resource.go能帮你写出与既有资源行为一致的适配层Create 前检活Create先按标识符Get一次若资源已存在于 Teleport非单例直接报错并提示tctl rm或terraform import两条出路防止误创建重复资源Create/Update 前规范化调用 normalizer 的NormalizeCreate/NormalizeUpdate最终一致性的重试创建/更新后进入重试循环按retry_base_duration默认 1s、retry_cap_duration默认 5s、retry_max_tries默认 10配置的指数退避带 HalfJitter反复Get直到读到数据超限后给出state outdated, please import resource的诊断——这些重试参数正是在 provider.go 的 Configure 中解析并存入RetryConfig的Update 收敛判断若描述符提供了ResourceRevisionUpdate 会一直轮询到远端metadata.Revision真正变化才认为更新生效见 resource.go 的 Update 循环——这就是文档中Update 应等待真实远端 revision 变化的实现Read 行为远端资源不存在时驱动会把资源从 state 中移除resp.State.RemoveResourceImport 状态解析与填充ImportState用Identifier.FromImportID解析导入 IDGet后经 Codec 写入 state并回填id属性统一诊断封装所有错误经internal/tfdiag包装成一致的 Terraform diagnostics。提交前的评审清单Review Checklist文档给出了明确的 PR 验收标准逐条核对资源恰好注册在一个活跃位置generic map 或 legacy registry二选一迁移场景下公开的 Terraform 名称与 import ID 保持不变资源与数据源的id字段被一致地填充资源的 kind/version/defaults 由 Teleport 默认值或显式 normalizer 设置适用时Update 会等待远端真实 revision 变化密钥与 write-only 字段的处理是有意为之的测试覆盖 create/update/delete/import数据源场景如适用也覆盖schema 或公开文档有变时已运行make gen-tfschema与make docs。参考实现速览架构总览integrations/terraform/ARCHITECTURE.mdProvider 入口与注册表integrations/terraform/provider/provider.go通用驱动实现integrations/terraform/provider/internal/tfdriver/resource.go、identifier.go、normalize.goAPI 适配层示例integrations/terraform/provider/internal/teleport/access_list.go描述符目录integrations/terraform/provider/internal/resources生成规则与 YAML 配置integrations/terraform/Makefile、protoc-gen-terraform-accesslist.yaml验收测试与 fixtureintegrations/terraform/testlib、testlib/fixtures文档生成机制integrations/terraform/DOCS.md资源指南规范rfd/0153-resource-guidelines.md赞分享网络安全认证鉴权运维后端【免费下载链接】teleportThe easiest, and most secure way to access and protect all of your infrastructure.项目地址https://gitcode.com/gh_mirrors/tel/teleport点击查看免费下载相关推荐terraform-provider-aws 新增 Ephemeral Resource临时资源完整开发指南terraform provider aws 新增 Ephemeral Resource临时资源完整开发指南 导读 本指南基于 terraform provIaC云原生基础设施Cua Fleets Terraform Provider从 Cyclops 迁移到 Fleets 的完整迁移指南Cua Fleets Terraform Provider从 Cyclops 迁移到 Fleets 的完整迁移指南 本篇指南基于 Cua 仓库中 MIGRAT人工智能AI AgentGUI 自动化Agent 评测强化学习Agent 沙箱计算机视觉MCP 服务IcemacOS 菜单栏管理工具一键隐藏图标10 分钟理好菜单栏IcemacOS 菜单栏管理工具一键隐藏图标10 分钟理好菜单栏 你的 Mac 菜单栏被各种应用图标挤满Wi Fi 和电池信息被推到角落。Ice 是一款桌面应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

SvelteKit 断点调试完整指南:从 VSCode 到浏览器 DevTools 的前后端单步调试 2026/9/21 2:58:11

SvelteKit 断点调试完整指南:从 VSCode 到浏览器 DevTools 的前后端单步调试

SvelteKit 断点调试完整指南:从 VSCode 到浏览器 DevTools 的前后端单步调试 【免费下载链接】kit web development, streamlined 项目地址: https://gitcode.com/gh_mirrors/kit/kit 导读 SvelteKit 应用同时包含浏览器端(客户端组件、load 中的…

阅读更多 →
discord.py 终极入门:10分钟打造你的第一个 Discord Bot(Python新手友好) 2026/9/21 2:58:11

discord.py 终极入门:10分钟打造你的第一个 Discord Bot(Python新手友好)

discord.py 终极入门:10分钟打造你的第一个 Discord Bot(Python新手友好) 【免费下载链接】discord.py An API wrapper for Discord written in Python. 项目地址: https://gitcode.com/gh_mirrors/di/discord.py discord.py 是一款用…

阅读更多 →
装修避坑指南:从水电改造到软装尺寸的施工工艺与验收标准全解析 2026/9/21 2:58:11

装修避坑指南:从水电改造到软装尺寸的施工工艺与验收标准全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Ghidra逆向入门:从安装配置到baby.exe实战分析 2026/9/21 2:58:11

Ghidra逆向入门:从安装配置到baby.exe实战分析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
RK3588多传感器AI融合自主导航系统实战 2026/9/21 2:58:11

RK3588多传感器AI融合自主导航系统实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Caffeine JCache 适配器 JSR-107 一致性审计:从 TCK 盲区到全规范面覆盖的工程实践 2026/9/21 2:55:10

Caffeine JCache 适配器 JSR-107 一致性审计:从 TCK 盲区到全规范面覆盖的工程实践

Caffeine JCache 适配器 JSR-107 一致性审计:从 TCK 盲区到全规范面覆盖的工程实践 【免费下载链接】caffeine A high performance caching library for Java 项目地址: https://gitcode.com/gh_mirrors/ca/caffeine JSR-107(JCache)1…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞