Skip to content

Commit 86877a0

Browse files
SBGoodsansgarm
andauthored
Implement GenerateResourceConfig RPC and existing Terraform default logic (#1559)
* Duplicate the Core logic for default config generation * Add block to test case * Enable the `GenerateResourceConfig` server capability * Update go.mod version to `1.24.0` * Update go.mod version to `1.24.0` * run golangci-lint run --fix * Apply suggestion from @ansgarm Co-authored-by: Ansgar Mertens <ansgar@hashicorp.com> * Add descriptive error for `GenerateResourceConfig` * Add test cases for nested blocks * Add diagnostic for null current state * Add changelog note * Update changelog with Terraform Core version * Update terraform-plugin-go dependency to v0.31.0 * Update `go.sum` --------- Co-authored-by: Ansgar Mertens <ansgar@hashicorp.com>
1 parent 3643171 commit 86877a0

File tree

7 files changed

+786
-6
lines changed

7 files changed

+786
-6
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
kind: NOTES
2+
body: 'This release moves the [resource configuration generation](https://developer.hashicorp.com/terraform/language/import/generating-configuration) logic for the `-generate-config-out` flag from
3+
Terraform Core to the SDKv2 for Terraform `v1.14.0` and above. There should be no functionality changes for resource configuration generation in this release.'
4+
time: 2026-03-04T13:46:17.250227-05:00
5+
custom:
6+
Issue: "1559"

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ require (
1414
github.com/hashicorp/logutils v1.0.0
1515
github.com/hashicorp/terraform-exec v0.25.0
1616
github.com/hashicorp/terraform-json v0.27.2
17-
github.com/hashicorp/terraform-plugin-go v0.30.0
17+
github.com/hashicorp/terraform-plugin-go v0.31.0
1818
github.com/hashicorp/terraform-plugin-log v0.10.0
1919
github.com/mitchellh/copystructure v1.2.0
2020
github.com/mitchellh/go-testing-interface v1.14.1
@@ -56,7 +56,7 @@ require (
5656
golang.org/x/tools v0.41.0 // indirect
5757
google.golang.org/appengine v1.6.8 // indirect
5858
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
59-
google.golang.org/grpc v1.79.1 // indirect
59+
google.golang.org/grpc v1.79.2 // indirect
6060
google.golang.org/protobuf v1.36.11 // indirect
6161
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
6262
)

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ github.com/hashicorp/terraform-exec v0.25.0 h1:Bkt6m3VkJqYh+laFMrWIpy9KHYFITpOyz
8484
github.com/hashicorp/terraform-exec v0.25.0/go.mod h1:dl9IwsCfklDU6I4wq9/StFDp7dNbH/h5AnfS1RmiUl8=
8585
github.com/hashicorp/terraform-json v0.27.2 h1:BwGuzM6iUPqf9JYM/Z4AF1OJ5VVJEEzoKST/tRDBJKU=
8686
github.com/hashicorp/terraform-json v0.27.2/go.mod h1:GzPLJ1PLdUG5xL6xn1OXWIjteQRT2CNT9o/6A9mi9hE=
87-
github.com/hashicorp/terraform-plugin-go v0.30.0 h1:VmEiD0n/ewxbvV5VI/bYwNtlSEAXtHaZlSnyUUuQK6k=
88-
github.com/hashicorp/terraform-plugin-go v0.30.0/go.mod h1:8d523ORAW8OHgA9e8JKg0ezL3XUO84H0A25o4NY/jRo=
87+
github.com/hashicorp/terraform-plugin-go v0.31.0 h1:0Fz2r9DQ+kNNl6bx8HRxFd1TfMKUvnrOtvJPmp3Z0q8=
88+
github.com/hashicorp/terraform-plugin-go v0.31.0/go.mod h1:A88bDhd/cW7FnwqxQRz3slT+QY6yzbHKc6AOTtmdeS8=
8989
github.com/hashicorp/terraform-plugin-log v0.10.0 h1:eu2kW6/QBVdN4P3Ju2WiB2W3ObjkAsyfBsL3Wh1fj3g=
9090
github.com/hashicorp/terraform-plugin-log v0.10.0/go.mod h1:/9RR5Cv2aAbrqcTSdNmY1NRHP4E3ekrXRGjqORpXyB0=
9191
github.com/hashicorp/terraform-registry-address v0.4.0 h1:S1yCGomj30Sao4l5BMPjTGZmCNzuv7/GDTDX99E9gTk=
@@ -228,8 +228,8 @@ google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAs
228228
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
229229
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
230230
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
231-
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
232-
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
231+
google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU=
232+
google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
233233
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
234234
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
235235
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=

helper/schema/grpc_provider.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ func (s *GRPCProviderServer) StopContext(ctx context.Context) context.Context {
7676
func (s *GRPCProviderServer) serverCapabilities() *tfprotov5.ServerCapabilities {
7777
return &tfprotov5.ServerCapabilities{
7878
GetProviderSchemaOptional: true,
79+
GenerateResourceConfig: true,
7980
}
8081
}
8182

@@ -1785,6 +1786,109 @@ func (s *GRPCProviderServer) ImportResourceState(ctx context.Context, req *tfpro
17851786
return resp, nil
17861787
}
17871788

1789+
func (s *GRPCProviderServer) GenerateResourceConfig(ctx context.Context, req *tfprotov5.GenerateResourceConfigRequest) (*tfprotov5.GenerateResourceConfigResponse, error) {
1790+
ctx = logging.InitContext(ctx)
1791+
1792+
resp := &tfprotov5.GenerateResourceConfigResponse{}
1793+
1794+
schemaBlock := s.getResourceSchemaBlock(req.TypeName)
1795+
1796+
stateVal, err := msgpack.Unmarshal(req.State.MsgPack, schemaBlock.ImpliedType())
1797+
if err != nil {
1798+
resp.Diagnostics = convert.AppendProtoDiag(ctx, resp.Diagnostics, err)
1799+
return resp, nil
1800+
}
1801+
1802+
if stateVal.IsNull() {
1803+
resp.Diagnostics = []*tfprotov5.Diagnostic{
1804+
{
1805+
Severity: tfprotov5.DiagnosticSeverityError,
1806+
Summary: "Unexpected Generate Config Request",
1807+
Detail: "An unexpected error was encountered when generating resource configuration. The current state was missing.\n\n" +
1808+
"This is always a problem with Terraform or terraform-plugin-sdk. Please report this to the provider developer.",
1809+
},
1810+
}
1811+
1812+
}
1813+
1814+
newConfigVal := stateVal
1815+
1816+
// Handle top level attributes and defaults
1817+
newConfigVal, err = cty.Transform(newConfigVal, func(path cty.Path, val cty.Value) (cty.Value, error) {
1818+
if val.IsNull() {
1819+
return val, nil
1820+
}
1821+
1822+
if len(path) == 0 {
1823+
return val, nil
1824+
}
1825+
1826+
ty := val.Type()
1827+
null := cty.NullVal(ty)
1828+
1829+
// find the attribute or block schema representing the value
1830+
attr := schemaBlock.AttributeByPath(path)
1831+
block := schemaBlock.BlockByPath(path)
1832+
switch {
1833+
case attr != nil:
1834+
// deprecated attributes
1835+
if attr.Deprecated {
1836+
return null, nil
1837+
}
1838+
1839+
// read-only attributes are not written in the configuration
1840+
if attr.Computed && !attr.Optional {
1841+
return null, nil
1842+
}
1843+
1844+
// The legacy SDK adds an Optional+Computed "id" attribute to the
1845+
// resource schema even if not defined in provider code.
1846+
// During validation, however, the presence of an extraneous "id"
1847+
// attribute in config will cause an error.
1848+
// Remove this attribute so we do not generate an "id" attribute
1849+
// where there is a risk that it is not in the real resource schema.
1850+
if path.Equals(cty.GetAttrPath("id")) && attr.Computed && attr.Optional {
1851+
return null, nil
1852+
}
1853+
1854+
// If we have "" for an optional value, assume it is actually null
1855+
// due to the legacy SDK.
1856+
if ty == cty.String {
1857+
if !val.IsNull() && attr.Optional && len(val.AsString()) == 0 {
1858+
return null, nil
1859+
}
1860+
}
1861+
return val, nil
1862+
1863+
case block != nil:
1864+
if block.Deprecated {
1865+
return null, nil
1866+
}
1867+
}
1868+
1869+
return val, nil
1870+
})
1871+
if err != nil {
1872+
configErr := fmt.Errorf("An unexpected error occurred while trying to generate resource configuration. "+
1873+
"This is an error in terraform-plugin-sdk used by the provider. "+
1874+
"Please report the following to the provider developers.\n\n"+
1875+
"Original Error: %s", err.Error())
1876+
resp.Diagnostics = convert.AppendProtoDiag(ctx, resp.Diagnostics, configErr)
1877+
return resp, nil
1878+
}
1879+
1880+
newConfigMP, err := msgpack.Marshal(newConfigVal, schemaBlock.ImpliedType())
1881+
if err != nil {
1882+
resp.Diagnostics = convert.AppendProtoDiag(ctx, resp.Diagnostics, err)
1883+
return resp, nil
1884+
}
1885+
resp.Config = &tfprotov5.DynamicValue{
1886+
MsgPack: newConfigMP,
1887+
}
1888+
1889+
return resp, nil
1890+
}
1891+
17881892
func (s *GRPCProviderServer) MoveResourceState(ctx context.Context, req *tfprotov5.MoveResourceStateRequest) (*tfprotov5.MoveResourceStateResponse, error) {
17891893
if req == nil {
17901894
return nil, fmt.Errorf("MoveResourceState request is nil")

0 commit comments

Comments
 (0)