Skip to content

Commit 2970911

Browse files
aRustyDevclaude
andauthored
feat(iac-terraform-provider-dev): improve skill documentation (#801)
Closes #695 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com>
1 parent 5f9a44a commit 2970911

1 file changed

Lines changed: 41 additions & 136 deletions

File tree

  • components/skills/iac-terraform-provider-dev

components/skills/iac-terraform-provider-dev/SKILL.md

Lines changed: 41 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,35 @@ description: Develop custom Terraform and OpenTofu providers using the Plugin Fr
77

88
Build production-quality Terraform and OpenTofu providers using the Plugin Framework. This skill covers the complete provider development lifecycle from design through testing and release.
99

10+
## Quick Start
11+
12+
For newcomers to Terraform provider development:
13+
14+
1. **Start with TDD**: Always write tests before implementation
15+
2. **Use Plugin Framework**: Modern replacement for SDKv2
16+
3. **Follow the structure**: Use the recommended directory layout
17+
4. **Test thoroughly**: Acceptance tests are crucial for reliability
18+
19+
### 5-Minute Setup
20+
21+
```bash
22+
# 1. Create provider structure
23+
mkdir terraform-provider-{name}
24+
cd terraform-provider-{name}
25+
26+
# 2. Initialize Go module
27+
go mod init terraform-provider-{name}
28+
29+
# 3. Add dependencies
30+
go get github.com/hashicorp/terraform-plugin-framework
31+
go get github.com/hashicorp/terraform-plugin-testing
32+
33+
# 4. Create basic structure (see Provider Structure below)
34+
# 5. Write your first test (see Acceptance Testing)
35+
```
36+
37+
See [Getting Started Guide](references/getting-started.md) for a complete walkthrough.
38+
1039
## Purpose
1140

1241
Guide the development of custom Terraform providers following HashiCorp's best practices, with emphasis on Test-Driven Development (TDD), proper resource lifecycle management, and comprehensive acceptance testing.
@@ -331,20 +360,19 @@ func (r *ThingResource) ImportState(ctx context.Context, req resource.ImportStat
331360

332361
## Acceptance Testing
333362

334-
### Test Setup
363+
Terraform providers require comprehensive acceptance testing to ensure reliability in production.
335364

336-
```go
337-
package provider
365+
| Test Pattern | Purpose | Reference |
366+
|-------------|---------|-----------|
367+
| Basic CRUD | Resource lifecycle testing | [Testing Patterns](references/testing-patterns.md#basic-crud) |
368+
| Import State | Verify import functionality | [Testing Patterns](references/testing-patterns.md#import-state) |
369+
| Plan Checks | Validate plan actions | [Testing Patterns](references/testing-patterns.md#plan-checks) |
370+
| State Checks | Modern assertion framework | [Testing Patterns](references/testing-patterns.md#state-checks) |
371+
| Drift Detection | Out-of-band change handling | [Testing Patterns](references/testing-patterns.md#drift-detection) |
338372

339-
import (
340-
"os"
341-
"testing"
342-
343-
"github.com/hashicorp/terraform-plugin-framework/providerserver"
344-
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
345-
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
346-
)
373+
### Essential Test Setup
347374

375+
```go
348376
var testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){
349377
"example": providerserver.NewProtocol6WithError(New("test")()),
350378
}
@@ -356,150 +384,27 @@ func testAccPreCheck(t *testing.T) {
356384
}
357385
```
358386

359-
### Resource Test with State Checks
387+
### Basic Test Structure
360388

361389
```go
362390
func TestAccThingResource_basic(t *testing.T) {
363391
resource.Test(t, resource.TestCase{
364392
PreCheck: func() { testAccPreCheck(t) },
365393
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
366394
Steps: []resource.TestStep{
367-
// Create and Read
368395
{
369396
Config: testAccThingResourceConfig("test-thing"),
370397
Check: resource.ComposeAggregateTestCheckFunc(
371398
resource.TestCheckResourceAttr("example_thing.test", "name", "test-thing"),
372399
resource.TestCheckResourceAttrSet("example_thing.test", "id"),
373-
resource.TestCheckResourceAttrSet("example_thing.test", "status"),
374-
),
375-
},
376-
// ImportState
377-
{
378-
ResourceName: "example_thing.test",
379-
ImportState: true,
380-
ImportStateVerify: true,
381-
},
382-
// Update
383-
{
384-
Config: testAccThingResourceConfig("updated-thing"),
385-
Check: resource.ComposeAggregateTestCheckFunc(
386-
resource.TestCheckResourceAttr("example_thing.test", "name", "updated-thing"),
387400
),
388401
},
389402
},
390403
})
391404
}
392-
393-
func testAccThingResourceConfig(name string) string {
394-
return fmt.Sprintf(`
395-
resource "example_thing" "test" {
396-
name = %[1]q
397-
}
398-
`, name)
399-
}
400405
```
401406

402-
### Plan Checks (terraform-plugin-testing v1.13.3+)
403-
404-
```go
405-
import (
406-
"github.com/hashicorp/terraform-plugin-testing/plancheck"
407-
)
408-
409-
func TestAccThingResource_planChecks(t *testing.T) {
410-
resource.Test(t, resource.TestCase{
411-
PreCheck: func() { testAccPreCheck(t) },
412-
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
413-
Steps: []resource.TestStep{
414-
{
415-
Config: testAccThingResourceConfig("test"),
416-
ConfigPlanChecks: resource.ConfigPlanChecks{
417-
PreApply: []plancheck.PlanCheck{
418-
plancheck.ExpectResourceAction("example_thing.test", plancheck.ResourceActionCreate),
419-
},
420-
},
421-
},
422-
{
423-
Config: testAccThingResourceConfig("updated"),
424-
ConfigPlanChecks: resource.ConfigPlanChecks{
425-
PreApply: []plancheck.PlanCheck{
426-
plancheck.ExpectResourceAction("example_thing.test", plancheck.ResourceActionUpdate),
427-
},
428-
},
429-
},
430-
},
431-
})
432-
}
433-
```
434-
435-
### State Checks (Modern Assertion Framework)
436-
437-
```go
438-
import (
439-
"github.com/hashicorp/terraform-plugin-testing/statecheck"
440-
"github.com/hashicorp/terraform-plugin-testing/compare"
441-
)
442-
443-
func TestAccThingResource_stateChecks(t *testing.T) {
444-
resource.Test(t, resource.TestCase{
445-
PreCheck: func() { testAccPreCheck(t) },
446-
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
447-
Steps: []resource.TestStep{
448-
{
449-
Config: testAccThingResourceConfig("test"),
450-
ConfigStateChecks: []statecheck.StateCheck{
451-
statecheck.ExpectKnownValue(
452-
"example_thing.test",
453-
tfjsonpath.New("name"),
454-
knownvalue.StringExact("test"),
455-
),
456-
statecheck.ExpectKnownValue(
457-
"example_thing.test",
458-
tfjsonpath.New("id"),
459-
knownvalue.NotNull(),
460-
),
461-
},
462-
},
463-
},
464-
})
465-
}
466-
```
467-
468-
### Drift Detection
469-
470-
```go
471-
func TestAccThingResource_drift(t *testing.T) {
472-
var thingID string
473-
474-
resource.Test(t, resource.TestCase{
475-
PreCheck: func() { testAccPreCheck(t) },
476-
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
477-
Steps: []resource.TestStep{
478-
{
479-
Config: testAccThingResourceConfig("original"),
480-
Check: resource.ComposeAggregateTestCheckFunc(
481-
resource.TestCheckResourceAttrWith("example_thing.test", "id", func(value string) error {
482-
thingID = value
483-
return nil
484-
}),
485-
),
486-
},
487-
{
488-
PreConfig: func() {
489-
// Simulate drift by modifying resource outside Terraform
490-
client := getTestClient()
491-
client.UpdateThing(context.Background(), thingID, "drifted", "")
492-
},
493-
Config: testAccThingResourceConfig("original"),
494-
Check: resource.ComposeAggregateTestCheckFunc(
495-
// Verify Terraform corrected the drift
496-
resource.TestCheckResourceAttr("example_thing.test", "name", "original"),
497-
),
498-
},
499-
},
500-
})
501-
}
502-
```
407+
See [Testing Patterns Reference](references/testing-patterns.md) for advanced patterns, plan checks, state checks, and drift detection examples.
503408

504409
## Best Practices
505410

0 commit comments

Comments
 (0)