@@ -7,6 +7,35 @@ description: Develop custom Terraform and OpenTofu providers using the Plugin Fr
77
88Build 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
1241Guide 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
348376var 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
362390func 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