원클릭으로
sdk-to-pf-migration
// Guides migration of Terraform resources from Plugin SDK to Plugin Framework. Use when migrating SDK resources to PF, planning SDK-to-PF migrations, or when the user asks to migrate a resource to the Plugin Framework.
// Guides migration of Terraform resources from Plugin SDK to Plugin Framework. Use when migrating SDK resources to PF, planning SDK-to-PF migrations, or when the user asks to migrate a resource to the Plugin Framework.
Orchestrates an end-to-end implementation loop for a single OpenSpec change: select a change, ask commit-only vs PR delivery, triage the change to determine an execution strategy (inline, single-implementor, or per-task) based on change size and complexity, implement tasks using the chosen strategy, run review and validation, feed findings back for fixes, push to origin, then either watch GitHub Actions on the branch (commit mode) or create a PR and delegate PR monitoring to the pr-monitoring-loop skill (PR mode). Use when the user wants to implement an approved OpenSpec proposal/change with iterative review and CI feedback.
Monitor GitHub pull requests through a subagent-based loop that watches CI checks, review comments, PR comments, review state, merge conflicts, and branch freshness. Use when a workflow reaches PR monitoring, CI polling, review feedback handling, or asks to keep a PR merge-ready.
Detects broken and flaky acceptance tests from recent CI failures on main and opens structured GitHub issues for automated remediation. Follow this skill strictly when analyzing CI failures.
Examines an existing Terraform resource or data source implementation and produces an OpenSpec requirements document under openspec/specs/. Use when the user asks to document requirements for a Terraform entity, capture behavior from code, or write a requirements doc for a resource/data source.
Gathers initial requirements for a new Terraform resource or data source by examining API clients (go-elasticsearch, generated kbapi), Elastic API docs (Elastic docs MCP server and/or web), then interviewing the user for gaps. Produces an OpenSpec proposal (change with proposal, design, tasks, and delta specs)—not a hand-written spec under openspec/specs/ alone. Use when designing a new entity, drafting requirements from an API, or before implementing a new resource/data source.
Analyzes an OpenSpec requirements spec for internal consistency, implementation compliance, and test opportunities; when a shell is available, run openspec validate first for structural checks. Use when reviewing specs, verifying implementation against requirements, or identifying test gaps.
| name | sdk-to-pf-migration |
| description | Guides migration of Terraform resources from Plugin SDK to Plugin Framework. Use when migrating SDK resources to PF, planning SDK-to-PF migrations, or when the user asks to migrate a resource to the Plugin Framework. |
Migrate Terraform resources from terraform-plugin-sdk/v2 to terraform-plugin-framework while preserving behavior and avoiding breaking changes.
provider/factory.go combines SDK and PF via tf6muxserver. PF resources take precedence when both define the same type.internal/clients/elasticsearch (and analogous client packages) plus internal/models are used by both SDK and PF.Migrate client code to return PF diags — do not introduce a compatibility layer in the PF resource.
fwdiag.Diagnostics instead of SDK diag.Diagnostics. Use diagutil.CheckErrorFromFW() for HTTP errors, fwdiag.NewErrorDiagnostic() or diagutil.FrameworkDiagFromError() for other errors.resp.Diagnostics with no conversion.diagutil.SDKDiagsFromFramework() when calling the migrated client.If the resource has dedicated client functions, migrate them to return fwdiag.Diagnostics:
sdkdiag.Diagnostics to fwdiag.Diagnosticsdiagutil.CheckErrorFromFW() instead of diagutil.CheckError() for HTTP responsesfwdiag.NewErrorDiagnostic() / diagutil.FrameworkDiagFromError() for errorsUpdate any SDK callers to use diagutil.SDKDiagsFromFramework() when calling these functions.
(Example: ILM used PutIlm / GetIlm / DeleteIlm — same pattern applies to any named CRUD helpers.)
Create internal/<domain>/<resource>/ (path mirrors where the SDK resource lived). Typical layout:
| File | Purpose |
|---|---|
resource.go | Resource struct, Metadata, Configure, ImportState |
schema.go | PF schema including the appropriate connection block for the backend |
models.go | Plan/State model types |
create.go | Create |
read.go | Read |
update.go | Update |
delete.go | Delete |
resource-description.md | Embedded description |
Schema: Use the same connection block helpers as other PF resources in this provider (e.g. providerschema.GetEsFWConnectionBlock(false) for Elasticsearch-backed resources). Replicate the SDK schema exactly; preserve attribute names, types, and validation.
Critical behaviors to preserve (audit the SDK implementation for each):
readonly / freeze / unfollow use enabled: false when absent in config.jsontypes.NormalizedType or the project’s equivalent for metadata-like blobs).total_shards_per_node: -1 when ES < 7.16).provider/plugin_framework.go — register NewResource in resources() following the existing registration pattern.provider/provider.go — remove the type from ResourcesMap.internal/<domain>/<resource>/acc_test.go with package <resource>_test.checkResourceDestroy from the old *_test.go.testdata/TestAccResourceX*/ into the new package.*_test.go.Add TestAccResourceXFromSDK to verify existing SDK-created state works after upgrade:
//go:embed testdata/TestAccResourceXFromSDK/create/resource.tf
var sdkCreateConfig string
func TestAccResourceXFromSDK(t *testing.T) {
name := sdkacctest.RandStringFromCharSet(10, sdkacctest.CharSetAlphaNum)
resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
Steps: []resource.TestStep{
{
ExternalProviders: map[string]resource.ExternalProvider{
"elasticstack": {
Source: "elastic/elasticstack",
VersionConstraint: "0.14.3", // last SDK version for this resource
},
},
Config: sdkCreateConfig,
ConfigVariables: config.Variables{"name": config.StringVariable(name)},
Check: resource.ComposeTestCheckFunc(...),
},
{
ProtoV6ProviderFactories: acctest.Providers,
ConfigDirectory: acctest.NamedTestCaseDirectory("create"),
ConfigVariables: config.Variables{"name": config.StringVariable(name)},
Check: resource.ComposeTestCheckFunc(...),
},
},
})
}
Use the last provider version where the resource was still SDK-based for VersionConstraint.
Run schema-coverage analysis. Add tests for:
TestAccResourceX_importState if missing)Use these PF migrations as patterns (complexity varies):
internal/elasticsearch/security/user/ — includes TestAccResourceSecurityUserFromSDKinternal/elasticsearch/index/ilm/ — large schema, version gating, JSON normalizationinternal/elasticsearch/index/datastreamlifecycle/ — smaller surface areamake buildgo test ./internal/<domain>/<resource>/... -vgo test ./internal/<domain>/<resource>/... -v -count=1 -run TestAcc. Follow testing for environment requirements.elasticstack_elasticsearch_index_lifecycle).Avoid unless unavoidable. Preserve:
elasticstack_elasticsearch_index_lifecycle for ILM)