Skip to content
Development
Skill

/provider-resources

Implement Terraform Provider resources and data sources using the Plugin Framework: CRUD operations, schema design, plan modifiers and validators, not-found handling, waiters for eventually consistent APIs, import support, resource design principles, and required acceptance test

From plugin
agent-skills
91020 skills

Claude Code

$ npx -y skills add hashicorp/agent-skills --skill provider-resources --agent claude-code

SKILL.md

provider-resources.SKILL.md
name: provider-resources
description: >-
  Implement Terraform Provider resources and data sources using the Plugin
  Framework: CRUD operations, schema design, plan modifiers and validators,
  not-found handling, waiters for eventually consistent APIs, import support,
  resource design principles, and required acceptance test coverage. Use when
  adding or changing a resource or data source, deciding whether an API
  concept should be a resource, wiring a resource to the provider's
  configured client, handling drift or resource-not-found, or reviewing a
  resource implementation before submission.
license: MPL-2.0
metadata:
  lifecycle-status: active
  copyright: Copyright IBM Corp. 2026
  version: "0.0.1"

Terraform Provider Resources Implementation Guide

Overview

This guide covers developing Terraform Provider resources and data sources. Resources represent infrastructure objects that Terraform manages through Create, Read, Update, and Delete (CRUD) operations.

**Use the [Plugin Framework](https://developer.hashicorp.com/terraform/plugin/framework) for all net-new resources and data sources.** Plugin SDKv2 is for maintaining resources that already exist on it; do not write new code against it. A provider can serve both during migration by muxing ([terraform-plugin-mux](https://developer.hashicorp.com/terraform/plugin/mux)), so adopting the Framework never requires a big-bang rewrite. To tell which mode an existing provider is in, check `go.mod`: `terraform-plugin-mux` present means it serves both SDKv2 and Framework code; only `terraform-plugin-sdk/v2` means SDKv2-only; only `terraform-plugin-framework` means Framework-only. Be cautious about *migrating* existing SDKv2 resources: the Framework distinguishes null from zero values, so naive migrations change behavior for existing users (use the `provider-framework-migration` skill, if available).

**References** (load when needed):

  • `references/design-principles.md` — what should (and should not) become a

resource; data source semantics; relationship and async-task modeling

  • `references/retries-and-waiters.md` — eventual consistency, retry

patterns, and status/wait function structure

File Structure

Most providers keep every resource in a single package:

internal/provider/
├── provider.go                  # Provider schema + Configure
├── widget_resource.go           # Resource implementation
├── widget_resource_test.go      # Acceptance tests
├── widget_data_source.go        # Data source (if applicable)
└── widget_data_source_test.go

Large multi-service providers (e.g. terraform-provider-aws) split into `internal/service/<service>/` packages instead, with an idiomatic file taxonomy worth adopting once a package grows: `consts.go`, `find.go` (finders), `status.go` (status functions), `wait.go` (waiters), `sweep.go` (test sweepers), `exports_test.go`.

Documentation lives in `docs/` and is generated with `tfplugindocs`:

docs/
├── resources/<name>.md          # generated; optional <name>.md.tmpl template
└── data-sources/<name>.md

(Hand-written `website/docs/r/*.html.markdown` trees exist in some older, large providers — follow the target repo's convention when editing one.)

Resource Structure

A Framework resource is a struct holding the API client, with interface assertions making the implemented behaviors explicit:

var (
    _ resource.Resource                = &widgetResource{}
    _ resource.ResourceWithConfigure   = &widgetResource{}
    _ resource.ResourceWithImportState = &widgetResource{}
)

func NewWidgetResource() resource.Resource {
    return &widgetResource{}
}

type widgetResource struct {
    client *examplecloud.Client
}

func (r *widgetResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
    resp.TypeName = req.ProviderTypeName + "_widget"
}

// Configure receives the client the provider built in its own Configure.
func (r *widgetResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
    if req.ProviderData == nil {
        return // provider not yet configured (e.g. validation phase)
    }
    client, ok := req.ProviderData.(*examplecloud.Client)
    if !ok {
        resp.Diagnostics.AddError(
            "Unexpected Resource Configure Type",
            fmt.Sprintf("Expected *examplecloud.Client, got: %T.", req.ProviderData),
        )
        return
    }
    r.client = client
}

func (r *widgetResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
    resp.Schema = schema.Schema{
        Attributes: map[string]schema.Attribute{
            "name": schema.StringAttribute{
                Required: true,
                PlanModifiers: []planmodifier.String{
                    stringplanmodifier.RequiresReplace(),
                },
                Validators: []validator.String{
                    stringvalidator.LengthBetween(1, 255),
                },
            },
            "id": schema.StringAttribute{
                Computed: true,
                PlanModifiers: []planmodifier.String{
                    stringplanmodifier.UseStateForUnknown(),
                },
            },
        },
    }
}

How the provider's `Configure` produces that client — schema, credential resolution, validation — is covered by the `provider-configuration` skill (if available).

**On `id`:** SDKv2 required a magic `id` attribute; the Framework does not. If the API has its own identifier, expose it under its real meaning and do not add a second, redundant `id`. Only keep `id` when it *is* the API's identifier (as above).

CRUD Operations

Create

func (r *widgetResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
    var data widgetResourceModel
    resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
    if resp.Diagnostics.HasError() {
        return
    }

    inp
Read more
@hashicorp
Ships withagent-skills

HashiCorp Agent Skills for Terraform and Packer. See SKILLS.md for the complete catalog and lifecycle status of each Skill. Legal note: Your use of a third-party MCP client or LLM is subject solely to that provider's terms.

Stars on GitHub

Momentum

stars in total
910
Star history of hashicorp/agent-skills on GitHub, a chart by star-history.com

Other skills on agent-skills.

aws-ami-builder

aws-ami-builder

Build Amazon Machine Images (AMIs) with Packer using the amazon-ebs builder. Use when creating custom AMIs for EC2 instances.

push-to-registry

push-to-registry

Push Packer build metadata to HCP Packer registry for tracking and managing image lifecycle. Use when integrating Packer builds…

windows-builder

windows-builder

Build Windows images with Packer using WinRM communicator and PowerShell provisioners. Use when creating Windows AMIs, Azure…

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills11d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc13d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react2d252k
@facebook@facebookInstall Skill

Questions about provider-resources.

Which coding agents can use provider-resources?
provider-resources installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.