Hi @maastha
I’ve just tried to reproduce the issue you describe but I’m unable to do so with the following code and configuration.
main.go
func main() {
ctx := context.Background()
var debug bool
flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve")
flag.Parse()
providers := []func() tfprotov5.ProviderServer{
providerserver.NewProtocol5(provider.New()), // Example terraform-plugin-framework provider
provider_sdk.Provider().GRPCProvider, // Example terraform-plugin-sdk provider
}
muxServer, err := tf5muxserver.NewMuxServer(ctx, providers...)
if err != nil {
log.Fatal(err)
}
var serveOpts []tf5server.ServeOpt
if debug {
serveOpts = append(serveOpts, tf5server.WithManagedDebug())
}
err = tf5server.Serve(
"registry.terraform.io/bendbennett/playground",
muxServer.ProviderServer,
serveOpts...,
)
if err != nil {
log.Fatal(err)
}
}
provider_sdk/provider.go
func Provider() *schema.Provider {
return &schema.Provider{
ResourcesMap: map[string]*schema.Resource{
"playground_example": exampleSdkResource(),
},
}
}
provider_sdk/example_sdk_resource.go
func exampleSdkResource() *schema.Resource {
return &schema.Resource{
CreateContext: create,
ReadContext: read,
UpdateContext: update,
DeleteContext: schema.NoopContext,
Importer: &schema.ResourceImporter{
StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
return nil, nil
},
},
Schema: map[string]*schema.Schema{
"attr_settings": {
Type: schema.TypeBool,
Optional: true,
Default: true,
},
"id": {
Computed: true,
Type: schema.TypeString,
},
},
}
}
func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
d.SetId("example-id")
return nil
}
func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
return nil
}
func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
return nil
}
Using the following configuration and executing terraform apply produces the following CLI output and state file:
terraform {
required_providers {
playground = {
source = "bendbennett/playground"
}
}
}
resource "playground_example" "example" {
}
Terraform will perform the following actions:
# playground_example.example will be created
+ resource "playground_example" "example" {
+ attr_settings = true
+ id = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
"resources": [
{
"mode": "managed",
"type": "playground_example",
"name": "example",
"provider": "provider[\"registry.terraform.io/bendbennett/playground\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
"attr_settings": true,
"id": "example-id"
},
"sensitive_attributes": [],
"private": "bnVsbA=="
}
]
}
],
Switching to using a framework-based implementation of the provider and resource.
provider/provider.go
var _ provider.Provider = &playgroundProvider{}
type playgroundProvider struct{}
func (p *playgroundProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) {
resp.TypeName = "playground"
}
func (p *playgroundProvider) Schema(context.Context, provider.SchemaRequest, *provider.SchemaResponse) {
}
func (p *playgroundProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
}
func (p *playgroundProvider) Resources(ctx context.Context) []func() resource.Resource {
return []func() resource.Resource{
NewResource,
}
}
func (p *playgroundProvider) DataSources(context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{
NewDatasource,
}
}
func New() provider.Provider {
return &playgroundProvider{}
}
provider/example_resource.go
var _ resource.Resource = (*exampleResource)(nil)
var _ resource.ResourceWithImportState = (*exampleResource)(nil)
func (r *exampleResource) Schema(ctx context.Context, request resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"attr_settings": schema.BoolAttribute{
Optional: true,
Computed: true,
Default: booldefault.StaticBool(true),
},
"id": schema.StringAttribute{
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
},
}
}
func NewResource() resource.Resource {
return &exampleResource{}
}
type exampleResourceData struct {
AttrSettings types.Bool `tfsdk:"attr_settings"`
Id types.String `tfsdk:"id"`
}
type exampleResource struct {
provider playgroundProvider
}
func (r *exampleResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_example"
}
func (r *exampleResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data exampleResourceData
diags := req.Plan.Get(ctx, &data)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
data.Id = types.StringValue("example-id")
diags = resp.State.Set(ctx, &data)
resp.Diagnostics.Append(diags...)
}
func (r *exampleResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var data exampleResourceData
diags := req.State.Get(ctx, &data)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
diags = resp.State.Set(ctx, &data)
resp.Diagnostics.Append(diags...)
}
func (r *exampleResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var data exampleResourceData
diags := req.Plan.Get(ctx, &data)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
diags = resp.State.Set(ctx, &data)
resp.Diagnostics.Append(diags...)
}
func (r *exampleResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var data exampleResourceData
diags := req.State.Get(ctx, &data)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
func (r *exampleResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
}
Using the same configuration as described above and executing terraform apply produces the following CLI output and state file:
No changes. Your infrastructure matches the configuration.
"resources": [
{
"mode": "managed",
"type": "playground_example",
"name": "example",
"provider": "provider[\"registry.terraform.io/bendbennett/playground\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
"attr_settings": true,
"id": "example-id"
},
"sensitive_attributes": []
}
]
}
],
Could there perhaps be something missing from the code that you supplied which is causing the in-place update that you describe?