ワンクリックで
scp-terraform-builder
Generate Terraform resource/data source code for Samsung Cloud Platform provider with exact project conventions
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Generate Terraform resource/data source code for Samsung Cloud Platform provider with exact project conventions
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
| name | scp-terraform-builder |
| description | Generate Terraform resource/data source code for Samsung Cloud Platform provider with exact project conventions |
| triggers | ["terraform","scp","resource","datasource","provider"] |
| argument-hint | <service-name> <resource-name> |
Generate exact, compilable Terraform provider code for Samsung Cloud Platform (SCP) following every convention in the terraform-provider-samsungcloudplatformv2 codebase.
terraform-provider-samsungcloudplatformv2/
samsungcloudplatform/
provider.go # Provider definition
service/
service.go # Central registry (ResourceConstructors, DataSourceConstructors)
<service>/ # Resource & data source implementations
<resource>.go # Resource (CRUD)
<resource>_datasource.go # Data source (list, plural TypeName)
client/
client.go # SCPClient struct with all service clients
common.go # Instance struct, GetDetailFromError()
<service>/
<service>.go # API client wrapper (CRUD methods)
model.go # Terraform model structs (tfsdk tags)
common/
common.go # ToSnakeCase(), state constants
tag/tag.go # tag.ResourceSchema(), tag.DataSourceSchema()
docs/resources/ # Resource documentation
docs/data-sources/ # Data source documentation
examples/ # Example HCL configurations
File: samsungcloudplatform/client/<service>/<service>.go
package <service>
import (
"context"
"net/http"
scpsdk "github.com/SamsungSDSCloud/terraform-sdk-samsungcloudplatformv2/v3/client"
scp<service> "github.com/SamsungSDSCloud/terraform-sdk-samsungcloudplatformv2/v3/library/<service>/<version>"
)
type Client struct {
Config *scpsdk.Configuration
sdkClient *scp<service>.APIClient
}
func NewClient(config *scpsdk.Configuration) *Client {
return &Client{
Config: config,
sdkClient: scp<service>.NewAPIClient(config),
}
}
// Create
func (client *Client) Create<Resource>(ctx context.Context, request <Resource>Resource) (*scp<service>.<Resource>ShowResponse, error) {
req := client.sdkClient.<ApiGroup>API.Create<Resource>(ctx)
req = req.<Resource>CreateRequest(scp<service>.<Resource>CreateRequest{
Name: request.Name.ValueString(),
Description: *scp<service>.NewNullableString(request.Description.ValueStringPointer()),
})
resp, _, err := req.Execute()
return resp, err
}
// Read (single)
func (client *Client) Get<Resource>(ctx context.Context, id string) (*scp<service>.<Resource>ShowResponse, error) {
req := client.sdkClient.<ApiGroup>API.Show<Resource>(ctx, id)
resp, _, err := req.Execute()
return resp, err
}
// Read (list)
func (client *Client) Get<Resource>List(ctx context.Context, request <Resource>DataSource) (*scp<service>.<Resource>ListResponse, error) {
req := client.sdkClient.<ApiGroup>API.List<Resource>s(ctx)
if !request.Limit.IsNull() {
req = req.Limit(request.Limit.ValueInt32())
}
if !request.Marker.IsNull() {
req = req.Marker(request.Marker.ValueString())
}
if !request.Sort.IsNull() {
req = req.Sort(request.Sort.ValueString())
}
resp, _, err := req.Execute()
return resp, err
}
// Update
func (client *Client) Update<Resource>(ctx context.Context, id string, request <Resource>Resource) (*scp<service>.<Resource>ShowResponse, error) {
req := client.sdkClient.<ApiGroup>API.Set<Resource>(ctx, id)
req = req.<Resource>SetRequest(scp<service>.<Resource>SetRequest{
Description: *scp<service>.NewNullableString(request.Description.ValueStringPointer()),
})
resp, _, err := req.Execute()
return resp, err
}
// Delete
func (client *Client) Delete<Resource>(ctx context.Context, id string) error {
req := client.sdkClient.<ApiGroup>API.Delete<Resource>(ctx, id)
_, err := req.Execute()
return err
}
File: samsungcloudplatform/client/<service>/model.go
package <service>
import (
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/types"
)
// Resource model - maps to Terraform resource schema via tfsdk tags
type <Resource>Resource struct {
Id types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
Description types.String `tfsdk:"description"`
Tags types.Map `tfsdk:"tags"`
<Resource> types.Object `tfsdk:"<resource>"` // computed nested output
}
// DataSource model - maps to data source schema (list query)
type <Resource>DataSource struct {
Limit types.Int32 `tfsdk:"limit"`
Marker types.String `tfsdk:"marker"`
Sort types.String `tfsdk:"sort"`
Name types.String `tfsdk:"name"`
State types.String `tfsdk:"state"`
<Resource>s []<Resource>Item `tfsdk:"<resource>s"`
}
// Nested computed object (response model)
type <Resource>Item struct {
Id types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
State types.String `tfsdk:"state"`
Description types.String `tfsdk:"description"`
CreatedAt types.String `tfsdk:"created_at"`
CreatedBy types.String `tfsdk:"created_by"`
ModifiedAt types.String `tfsdk:"modified_at"`
ModifiedBy types.String `tfsdk:"modified_by"`
}
// AttributeTypes is REQUIRED for types.Object conversion via types.ObjectValueFrom()
func (m <Resource>Item) AttributeTypes() map[string]attr.Type {
return map[string]attr.Type{
"id": types.StringType,
"name": types.StringType,
"state": types.StringType,
"description": types.StringType,
"created_at": types.StringType,
"created_by": types.StringType,
"modified_at": types.StringType,
"modified_by": types.StringType,
}
}
Key rules:
tfsdk tag values must be snake_case and match schema attribute keys exactlytypes.Object in parent, require AttributeTypes() methodtypes.StringPointerValue(field.Get())types.StringValue(field)types.StringValue(string(field))types.StringValue(field.Format(time.RFC3339))File: samsungcloudplatform/client/client.go
Add to SCPClient struct:
<ServiceName> *<service>.Client
Add to NewSCPClient():
<ServiceName>: <service>.NewClient(NewDefaultConfig(providerConfig, <service>.ServiceType)),
Add import:
"github.com/SamsungSDSCloud/terraform-provider-samsungcloudplatformv2/v3/samsungcloudplatform/client/<service>"
File: samsungcloudplatform/service/<service>/<resource>.go
package <service>
import (
"context"
"fmt"
"time"
"github.com/SamsungSDSCloud/terraform-provider-samsungcloudplatformv2/v3/samsungcloudplatform/client"
"github.com/SamsungSDSCloud/terraform-provider-samsungcloudplatformv2/v3/samsungcloudplatform/client/<service>"
"github.com/SamsungSDSCloud/terraform-provider-samsungcloudplatformv2/v3/samsungcloudplatform/common"
"github.com/SamsungSDSCloud/terraform-provider-samsungcloudplatformv2/v3/samsungcloudplatform/common/tag"
scpsdk "github.com/SamsungSDSCloud/terraform-sdk-samsungcloudplatformv2/v3/client"
scp<service> "github.com/SamsungSDSCloud/terraform-sdk-samsungcloudplatformv2/v3/library/<service>/<version>"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/types"
)
// Interface compliance check
var (
_ resource.Resource = &<service><Resource>Resource{}
_ resource.ResourceWithConfigure = &<service><Resource>Resource{}
)
func New<Service><Resource>Resource() resource.Resource {
return &<service><Resource>Resource{}
}
type <service><Resource>Resource struct {
config *scpsdk.Configuration
client *<service>.Client
clients *client.SCPClient
}
func (r *<service><Resource>Resource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_<service>_<resource>"
}
func (r *<service><Resource>Resource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "<resource> description",
Attributes: map[string]schema.Attribute{
// Tags - use shared helper
"tags": tag.ResourceSchema(),
// ID - always computed, preserve on plan
"id": schema.StringAttribute{
Description: "Identifier of the resource.",
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
// Required field
common.ToSnakeCase("Name"): schema.StringAttribute{
Description: "Name of the resource.",
Required: true,
},
// Optional field with default
common.ToSnakeCase("Description"): schema.StringAttribute{
Description: "Description",
Optional: true,
Computed: true,
Default: stringdefault.StaticString(""),
},
// Immutable field (forces recreation)
// common.ToSnakeCase("Type"): schema.StringAttribute{
// Required: true,
// PlanModifiers: []planmodifier.String{
// stringplanmodifier.RequiresReplace(),
// },
// },
// Computed nested object (API response)
common.ToSnakeCase("<Resource>"): schema.SingleNestedAttribute{
Description: "<Resource> details",
Computed: true,
Attributes: map[string]schema.Attribute{
common.ToSnakeCase("Id"): schema.StringAttribute{Computed: true, Description: "Id"},
common.ToSnakeCase("Name"): schema.StringAttribute{Computed: true, Description: "Name"},
common.ToSnakeCase("State"): schema.StringAttribute{Computed: true, Description: "State"},
common.ToSnakeCase("Description"): schema.StringAttribute{Computed: true, Description: "Description"},
common.ToSnakeCase("CreatedAt"): schema.StringAttribute{Computed: true, Description: "CreatedAt"},
common.ToSnakeCase("CreatedBy"): schema.StringAttribute{Computed: true, Description: "CreatedBy"},
common.ToSnakeCase("ModifiedAt"): schema.StringAttribute{Computed: true, Description: "ModifiedAt"},
common.ToSnakeCase("ModifiedBy"): schema.StringAttribute{Computed: true, Description: "ModifiedBy"},
},
},
},
}
}
func (r *<service><Resource>Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
if req.ProviderData == nil {
return
}
inst, ok := req.ProviderData.(client.Instance)
if !ok {
resp.Diagnostics.AddError(
"Unexpected Data Source Configure Type",
fmt.Sprintf("Expected *client.Instance, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)
return
}
r.client = inst.Client.<ServiceName>
r.clients = inst.Client
}
func (r *<service><Resource>Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var plan <service>.<Resource>Resource
diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
data, err := r.client.Create<Resource>(ctx, plan)
if err != nil {
detail := client.GetDetailFromError(err)
resp.Diagnostics.AddError(
"Error creating <resource>",
"Could not create <resource>, unexpected error: "+err.Error()+"\nReason: "+detail,
)
return
}
plan.Id = types.StringValue(data.<Resource>.Id)
model := create<Resource>Model(data)
objValue, diags := types.ObjectValueFrom(ctx, model.AttributeTypes(), model)
plan.<Resource> = objValue
diags = resp.State.Set(ctx, plan)
resp.Diagnostics.Append(diags...)
}
func (r *<service><Resource>Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var state <service>.<Resource>Resource
diags := req.State.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
data, err := r.client.Get<Resource>(ctx, state.Id.ValueString())
if err != nil {
detail := client.GetDetailFromError(err)
resp.Diagnostics.AddError(
"Error Reading <resource>",
"Could not read <resource> ID "+state.Id.ValueString()+": "+err.Error()+"\nReason: "+detail,
)
return
}
model := create<Resource>Model(data)
objValue, diags := types.ObjectValueFrom(ctx, model.AttributeTypes(), model)
state.<Resource> = objValue
diags = resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
}
func (r *<service><Resource>Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var state <service>.<Resource>Resource
diags := req.Plan.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
_, err := r.client.Update<Resource>(ctx, state.Id.ValueString(), state)
if err != nil {
detail := client.GetDetailFromError(err)
resp.Diagnostics.AddError(
"Error Updating <resource>",
"Could not update <resource>, unexpected error: "+err.Error()+"\nReason: "+detail,
)
return
}
// Re-read after update (update response may not have all fields)
data, err := r.client.Get<Resource>(ctx, state.Id.ValueString())
if err != nil {
detail := client.GetDetailFromError(err)
resp.Diagnostics.AddError(
"Error Reading <resource>",
"Could not read <resource> ID "+state.Id.ValueString()+": "+err.Error()+"\nReason: "+detail,
)
return
}
model := create<Resource>Model(data)
objValue, diags := types.ObjectValueFrom(ctx, model.AttributeTypes(), model)
state.<Resource> = objValue
diags = resp.State.Set(ctx, state)
resp.Diagnostics.Append(diags...)
}
func (r *<service><Resource>Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var state <service>.<Resource>Resource
diags := req.State.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
err := r.client.Delete<Resource>(ctx, state.Id.ValueString())
if err != nil {
detail := client.GetDetailFromError(err)
resp.Diagnostics.AddError(
"Error Deleting <resource>",
"Could not delete <resource>, unexpected error: "+err.Error()+"\nReason: "+detail,
)
return
}
}
// Convert SDK response to Terraform model
func create<Resource>Model(data *scp<service>.<Resource>ShowResponse) <service>.<Resource>Item {
return <service>.<Resource>Item{
Id: types.StringValue(data.Get<Resource>().Id),
Name: types.StringValue(data.Get<Resource>().Name),
State: types.StringValue(string(data.Get<Resource>().State)),
Description: types.StringPointerValue(data.Get<Resource>().Description.Get()),
CreatedAt: types.StringValue(data.Get<Resource>().CreatedAt.Format(time.RFC3339)),
CreatedBy: types.StringValue(data.Get<Resource>().CreatedBy),
ModifiedAt: types.StringValue(data.Get<Resource>().ModifiedAt.Format(time.RFC3339)),
ModifiedBy: types.StringValue(data.Get<Resource>().ModifiedBy),
}
}
File: samsungcloudplatform/service/<service>/<resource>_datasource.go
package <service>
import (
"context"
"fmt"
"time"
"github.com/SamsungSDSCloud/terraform-provider-samsungcloudplatformv2/v3/samsungcloudplatform/client"
"github.com/SamsungSDSCloud/terraform-provider-samsungcloudplatformv2/v3/samsungcloudplatform/client/<service>"
"github.com/SamsungSDSCloud/terraform-provider-samsungcloudplatformv2/v3/samsungcloudplatform/common"
scpsdk "github.com/SamsungSDSCloud/terraform-sdk-samsungcloudplatformv2/v3/client"
"github.com/hashicorp/terraform-plugin-framework-validators/int32validator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
)
var (
_ datasource.DataSource = &<service><Resource>DataSource{}
_ datasource.DataSourceWithConfigure = &<service><Resource>DataSource{}
)
func New<Service><Resource>DataSource() datasource.DataSource {
return &<service><Resource>DataSource{}
}
type <service><Resource>DataSource struct {
config *scpsdk.Configuration
client *<service>.Client
clients *client.SCPClient
}
// TypeName for data sources is PLURAL (e.g., "_vpc_publicips")
func (d *<service><Resource>DataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_<service>_<resource>s"
}
func (d *<service><Resource>DataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "list of <resource>.",
Attributes: map[string]schema.Attribute{
// Standard list query parameters
common.ToSnakeCase("Limit"): schema.Int32Attribute{
Description: "Limit \n - maximum : 10000 \n - minimum : 1",
Optional: true,
Validators: []validator.Int32{int32validator.Between(1, 10000)},
},
common.ToSnakeCase("Marker"): schema.StringAttribute{
Description: "Marker \n - maxLength : 64 \n - minLength : 1",
Optional: true,
Validators: []validator.String{stringvalidator.LengthBetween(1, 64)},
},
common.ToSnakeCase("Sort"): schema.StringAttribute{
Description: "Sort \n - example : created_at:desc",
Optional: true,
},
// Filter parameters (service-specific)
common.ToSnakeCase("Name"): schema.StringAttribute{
Description: "Name filter",
Optional: true,
},
common.ToSnakeCase("State"): schema.StringAttribute{
Description: "State filter",
Optional: true,
},
// Result list (computed)
common.ToSnakeCase("<Resource>s"): schema.ListNestedAttribute{
Description: "A list of <resource>.",
Computed: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
common.ToSnakeCase("Id"): schema.StringAttribute{Computed: true, Description: "Id"},
common.ToSnakeCase("Name"): schema.StringAttribute{Computed: true, Description: "Name"},
common.ToSnakeCase("State"): schema.StringAttribute{Computed: true, Description: "State"},
common.ToSnakeCase("Description"): schema.StringAttribute{Computed: true, Description: "Description"},
common.ToSnakeCase("CreatedAt"): schema.StringAttribute{Computed: true, Description: "CreatedAt"},
common.ToSnakeCase("CreatedBy"): schema.StringAttribute{Computed: true, Description: "CreatedBy"},
common.ToSnakeCase("ModifiedAt"): schema.StringAttribute{Computed: true, Description: "ModifiedAt"},
common.ToSnakeCase("ModifiedBy"): schema.StringAttribute{Computed: true, Description: "ModifiedBy"},
},
},
},
},
}
}
func (d *<service><Resource>DataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
if req.ProviderData == nil {
return
}
inst, ok := req.ProviderData.(client.Instance)
if !ok {
resp.Diagnostics.AddError(
"Unexpected Data Source Configure Type",
fmt.Sprintf("Expected *client.Instance, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)
return
}
d.client = inst.Client.<ServiceName>
d.clients = inst.Client
}
func (d *<service><Resource>DataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var state <service>.<Resource>DataSource
diags := req.Config.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
data, err := d.client.Get<Resource>List(ctx, state)
if err != nil {
detail := client.GetDetailFromError(err)
resp.Diagnostics.AddError(
"Error reading <resource>",
"Could not read <resource>, unexpected error: "+err.Error()+"\nReason: "+detail,
)
return
}
for _, item := range data.<Resource>s {
state.<Resource>s = append(state.<Resource>s, <service>.<Resource>Item{
Id: types.StringValue(item.Id),
Name: types.StringValue(item.Name),
State: types.StringValue(string(item.State)),
Description: types.StringPointerValue(item.Description.Get()),
CreatedAt: types.StringValue(item.CreatedAt.Format(time.RFC3339)),
CreatedBy: types.StringValue(item.CreatedBy),
ModifiedAt: types.StringValue(item.ModifiedAt.Format(time.RFC3339)),
ModifiedBy: types.StringValue(item.ModifiedBy),
})
}
diags = resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
}
File: samsungcloudplatform/service/service.go
Add import:
"github.com/SamsungSDSCloud/terraform-provider-samsungcloudplatformv2/v3/samsungcloudplatform/service/<service>"
Add to ResourceConstructors:
<service>.New<Service><Resource>Resource,
Add to DataSourceConstructors:
<service>.New<Service><Resource>DataSource,
common.ToSnakeCase("CamelCaseName") for attribute keys (e.g., common.ToSnakeCase("IpAddress") -> "ip_address")"id" and "tags" are written directly as lowercase| SDK Type | Terraform Conversion |
|---|---|
string (required) | types.StringValue(field) |
NullableString (optional) | types.StringPointerValue(field.Get()) |
| Enum type | types.StringValue(string(field)) |
time.Time | types.StringValue(field.Format(time.RFC3339)) |
NullableEnum | Cast via Get() then string(*) |
Always use client.GetDetailFromError(err) to extract API error details:
if err != nil {
detail := client.GetDetailFromError(err)
resp.Diagnostics.AddError(
"Error <verb>ing <resource>",
"Could not <verb> <resource>, unexpected error: "+err.Error()+"\nReason: "+detail,
)
return
}
After calling Update API, always re-read via Get API because update responses may not have all fields populated.
Use types.ObjectValueFrom() with the model's AttributeTypes():
model := createModel(data)
objValue, diags := types.ObjectValueFrom(ctx, model.AttributeTypes(), model)
state.NestedField = objValue
"tags": tag.ResourceSchema()"tags": tag.DataSourceSchema()convertToTags(request.Tags.Elements())stringplanmodifier.UseStateForUnknown() - for id field (preserve across plans)stringplanmodifier.RequiresReplace() - for immutable fields (forces recreation)r receiver: func (r *myResource) Create(...)d receiver: func (d *myDataSource) Read(...)This provider does NOT implement ImportState. Do not add it.
Always "samsungcloudplatformv2" - the prefix for all resource/data source type names.
Each service has a specific SDK version (e.g., VPC uses 1.1, IAM uses 1.2). Check AllowSDKDefaultVersion in client/client.go for the correct version.
samsungcloudplatform/client/<service>/<service>.go - Client with CRUD methodssamsungcloudplatform/client/<service>/model.go - Model structs with tfsdk tags and AttributeTypes()samsungcloudplatform/client/client.go - Add field to SCPClient struct + init in NewSCPClient()samsungcloudplatform/service/<service>/<resource>.go - Resource implementationsamsungcloudplatform/service/<service>/<resource>_datasource.go - Data source implementationsamsungcloudplatform/service/service.go - Register constructors in both slicesgo build ./... passes