| name | review-schema |
| description | Review Terraform AWS Provider Plugin Framework schema shape: model structs, Required/Optional/Computed combinations, plan modifiers (no ForceNew), nested-block custom types, validators, sensitive attributes, and timeouts. Use when reviewing a PR that changes resource or data-source schema in internal/service/**/*.go. |
Review: Schema & Resource Shape
Assume the @maintainer persona. Scope: model struct, attributes, blocks, validators, plan modifiers, timeouts on non-test resource code. Loaded from review-pr.
Model struct
type fooResourceModel struct {
framework.WithRegionModel
Timeouts timeouts.Value `tfsdk:"timeouts"`
}
framework.WithRegionModel is the first embedded field for regional resources/data sources/list resources. Global services (CloudFront, IAM, Route 53 hosted zones, etc.) omit it and register with inttypes.ResourceRegionDisabled().
tfsdk tags must match schema attribute names exactly (snake_case).
- Use Plugin Framework types (
types.String, etc.) and fwtypes.ListNestedObjectValueOf[T] for nested blocks.
- Time fields use
timetypes.RFC3339, never types.String.
Attribute rules
- Attribute names are
snake_case.
- Alphabetize within
Attributes and within Blocks. No blank lines between attributes.
- Use
internal/names constants (names.AttrARN, names.AttrName, names.AttrTags, names.AttrTagsAll, names.AttrTimeouts, etc.) instead of string literals when one exists.
Required / Optional / Computed
Only four valid combinations. Flag any other.
| Required | Optional | Computed | When |
|---|
| ✓ | | | User must supply |
| ✓ | | User may supply; never use Default here |
| | ✓ | Read-only |
| ✓ | ✓ | User or provider supplies; required pairing for Default |
Prefer Optional + Computed over Optional + Default when AWS supplies a server-side default.
id attribute
Plugin Framework does not require an id attribute. Include names.AttrID: framework.IDAttribute() only when the AWS API returns an Id field. Flag schemas that include it on ARN-identified resources just by habit.
Plan modifiers (no ForceNew)
Plugin Framework uses plan modifiers, not ForceNew. Flag any new Framework code that mentions ForceNew.
stringplanmodifier.RequiresReplace() — replaces ForceNew: true
stringplanmodifier.UseStateForUnknown() — keeps a computed value stable across plans
Validators replace MaxItems/MinItems
Use listvalidator.SizeAtMost(N) / SizeAtLeast(N) (and setvalidator equivalents) on the block, not MaxItems/MinItems on the attribute. For a single nested object, combine listvalidator.SizeAtMost(1) with a list nested block.
Nested blocks need a custom type
schema.ListNestedBlock requires CustomType: fwtypes.NewListNestedObjectTypeOf[complexArgumentModel](ctx). The model field is fwtypes.ListNestedObjectValueOf[complexArgumentModel]. Flag nested blocks that omit CustomType.
For sets of strings, use CustomType: fwtypes.SetOfStringType with fwtypes.SetValueOf[types.String].
For AWS API map blocks, the provider schema has no map-of-objects type — use MapBlockKey. Flag attempts to model AWS map blocks with schema.MapAttribute.
Sensitive attributes
Mark credentials, tokens, and write-only secrets Sensitive: true. Flag computed or required attributes that obviously hold secret material but aren't marked sensitive.
Timeouts
Resources with configurable timeouts use timeouts.Block(ctx, timeouts.Opts{Create: true, Update: true, Delete: true}) and set defaults via r.SetDefaultCreateTimeout / SetDefaultUpdateTimeout / SetDefaultDeleteTimeout that reflect AWS API behavior — RDS Create can take 30–60 minutes; an EC2 SG takes seconds. Don't use arbitrary round numbers. Data sources and list resources do not expose configurable timeouts.