Generated banner for New Terraform Provider
Generated logo for New Terraform Provider

New Terraform Provider

HashiCorpOfficialPublished by HashiCorp

  • Skill
  • Workflow
  • Cloud and DevOps
  • MPL-2.0
  • Low risk
  • Has scripts

From the author

Use this when scaffolding a new Terraform provider with the Plugin Framework: workspace layout, go module setup, provider server main.go, and a provider.go with schema and Configure. Also use when a user wants to start building a provider for a new API or asks how to begin a terraform-provider-* project.
— the description by HashiCorp

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a standardized workflow for initializing a new Terraform provider project. It handles the creation of the workspace directory, Go module initialization, and the generation of essential boilerplate files including main.go and the provider configuration. It is designed to help developers quickly set up the foundation for building custom providers for new APIs using the modern Plugin Framework.

What it does

  • Creates a new workspace directory with the required naming convention.
  • Initializes a Go module for the provider project.
  • Downloads the necessary Terraform Plugin Framework dependencies.
  • Generates a main.go file for the provider server.
  • Creates a provider.go file with initial schema and configuration logic.
  • Performs initial validation by running build and test commands.

When to use it

  • When starting a new Terraform provider project from scratch.
  • When you need to integrate a new API into the Terraform ecosystem.
  • When you want to ensure your provider follows standard Plugin Framework project structure.

Advantages

  • Ensures consistent project structure across different provider developments.
  • Automates the repetitive setup tasks for Go modules and file scaffolding.
  • Provides a clean starting point that is ready for immediate development and testing.

Usage examples

“I need to start building a new Terraform provider for the Acme API. How do I begin?”
The agent will create a new directory named terraform-provider-acme, initialize the Go module, and generate the necessary boilerplate files to start development.
“Scaffold a new Terraform provider project for my internal service.”
The agent will set up the workspace, configure the main.go and provider.go files, and run the initial build and test commands to verify the setup.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/hashicorp/new-terraform-provider/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip new-terraform-provider.zip -d .agents/skills

Documentation by HashiCorp

To scaffold a new Terraform provider with Plugin Framework:

  1. If I am already in a Terraform provider workspace, then confirm that I want to create a new workspace. If I do not want to create a new workspace, then skip all remaining steps.

  2. Create a new workspace root directory. The root directory name should be prefixed with "terraform-provider-". Perform all subsequent steps in this new workspace.

  3. Initialize a new Go module.

  4. Run go get -u github.com/hashicorp/terraform-plugin-framework@latest.

  5. Write a main.go file that follows the example.

  6. Write an internal/provider/provider.go file that follows the example. Rename the demo provider, the DEMO_* environment variables, and the authentication attributes to match the target API.

  7. Remove TODO comments from main.go and provider.go.

  8. Run go mod tidy

  9. Run go build -o /dev/null

  10. Run go test ./... The scaffold resolves credentials as explicit config with environment variable fallback. To grow that into a full credential provider chain (shared credentials files, profiles, platform identity, configure-time validation), use the provider-configuration skill (if available). To add the first resource or data source, use the provider-resources skill (if available).

Files

assets/provider.go

// Copyright IBM Corp. 2025, 2026
// SPDX-License-Identifier: MPL-2.0

package provider

import (
	"context"
	"os"

	"github.com/hashicorp/terraform-plugin-framework/datasource"
	"github.com/hashicorp/terraform-plugin-framework/path"
	"github.com/hashicorp/terraform-plugin-framework/provider"
	"github.com/hashicorp/terraform-plugin-framework/provider/schema"
	"github.com/hashicorp/terraform-plugin-framework/resource"
	"github.com/hashicorp/terraform-plugin-framework/types"
)

var _ provider.Provider = &demoProvider{}

// New returns the provider factory consumed by main.go.
func New(version string) func() provider.Provider {
	return func() provider.Provider {
		return &demoProvider{version: version}
	}
}

// TODO: Rename demoProvider (and the "demo" TypeName below) after your provider.
type demoProvider struct {
	version string
}

type demoProviderModel struct {
	Endpoint types.String `tfsdk:"endpoint"`
	APIKey   types.String `tfsdk:"api_key"`
}

func (p *demoProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) {
	// TODO: Update this with your provider's type name. It is the prefix of
	// every resource and data source type (e.g. "demo" -> demo_widget).
	resp.TypeName = "demo"
	resp.Version = p.version
}

func (p *demoProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) {
	resp.Schema = schema.Schema{
		Attributes: map[string]schema.Attribute{
			// Authentication attributes are Optional (never Required) so
			// environment variables can supply them; secrets are Sensitive.
			// TODO: Replace endpoint/api_key and the DEMO_* environment
			// variables with your API's connection settings.
			"endpoint": schema.StringAttribute{
				Optional:            true,
				MarkdownDescription: "API endpoint. May also be set via the `DEMO_ENDPOINT` environment variable.",
			},
			"api_key": schema.StringAttribute{
				Optional:            true,
				Sensitive:           true,
				MarkdownDescription: "API key. May also be set via the `DEMO_API_KEY` environment variable.",
			},
		},
	}
}

func (p *demoProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
	var config demoProviderModel
	resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
	if resp.Diagnostics.HasError() {
		return
	}

	// Values wired to other resources' outputs are unknown during planning;
	// treating them as empty would silently mis-authenticate.
	if config.Endpoint.IsUnknown() {
		resp.Diagnostics.AddAttributeError(
			path.Root("endpoint"),
			"Unknown endpoint",
			"endpoint depends on a value known only after apply. Set a static value or use the DEMO_ENDPOINT environment variable.",
		)
	}
	if config.APIKey.IsUnknown() {
		resp.Diagnostics.AddAttributeError(
			path.Root("api_key"),
			"Unknown API key",
			"api_key depends on a value known only after apply. Set a static value or use the DEMO_API_KEY environment variable.",
		)
	}
	if resp.Diagnostics.HasError() {
		return
	}

	// Explicit configuration wins; environment variables are the fallback.
	endpoint := config.Endpoint.ValueString()
	if endpoint == "" {
		endpoint = os.Getenv("DEMO_ENDPOINT")
	}
	apiKey := config.APIKey.ValueString()
	if apiKey == "" {
		apiKey = os.Getenv("DEMO_API_KEY")
	}

	if endpoint == "" {
		resp.Diagnostics.AddAttributeError(
			path.Root("endpoint"),
			"Missing endpoint",
			"Set endpoint in the provider block or export DEMO_ENDPOINT.",
		)
	}
	if apiKey == "" {
		resp.Diagnostics.AddAttributeError(
			path.Root("api_key"),
			"Missing API key",
			"Set api_key in the provider block or export DEMO_API_KEY.",
		)
	}
	if resp.Diagnostics.HasError() {
		return
	}

	// TODO: Construct your API client here and hand it to resources and data
	// sources. They receive it in their Configure methods via ProviderData.
	//
	//   client := demoapi.NewClient(endpoint, apiKey)
	//   resp.ResourceData = client
	//   resp.DataSourceData = client
	_ = endpoint
	_ = apiKey
}

func (p *demoProvider) Resources(_ context.Context) []func() resource.Resource {
	return []func() resource.Resource{
		// TODO: Register resource constructors here, e.g. NewWidgetResource.
	}
}

func (p *demoProvider) DataSources(_ context.Context) []func() datasource.DataSource {
	return []func() datasource.DataSource{
		// TODO: Register data source constructors here.
	}
}

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Security

What our scanner noticed· Low risk

  • Scripts that your agent may run

This skill includes scripts that your agent may run. Read them before you use it.

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Published by
HashiCorp
Publisher
HashiCorp (Official)
Source
hashicorp/agent-skills · plugins/terraform/skills/new-terraform-provider · commit 5e30fa5
Licence
Mozilla Public License 2.0 (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by HashiCorp or HashiCorp.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download new-terraform-provider.zip