| name | provider-resources |
| description | Implement Terraform Provider resources and data sources using the Plugin Framework. Use when developing CRUD operations, schema design, state management, and acceptance testing for provider resources. |
| metadata | {"copyright":"Copyright IBM Corp. 2026","version":"0.0.1"} |
Provider Resources Implementation
References: Plugin Framework · Resources · Data Sources
File Structure
internal/service/<service>/
├── <resource_name>.go # Resource implementation
├── <resource_name>_test.go # Acceptance tests
├── <resource_name>_data_source.go # Data source (if applicable)
├── find.go # Finder functions
├── exports_test.go # Test exports
└── service_package_gen.go # Auto-generated registration
website/docs/r/<service>_<resource_name>.html.markdown # Resource docs
website/docs/d/<service>_<resource_name>.html.markdown # Data source docs
Resource Structure
Plugin Framework Pattern (preferred)
type resourceExample struct {
framework.ResourceWithConfigure
}
func (r *resourceExample) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_example"
}
func (r *resourceExample) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"id": framework.IDAttribute(),
"name": schema.StringAttribute{
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
Validators: []validator.String{
stringvalidator.LengthBetween(1, 255),
},
},
"arn": schema.StringAttribute{
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
},
}
}
SDKv2 Pattern (legacy — use only when extending existing SDKv2 resources)
func ResourceExample() *schema.Resource {
return &schema.Resource{
CreateWithoutTimeout: resourceExampleCreate,
ReadWithoutTimeout: resourceExampleRead,
UpdateWithoutTimeout: resourceExampleUpdate,
DeleteWithoutTimeout: resourceExampleDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Schema: map[string]*schema.Schema{
},
CustomizeDiff: verify.SetTagsDiff,
}
}
CRUD Operations
Each resource implements Create, Read, Update, and Delete methods.
Key patterns:
- Create: deserialize plan → call API → set ID and computed fields → save state
- Read: load state → call finder → handle NotFound by removing from state → update state
- Update: compare plan vs state → call API for changed fields → save state
- Delete: load state → call API → handle NotFound gracefully
See references/crud-operations.md for complete code examples.
Schema Design
Attribute Types
| Terraform Type | Framework Type | Use Case |
|---|
string | schema.StringAttribute | Names, ARNs, IDs |
number | schema.Int64Attribute, schema.Float64Attribute | Counts, sizes |
bool | schema.BoolAttribute | Feature flags |
list | schema.ListAttribute | Ordered collections |
set | schema.SetAttribute | Unordered unique items |
map | schema.MapAttribute | Key-value pairs |
object | schema.SingleNestedAttribute | Complex nested config |
Plan Modifiers
stringplanmodifier.RequiresReplace()
stringplanmodifier.UseStateForUnknown()
stringplanmodifier.RequiresReplaceIf(
func(ctx context.Context, req planmodifier.StringRequest, resp *stringplanmodifier.RequiresReplaceIfFuncResponse) {
},
"description",
"markdown description",
)
Validators
stringvalidator.LengthBetween(1, 255)
stringvalidator.RegexMatches(regexp.MustCompile(`^[a-z0-9-]+$`), "must be lowercase alphanumeric with hyphens")
stringvalidator.OneOf("option1", "option2", "option3")
int64validator.Between(1, 100)
int64validator.AtLeast(1)
int64validator.AtMost(1000)
listvalidator.SizeAtLeast(1)
listvalidator.SizeAtMost(10)
Sensitive Attributes
Mark attributes containing secrets as Sensitive: true to prevent values from appearing in logs and plan output.
"password": schema.StringAttribute{
Required: true,
Sensitive: true,
Validators: []validator.String{
stringvalidator.LengthAtLeast(8),
},
}
State Management
Finder Functions
Wrap API Get calls in a finder that returns retry.NotFoundError when the resource is missing, enabling consistent NotFound handling across CRUD operations.
State Waiters
For async resources, use retry.StateChangeConf to poll until the resource reaches a target state.
See references/state-management.md for implementation examples.
Error Handling
API Error Matching
var notFound *types.ResourceNotFoundException
if errors.As(err, ¬Found) { }
var conflict *types.ConflictException
if errors.As(err, &conflict) { }
var throttle *types.ThrottlingException
if errors.As(err, &throttle) { }
Diagnostics
resp.Diagnostics.AddError("Error creating resource",
fmt.Sprintf("Could not create resource: %s", err))
resp.Diagnostics.AddWarning("Resource modified outside Terraform",
"State may be inconsistent")
resp.Diagnostics.AddAttributeError(path.Root("name"),
"Invalid name", "Name must be lowercase alphanumeric")
Testing
Run acceptance tests with TF_ACC=1:
go test -c -o /dev/null ./internal/service/<service>
TF_ACC=1 go test ./internal/service/<service> -run TestAccExample -v -timeout 60m
TF_ACC=1 go test ./internal/service/<service> -sweep=<region> -v
Every resource needs:
- Basic acceptance test with import state verification
- Disappears test (
acctest.CheckResourceDisappears)
testAccCheckExampleExists and testAccCheckExampleDestroy helpers
See references/testing-examples.md for full test code.
Documentation
Each resource and data source requires an HTML markdown doc with: Example Usage, Argument Reference, Attribute Reference, Import.
See references/documentation-template.md for the standard template.
Pre-Submission Checklist
External References