Automated summary
Guides the migration of existing Terraform resources in the Aiven provider from the SDK to the Plugin Framework using YAML-generated code while preserving state compatibility.
Strengths
- Provides a step-by-step workflow for analyzing an SDK resource, locating API operations, and mapping its schema to YAML.
- Includes precise mapping tables between Terraform SDK types and attributes and their YAML equivalents.
- Emphasizes behavior parity, state compatibility, and checking data sources that may use a different API operation.
- Explicitly builds on the tf-resource-generator skill for generation commands and testing patterns.
Limitations
- Strongly depends on the internal Aiven repository layout and the companion tf-resource-generator skill; it is not standalone.
- Assumes familiarity with Terraform provider internals, Go, and OpenAPI operation IDs.
- Does not cover full CRUD implementation, regression testing, or state-upgrade functions in detail here.
Use when porting an existing Aiven Terraform resource or data source from terraform-plugin-sdk/v2 to terraform-plugin-framework.
Do not use for creating a brand-new resource or for a non-Aiven Terraform provider; use the generator skill or provider-specific guidance instead.
Security analysis
SafeThe skill is a repo-scoped developer workflow (Aiven Terraform provider) with no network exfiltration, no curl|sh, no obfuscated payloads, and no safety-disabling instructions. Tool use (Bash/Grep/Glob/Edit/Write/Task) is appropriate and mostly read-only discovery plus standard build tasks. The only mildly destructive element is the instruction to delete legacy SDK resources after verification, which is a normal but irreversible refactor step and should be done with version control in place.
- •Step 'After Migration' instructs deleting SDK resource files and provider registrations (irreversible if uncommitted); scoped to the repo and gated on passing tests/changelog, but an agent should confirm VCS state before deleting
- •Bash usage is limited to read-only discovery (find/grep/diff/task) plus `task generate`/`task build`, which run repo-defined tasks — acceptable but executes project build scripts
Examples
Migrate the existing aiven_flink_application resource from internal/sdkprovider to the Plugin Framework. Analyze its schema and CRUD behavior first, then produce the YAML and generated code changes while preserving state compatibility.Port the aiven_service_integration data source to the Plugin Framework. Check whether it uses a different API operation than the resource and map the SDK schema to YAML.Convert this legacy Terraform SDK resource to Plugin Framework: ensure forceNew, sensitive, conflictsWith, and nested set fields are mapped correctly, and identify the clientHandler from go-client-codegen.name: tf-resource-migration description: Migrate existing Terraform resources from SDK provider (sdkprovider) to Plugin Framework. Use when converting legacy resources, ensuring state compatibility, or when the user asks to port/migrate existing resources. allowed-tools: Read, Grep, Glob, Bash, Edit, Write, Task
Terraform Resource Migration Skill
Migrate existing SDK-based resources to Plugin Framework while maintaining state compatibility and behavior parity.
Overview
This skill guides migration of resources from:
- Source:
internal/sdkprovider/(terraform-plugin-sdk/v2) - Target:
internal/plugin/(terraform-plugin-framework) with YAML-generated code
Key Challenge: Preserve exact behavior and state compatibility so users don't experience breaking changes.
Prerequisites: This skill builds on tf-resource-generator. For YAML syntax, generation commands, and testing patterns, see that skill.
When to Use This Skill
Use this skill when:
- Migrating existing
aiven_*resources from SDK to Plugin Framework - User says: "migrate", "convert", "port", "move to Plugin Framework"
- Modernizing legacy resources
Use tf-resource-generator skill when:
- Creating brand new resources from scratch
- Adding resources that don't exist yet
Migration Workflow
1. Analyze the Existing SDK Resource
Find the SDK resource:
# Find resource file
find internal/sdkprovider -name "*resource_*.go" | grep -i "resource_name"
# Find data source file
find internal/sdkprovider -name "*datasource_*.go" | grep -i "resource_name"
Read and document:
- Schema definition (all fields, types, attributes)
- CRUD functions (Create, Read, Update, Delete)
- Custom logic and transformations
- State upgrade functions (if any)
- Existing tests (critical for parity)
2. Identify API Operations
Check what API operations the SDK resource uses:
# Search for API client calls in the resource
grep -A 5 "client\." internal/sdkprovider/service/resource_name.go
# IMPORTANT: Also check the data source — it may use a different API operation
grep -A 5 "client\." internal/sdkprovider/service/resource_name_data_source.go
Then find corresponding OpenAPI operation IDs. See tf-resource-generator for OpenAPI search patterns.
Determine clientHandler: The clientHandler YAML value is the Go package name under github.com/aiven/go-client-codegen/handler/. Find it by searching for the operation ID in the module cache:
grep -r "OperationID" $(go env GOMODCACHE)/github.com/aiven/go-client-codegen*/handler/
For example, ServiceFlinkCreateApplication lives in handler/flinkapplication/, so clientHandler: flinkapplication.
3. Map SDK Schema to YAML
SDK Type → YAML Type
| SDK Type | YAML Type |
|----------|-----------|
| schema.TypeString | type: string |
| schema.TypeInt | type: integer |
| schema.TypeFloat | type: number |
| schema.TypeBool | type: boolean |
| schema.TypeList | type: arrayOrdered |
| schema.TypeSet | type: array (or arrayOrdered for performance) |
| schema.TypeMap | additionalProperties: {type: string} |
SDK Attributes → YAML Attributes
| SDK Attribute | YAML Attribute |
|---------------|----------------|
| Required: true | required: true |
| Optional: true | optional: true |
| Computed: true | computed: true |
| Sensitive: true | sensitive: true |
| ForceNew: true | forceNew: true |
| ConflictsWith: [] | conflictsWith: [] |
| ExactlyOneOf: [] | exactlyOneOf: [] |
Nested Blocks
SDK Set of objects:
"tags": {
Type: schema.TypeSet,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"key": {Type: schema.TypeString},
"value": {Type: schema.TypeString},
},
},
}
YAML (use arrayOrdered for performance):
schema:
tags:
type: arrayOrdered
items:
type: object
properties:
key:
type: string
value:
type: string
4. Preserve ID Structure
CRITICAL: The ID format MUST stay the same for state compatibility.
Find the ID format in SDK code:
# Look for ResourceData.SetId calls
grep -A 2 "SetId" internal/sdkprovider/service/resource_name.go
# Look for ID builder functions
grep -B 5 "buildResourceID\|parseResourceID" internal/sdkprovider/service/resource_name.go
Common ID patterns:
- Single field:
project - Composite:
project/service_name/database_name
Set in YAML:
idAttributeComposed: [project, service_name, database_name]
5. Handle Custom Logic
Identify in SDK code:
StateUpgraders→ May need state upgrader in Plugin FrameworkCustomizeDiff→ UseplanModifier: true- Flatten/Expand functions → Use
expandModifier: true/flattenModifier: true
For modifier implementation details, see tf-resource-generator skill.
6. Create YAML Definition
Create definitions/resource_name.yml. For complete YAML syntax reference, see tf-resource-generator skill.
Focus on migration-specific concerns:
- Match all SDK schema fields exactly
- Preserve ID structure
- Copy descriptions from SDK resource
7. Generate and Build
task generate
task build
task lint
8. State Compatibility Verification
CRITICAL: Ensure state is compatible between SDK and Plugin Framework versions.
Check schema version in SDK:
grep -A 3 "SchemaVersion" internal/sdkprovider/service/resource_name.go
If SDK has SchemaVersion > 0, you MUST handle state upgrades.
9. Backward Compatibility Testing
CRITICAL: Test that existing state from SDK version works with Plugin Framework version.
Use acc.BackwardCompatibilitySteps() helper:
func TestAccAivenResource_backwardCompat(t *testing.T) {
resourceName := "aiven_resource_name.test"
projectName := acc.ProjectName()
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acc.TestAccPreCheck(t) },
Steps: acc.BackwardCompatibilitySteps(t, acc.BackwardCompatConfig{
TFConfig: testAccResourceConfig(projectName),
OldProviderVersion: "4.47.0", // Check CHANGELOG.md for latest
Checks: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "project", projectName),
// Add all key attribute checks
),
}),
})
}
Find the latest version:
head -20 CHANGELOG.md
What this test does:
- Creates resource with OLD SDK provider version
- Applies with NEW Plugin Framework version
- Verifies state is compatible and attributes match
Example: See internal/plugin/service/mysql/database/database_test.go
10. Parity Testing
CRITICAL: Verify behavior matches SDK resource exactly.
Find SDK tests:
ls internal/sdkprovider/service/*resource_name*_test.go
Ensure Plugin Framework tests cover:
- All CRUD operations from SDK tests
- Edge cases
- Error handling
- Import functionality
- Special field behaviors
State Compatibility Checklist
Before marking migration complete:
- [ ] Resource ID format is identical
- [ ] All schema fields are present (no removals)
- [ ] Field types match exactly
- [ ] Computed fields work the same way
- [ ] Default values match
- [ ] Required/Optional flags match
- [ ] ForceNew behavior matches
- [ ] Import works with existing IDs
- [ ] Existing state can be used without migration
- [ ] Backward compatibility test added using
acc.BackwardCompatibilitySteps() - [ ] All SDK test scenarios pass with Plugin version
- [ ] Changelog entry added to
CHANGELOG.md
Common Migration Issues
| Issue | Solution |
|-------|----------|
| ID format changed accidentally | Verify idAttributeComposed matches SDK's ID builder |
| Set ordering causes diffs | Use arrayOrdered instead of array |
| Computed field becomes required | Keep as computed: true if API provides it |
| Custom validation lost | Implement in custom modifier or use schema validation |
| State upgrade needed | Implement state upgrader in Plugin Framework |
| DiffSuppressFunc behavior | Implement plan modifier for custom diff logic |
| Data source lookup key differs from resource ID | Override DataSourceOptions.Read and DataSourceOptions.Schema via init() (see below) |
| DiffSuppressFunc behavior | Use planModifier: true for custom diff logic |
| Renamed ID field missing in old state | Use planModifier: true to extract from composite ID (see tf-resource-generator skill) |
| Read fails with 404 after migration | Likely a renamed ID field is empty — use planModifier to populate it before the API call |
Data Source Lookup Key Differs from Resource ID
The generator derives the data source schema from idAttributeComposed — ID fields become Required, everything else becomes Computed. This works when the data source looks up by the same fields as the resource ID.
Problem: The SDK data source may look up by a different field (e.g., name) than what's in the resource ID (e.g., application_id). The generator has no way to express "the resource ID uses application_id, but the data source should require name."
Detection: Always read the SDK data source implementation. If it uses a List endpoint and filters by a field that's NOT in idAttributeComposed, you need a custom override.
Solution: Create a non-generated file that overrides DataSourceOptions via init():
- Override
DataSourceOptions.Schema— wrap the generateddatasourceSchema()and swap only the attributes that differ (don't duplicate the whole schema) - Override
DataSourceOptions.Read— use the List endpoint to find by name, then Get by ID for full details
Key points:
- Wrap the generated
datasourceSchema()— don't duplicate it. Only override the attributes that differ. - The read function uses List + filter by name, then Get by ID for full details (same pattern the SDK data source used).
- This preserves backward compatibility: existing configs using
namekeep working.
Reference: See internal/plugin/service/flink/application/application.go for a complete implementation.
Migration-Specific Commands
# Find SDK resource
find internal/sdkprovider -name "*resource_*.go" | grep -i "name"
# Analyze SDK schema
grep -A 20 "Schema:" internal/sdkprovider/service/resource.go
# Find SDK ID format
grep -A 2 "SetId" internal/sdkprovider/service/resource.go
# Compare implementations
diff internal/sdkprovider/service/resource.go internal/plugin/service/resource/zz_resource.go
# Run backward compatibility test
task test-acc -- -run TestAccAivenResource_backwardCompat
Key Principles
- State compatibility first - Users should not need to recreate resources
- Preserve exact behavior - Match SDK resource behavior precisely
- Test thoroughly - All SDK test scenarios must pass with Plugin version
- Remove SDK version - Once verified, delete SDK resource to avoid maintenance burden
After Migration
Once all tests pass and state compatibility is verified:
- Remove SDK resource - Delete from
internal/sdkprovider/and remove provider registration - Update documentation - Ensure docs reflect the Plugin Framework version
- Add migration notes if needed - Document any unavoidable behavioral differences
- Add changelog entry - Add record to
CHANGELOG.mdunder the unreleased section:
- Migrate `aiven_resource_name` to the Plugin Framework
Do not maintain both versions - this creates maintenance burden and user confusion.
From the file to a first result
The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.
View the source and supporting filesThis download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.
1. Download the file
Download SKILL.mdFrom the root of a test project, save the file as .skill-downloads/terraform-resource-migration-mmk3nhss/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.
Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.
mkdir -p '.skill-downloads/terraform-resource-migration-mmk3nhss' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/terraform-resource-migration-mmk3nhss/download' --output '.skill-downloads/terraform-resource-migration-mmk3nhss/SKILL.md.part' && mv '.skill-downloads/terraform-resource-migration-mmk3nhss/SKILL.md.part' '.skill-downloads/terraform-resource-migration-mmk3nhss/SKILL.md'2. Check prerequisites
Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.
Tools declared by the author (check availability in your agent): Read, Grep, Glob, Bash, Edit, Write, Task
No recognized prerequisites section in this file. This does not mean it is self-contained: libraries, tools and access requirements may be mentioned elsewhere in the content or source repository.
The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.
3. Load the skill
After inspection, copy the complete folder into your project using one of the commands below, then open a new tool session. The command stops if a folder with the same name already exists: compare the files before replacing it.
Claude Code
test -f '.skill-downloads/terraform-resource-migration-mmk3nhss/SKILL.md' && test -s '.skill-downloads/terraform-resource-migration-mmk3nhss/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/tf-resource-migration' && cp -R '.skill-downloads/terraform-resource-migration-mmk3nhss' '.claude/skills/tf-resource-migration'OpenSkills
OpenSkills must already be installed. The local install/read procedure is checked with OpenSkills 1.5.0, which requires Node.js ≥ 20.6.0. Catalog slugs are not resolved automatically.
Official OpenSkills installationopenskills install './.skill-downloads/terraform-resource-migration-mmk3nhss'openskills read 'terraform-resource-migration-mmk3nhss'Reading should display the installed file content and its base directory. It does not run the skill or install its dependencies.
For an agent that reads AGENTS.md, then sync the index. This command changes AGENTS.md: review the diff before using it.
openskills syncManual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.
4. Try a first task
Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:
Read .skill-downloads/terraform-resource-migration-mmk3nhss/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.
React Hook Form Best Practices
Development
Master React Hook Form: avoid anti-patterns, manage subscriptions, and handle reset, dirty state, and controlled inputs correctly.
Emotion to Mantine Migration
Development
Migrate Emotion styled-components to Mantine components with style props and CSS modules. Use when converting .styled.tsx files or removing @emotion imports.
Scope Creep Detector
Development
Analyzes git diffs against a stated intent to detect scope creep, unrelated files, broad PRs, and oversized changes, then suggests keep, split, or justify actions.