fix: improve github_organization_custom_properties resource - #3234
fix: improve github_organization_custom_properties resource#3234mkushakov wants to merge 17 commits into
Conversation
|
👋 Hi! Thank you for this contribution! Just to let you know, our GitHub SDK team does a round of issue and PR reviews twice a week, every Monday and Friday! We have a process in place for prioritizing and responding to your input. Because you are a part of this community please feel free to comment, add to, or pick up any issues/PRs that are labeled with |
| Create: resourceGithubCustomPropertiesCreate, | ||
| Read: resourceGithubCustomPropertiesRead, | ||
| Update: resourceGithubCustomPropertiesUpdate, | ||
| Delete: resourceGithubCustomPropertiesDelete, | ||
| Importer: &schema.ResourceImporter{ | ||
| State: resourceGithubCustomPropertiesImport, |
There was a problem hiding this comment.
Please refactor to use the Context functions
There was a problem hiding this comment.
This comment targeted the in-place bug-fix approach on the old resource. Per @stevehipwell's suggestion below, that approach was replaced with a new resource (github_organization_repository_custom_property) instead, which uses *Context CRUD throughout. The old resource is now deprecated and left otherwise as-is so existing state keeps working — happy to revisit if you'd rather see it migrated too.
| } | ||
| return resourceGithubCustomPropertiesRead(d, meta) | ||
| // Create uses the same upsert API, and already calls Read at the end | ||
| return resourceGithubCustomPropertiesCreate(d, meta) |
There was a problem hiding this comment.
issue: Never call any CRUD function
There was a problem hiding this comment.
Same as above — this was flagging Update calling Create then Read on the old resource. That code path is unchanged now that the fix lives in the new github_organization_repository_custom_property resource, whose Update doesn't call another CRUD function.
c8c34c4 to
b4fdf7b
Compare
1267efc to
e40236c
Compare
|
@mkushakov if you're still interested in contributing this change, I think it may be better to create a new resource named correctly (#2936) and then deprecate this resource. Off the top of my head I think the resource should be I've done a full refactor on the |
8122d85 to
c19b5c4
Compare
hello @stevehipwell thanks for suggestions, as you asked i have following in my PR:
|
stevehipwell
left a comment
There was a problem hiding this comment.
Thanks for the changes @mkushakov, this looks like a great addition. As well as the inline comments I've got the following high level feedback.
- We need a
github_organization_repository_custom_propertiesdata source to replacegithub_organization_custom_properties - Could you align your changes/patterns to #3476 (this should be "correct" in terms of the contribution guide)
| func dataSourceGithubOrganizationRepositoryCustomPropertyRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { | ||
| if err := checkOrganization(meta); err != nil { | ||
| return diag.FromErr(err) | ||
| } | ||
|
|
||
| client := meta.(*Owner).v3client | ||
| orgName := meta.(*Owner).name | ||
| propertyName := d.Get("property_name").(string) |
There was a problem hiding this comment.
| func dataSourceGithubOrganizationRepositoryCustomPropertyRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { | |
| if err := checkOrganization(meta); err != nil { | |
| return diag.FromErr(err) | |
| } | |
| client := meta.(*Owner).v3client | |
| orgName := meta.(*Owner).name | |
| propertyName := d.Get("property_name").(string) | |
| func dataSourceGithubOrganizationRepositoryCustomPropertyRead(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics { | |
| meta, _ := m.(*Owner) | |
| client := meta.v3client | |
| owner := meta.name | |
| if !meta.IsOrganization { | |
| return return diag.FromErr(fmt.Errorf("repository custom properties are only supported for organizations, %q is a user", owner)) | |
| } | |
| propertyName := d.Get("property_name").(string) |
There was a problem hiding this comment.
Done, with one deviation: I used the existing checkOrganizationOK helper (used by ~15 other data sources) instead of the inline fmt.Errorf check, since it returns diag.Diagnostics directly. Applied the same m any / meta, _ := m.(*Owner) / owner := meta.name shape to all 4 resource CRUD functions too, to match ARCHITECTURE.md and the #3476 pattern.
| if !slices.Contains([]github.PropertyValueType{ | ||
| github.PropertyValueTypeSingleSelect, | ||
| github.PropertyValueTypeMultiSelect, | ||
| }, cp.ValueType) { | ||
| cp.AllowedValues = nil | ||
| } |
There was a problem hiding this comment.
I think a switch statement would work better here.
There was a problem hiding this comment.
Done — replaced slices.Contains with a switch in both the data source and the resource's Read.
| func TestAccGithubOrganizationRepositoryCustomPropertyDataSource(t *testing.T) { | ||
| const dataAddr = "data.github_organization_repository_custom_property.test" | ||
|
|
||
| t.Run("reads a property created by the resource", func(t *testing.T) { |
There was a problem hiding this comment.
Can we make this test work correctly in an organization where there are already existing properties?
There was a problem hiding this comment.
Fixed by creating the fixture via mustCreateTestOrganizationRepositoryCustomProperty with a randomized name instead of a hardcoded one in the TF config. Also applied the same fix to the resource's own acceptance test (resource_github_organization_repository_custom_property_test.go), which had the identical problem across all 10 subtests plus no t.Parallel().
|
|
||
| t.Run("reads a property created by the resource", func(t *testing.T) { | ||
| config := ` | ||
| resource "github_organization_repository_custom_property" "test" { |
There was a problem hiding this comment.
Could you look at #3476 and copy the pattern for configuring the test dependencies outside of TF code?
There was a problem hiding this comment.
Done — switched to mustCreateTestOrganizationRepositoryCustomProperty(t, ...), matching the pattern in #3476.
| cp.AllowedValues = nil | ||
| } | ||
|
|
||
| if err := setOrganizationRepositoryCustomPropertyState(d, cp); err != nil { |
There was a problem hiding this comment.
I think for now I'd rather see the code duplicated in the method bodies, as if we move the logic we ought to have a single documented pattern for how to do it.
There was a problem hiding this comment.
Done — removed setOrganizationRepositoryCustomPropertyState and inlined the d.Set calls in Create/Read/Update and the data source's Read.
|
@mkushakov are you still going ahead with this PR? Thanks! 🙏 |
|
@mkushakov if you're unable to continue with this work, please let me know and I'll pick it up. |
c19b5c4 to
ff065db
Compare
… setting Addresses review feedback from integrations#3234: - Use m any + meta, _ := m.(*Owner) in every CRUD/read function, matching the convention documented in ARCHITECTURE.md and used by the integrations#3476 refactor of github_repository_custom_property. Keep checkOrganizationOK (used by ~15 other data sources) rather than an inline org check. - Replace slices.Contains with a switch statement for the select-type check. - Drop the shared setOrganizationRepositoryCustomPropertyState helper and inline the d.Set calls in each CRUD method body, per stevehipwell's request to keep a single documented pattern until state-setting logic is unified repo-wide.
|
@stevehipwell / @abdonrd , sorry for delay. I have time to work on it and try to address all comments. Feel free to let me know if there is some missing function or issues. |
There was a problem hiding this comment.
Pull request overview
These provider review instructions are being used.
Adds singular organization repository custom-property resource/data source implementations while deprecating legacy plural APIs.
Changes:
- Adds CRUD, validation, import, and acceptance tests.
- Registers and documents the new APIs.
- Adds deprecation guidance for legacy APIs.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
github/resource_github_organization_repository_custom_property.go |
New resource implementation. |
github/resource_github_organization_repository_custom_property_test.go |
Resource acceptance tests. |
github/data_source_github_organization_repository_custom_property.go |
New data source. |
github/data_source_github_organization_repository_custom_property_test.go |
Data-source acceptance test. |
github/provider.go |
Registers new APIs. |
github/resource_github_organization_custom_properties.go |
Deprecates legacy resource. |
github/data_source_github_organization_custom_properties.go |
Deprecates legacy data source. |
templates/resources/organization_repository_custom_property.md.tmpl |
Resource documentation template. |
templates/resources/organization_custom_properties.md.tmpl |
Resource deprecation notice. |
templates/data-sources/organization_repository_custom_property.md.tmpl |
Data-source documentation template. |
templates/data-sources/organization_custom_properties.md.tmpl |
Data-source deprecation notice. |
examples/resources/github_organization_repository_custom_property/resource_1.tf |
Resource usage examples. |
examples/resources/github_organization_repository_custom_property/import.sh |
CLI import example. |
examples/resources/github_organization_repository_custom_property/import-by-string-id.tf |
Import-block example. |
examples/data-sources/github_organization_repository_custom_property/data-source_1.tf |
Data-source example. |
docs/resources/organization_repository_custom_property.md |
Generated resource documentation. |
docs/resources/organization_custom_properties.md |
Generated resource deprecation notice. |
docs/data-sources/organization_repository_custom_property.md |
Generated data-source documentation. |
docs/data-sources/organization_custom_properties.md |
Generated data-source deprecation notice. |
RESOURCES.md |
Updates implementation status. |
ARCHITECTURE.md |
Records deprecated API replacements. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| "default_value": { | ||
| Type: schema.TypeString, | ||
| Optional: true, | ||
| Computed: true, | ||
| Description: "Default value applied to repositories that do not explicitly set the property.", |
There was a problem hiding this comment.
Good catch, and it was worse than described: DefaultValueString() returns ("", false) for both multi_select and true_false (it only handles string/single_select/url), and we discarded the ok bool. So true_false with a default_value didn't just lose data — it failed apply outright with "Provider produced inconsistent result after apply". Our own resource_1.tf example hit this.
Fixed in 729d750: default_value is now a TypeList of strings in both the resource and the data source, mirroring how #3476 models property_value. Requests send an array for multi_select and a bare string otherwise; responses go through a helper that picks the matching go-github accessor per value_type. Added true_false and multi_select acceptance subtests — both would have failed before this change.
| if selectType && len(allowedValues) == 0 { | ||
| return fmt.Errorf("allowed_values is required when value_type is %q", valueType) | ||
| } | ||
| if !selectType && len(allowedValues) > 0 { | ||
| return fmt.Errorf("allowed_values must not be set when value_type is %q", valueType) | ||
| } |
There was a problem hiding this comment.
You're right that the description and the validation disagreed. I checked GitHub's REST docs though, and the required ⇒ default_value relationship isn't documented anywhere. Enforcing it at plan time would block configs that the API might well accept, which seems worse than the current behaviour of surfacing a 422 at apply. So I fixed the mismatch in the other direction — the description now says GitHub may reject required = true without a default, and there's no new CustomizeDiff rule. Happy to add the validation if maintainer think we should actually enforces it.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 22 out of 22 changed files in this pull request and generated no new comments.
Suppressed comments (8)
github/resource_github_organization_repository_custom_property.go:85
- Empty allowed values pass the non-empty-list check, but
expandStringListremoves each empty string before the request. For example,[""]passes planning and becomes an empty API list. Validate each element as non-empty so invalid configurations fail at plan time.
Elem: &schema.Schema{Type: schema.TypeString},
github/resource_github_organization_repository_custom_property.go:121
- The scalar cardinality check still accepts any string for
true_false, even though this type can only encode"true"or"false". Invalid values therefore survive planning and fail at the GitHub API. Add type-specific diff validation (and a plan test) for this bounded value.
if d.NewValueKnown("default_value") && valueType != github.PropertyValueTypeMultiSelect {
if defaultValue, _ := d.Get("default_value").([]any); len(defaultValue) > 1 {
return fmt.Errorf("default_value must contain at most one element when value_type is %q, got %d", valueType, len(defaultValue))
}
github/resource_github_organization_repository_custom_property.go:344
- This new 404-as-success delete branch is not exercised by the acceptance suite: the out-of-band deletion test recreates the property before final destroy. Add a test that removes the property immediately before Terraform destroy and verifies cleanup succeeds, so this stated behavior cannot regress.
if ghErr, ok := errors.AsType[*github.ErrorResponse](err); ok && ghErr.Response.StatusCode == 404 {
return nil
}
github/resource_github_organization_repository_custom_property.go:86
allowed_valuesis bothOptionalandComputed, so when it is omitted on a new resource its planned value is unknown. TheNewValueKnown("allowed_values")guard then skips the required-list check, allowing asingle_select/multi_selectplan to reach the API without values instead of producing the provider error asserted by the acceptance test. Make this field optional-only (the read path can still populate it) or inspect raw configuration explicitly.
Computed: true,
github/resource_github_organization_repository_custom_property.go:72
- Empty elements pass schema validation, but
expandStringListdrops them. A configureddefault_value = [""]therefore plans successfully and is silently sent as no default, causing state to disagree with configuration. Reject empty elements during planning (or preserve them if the API supports them).
This issue also appears on line 85 of the same file.
Elem: &schema.Schema{Type: schema.TypeString},
github/resource_github_organization_repository_custom_property.go:22
urlis exposed as a supported value type, but the new acceptance suite never creates or imports a URL definition/default despite the PR stating that all five types are covered. Add a URL subtest that verifies its scalar default round-trips through the API and import path.
string(github.PropertyValueTypeURL),
github/resource_github_organization_repository_custom_property.go:144
- Clearing a previously configured default cannot work here. For
default_value = [],GetOkis false, leaving theanyfield nil; go-github omits that field from the PATCH payload, so GitHub retains the old default and the response restores it into state. Encode an explicit JSONnullfor an empty configured list and add an update test that changes a non-empty default to empty.
if v, ok := d.GetOk("default_value"); ok {
if defaultValue := expandStringList(v.([]any)); len(defaultValue) > 0 {
// Only multi_select sends an array; the other types send a bare string.
switch valueType {
case github.PropertyValueTypeMultiSelect:
github/resource_github_organization_repository_custom_property.go:54
- A required string still accepts
property_name = "", which produces an invalid API path and defers a deterministic input error until apply. Add plan-time non-empty validation to this new schema attribute.
This issue also appears on line 118 of the same file.
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "Name of the custom property.",
},
…erties resource Adding the DeprecationMessage field left the remaining Create/Read/Update/Delete fields misaligned.
… setting Addresses review feedback from integrations#3234: - Use m any + meta, _ := m.(*Owner) in every CRUD/read function, matching the convention documented in ARCHITECTURE.md and used by the integrations#3476 refactor of github_repository_custom_property. Keep checkOrganizationOK (used by ~15 other data sources) rather than an inline org check. - Replace slices.Contains with a switch statement for the select-type check. - Drop the shared setOrganizationRepositoryCustomPropertyState helper and inline the d.Set calls in each CRUD method body, per stevehipwell's request to keep a single documented pattern until state-setting logic is unified repo-wide.
Addresses stevehipwell's feedback that the data-source test needs to work in an organization that already has custom properties, and applies the same fix to the resource acceptance test, which had the identical problem: every subtest used a hardcoded tf-acc-test-* property name with no t.Parallel(), so concurrent runs against one org (or a leftover from a failed run) would collide. Also switches the data-source test to create its fixture via mustCreateTestOrganizationRepositoryCustomProperty instead of a resource block in the TF config, per the pattern in integrations#3476.
…y pattern Copies the pattern from integrations#3476 (github_repository_custom_property): - Rewrite both templates to be schema-driven ({{ .Description }}, subcategory, HasExamples/ExampleFiles, HasImport/HasImportIDConfig/HasImportIdentityConfig) instead of hardcoded front-matter prose, so the rendered docs can't drift from the resource/data-source Description fields again. - Move examples to the auto-discovered examples/{resources,data-sources}/ github_organization_repository_custom_property/ layout. Fold the three previously separately-headed usage examples (default, editable-by, boolean) into a single resource_1.tf with comment headers, per the single-example-file convention in examples.instructions.md. - Add import-by-string-id.tf for the modern `import` block syntax. - Regenerate docs/resources and docs/data-sources for this resource.
…iner indexes RESOURCES.md and ARCHITECTURE.md list every resource/data source and its deprecation status; both were missing the new github_organization_repository_custom_property resource and data source, and the (🚫) deprecation marker on github_organization_custom_properties.
github.CustomProperty.DefaultValue is polymorphic: multi_select carries an
array, true_false a stringified bool, and the rest plain strings. The code
called only DefaultValueString() and discarded its ok bool, which returns
("", false) for both multi_select AND true_false, so:
- true_false properties with a default_value failed apply outright with
"Provider produced inconsistent result after apply" — the committed
resource_1.tf example hit this.
- multi_select list defaults could not be represented at all (integrations#2806).
default_value is now a TypeList of strings in both the resource and the data
source, mirroring how integrations#3476 models property_value on
github_repository_custom_property. Requests send an array for multi_select and
a bare string otherwise; responses are normalised by
flattenOrganizationRepositoryCustomPropertyDefaultValue, which selects the
matching go-github accessor per value_type and returns an error rather than
silently yielding "" when a value cannot be parsed. CustomizeDiff rejects
multi-element default_value for the four scalar types.
Also addresses review feedback:
- Timeouts block so users can configure per-operation timeouts.
- Real import function seeding property_name from the ID, letting Read use the
attribute getter instead of d.Id().
- diag.Errorf instead of diag.FromErr(fmt.Errorf(...)), the dominant idiom in
this package.
- Guard against the API returning an empty property name.
- Soften the `required` description: GitHub's REST docs do not actually
document that default_value is mandatory when required is true, so this is
no longer stated as a hard rule.
…e plans New subtests, closing gaps that let the default_value bug ship green: - true_false with a default_value, which previously failed apply with "Provider produced inconsistent result after apply". - multi_select with multiple default values, the integrations#2806 scenario. - a scalar type rejecting a multi-element default_value. - out-of-band deletion via the API, asserting the next plan is a Create so the graceful-404 handling in Read cannot regress. - default_value is an empty list (not [""]) when the property has no default. Addresses deiga's review feedback: - Collapse every before/after config pair into one parameterised template string instead of duplicated configBefore/configAfter blocks. - Assert ResourceActionUpdate on the in-place update steps.
Updates the resource example to the new default_value list syntax, adds a multi_select example demonstrating multiple default values (integrations#2806), and regenerates both pages so the schema tables and the new timeouts block match the code.
flattenOrganizationRepositoryCustomPropertyDefaultValue is used by both the resource and the data source, so per review feedback it moves out of the resource file into github/util_custom_property.go, following the util_<domain>.go convention in ARCHITECTURE.md. buildOrganizationRepositoryCustomProperty and the two value-type var blocks stay put — they are only used by the resource.
Per review feedback the narrative note doesn't belong in the doc template. The pointer to github_repository_custom_property and the REST API link move into a top-of-file comment in resource_1.tf, which examples.instructions.md names as the place for extra context. The template now holds nothing beyond the repo-standard front matter.
…l file parseRepositoryCustomPropertyValueToStringSlice is shared by data_source_github_repository_custom_properties.go and resource_github_repository_custom_property.go, so it joins the other custom property helper in util_custom_property.go rather than living in a data source file. Requested in review.
Four schema/diff correctness fixes from review: - allowed_values is no longer Computed. An omitted Optional+Computed list is unknown during plan (schemaMap.diffList marks the count NewComputed when both old and new lengths are 0), so d.NewValueKnown returned false and the cross-field validation skipped itself -- meaning "allowed_values is required when value_type is single_select" could never fire and the request reached the API without values. Nothing needed the Computed behaviour: select types always set the field in config, and Read clears it for the other types. - Reject empty strings in allowed_values and default_value elements. expandStringList silently drops "", so allowed_values = [""] passed the length check and became an empty API list, and default_value = [""] became no default at all while the config said otherwise. - Constrain true_false defaults to exactly "true"/"false". strconv.ParseBool accepts "True" and "1", which the read path would normalise back to "true" and fail the apply with an inconsistent-result error. - Reject an empty property_name on both the resource and the data source rather than building an invalid API path. Also documents that a default_value cannot be removed once set: the API field is omitted when nil rather than sent as JSON null, so GitHub keeps the old value.
…dations
- url subtest with a scalar default and import, so all five value_types are
genuinely exercised as the PR claims.
- Destroy step whose PreConfig removes the property out of band, covering the
404-as-success branch in Delete. The existing recreate test always recreates
the property first, so that branch was never reached.
- Plan-only subtests for the two new rules: a non-boolean true_false default
("True", which strconv.ParseBool would otherwise accept and silently
normalise) and an empty string in allowed_values.
Both templates were byte-for-byte identical to the repo-level fallbacks at templates/resources.md.tmpl and templates/data-sources.md.tmpl, which tfplugindocs uses when no per-resource template exists. Verified the generated pages are unchanged with the templates removed. Also picks up the default_value description note from the previous commit.
1cf2a52 to
d4427c0
Compare
Resolves #2806
Resolves #3191
Resolves #3580
Refs #2936
Before the change?
github_organization_custom_propertieswas the only way to manage org-level custom property definitions, and it had problems that could not be fixed in place without breaking existing state:default_valuewas a plain string, so amulti_selectproperty could not express its list-valued default at all ([BUG]: GitHub_organization_custom_properties #2806), and the schema did not map cleanly onto the fivevalue_types (Proposal: Separateresource_github_organization_custom_propertiesinto separate resources pervalue_type#3191). It also never round-tripped fortrue_falseormulti_select:DefaultValueString()returns("", false)for those types and the read path discarded theok, writing""into state and producing a permanent+ default_valuediff on every plan ([BUG]: github_organization_custom_properties of typetrue_falsedetects drift ondefault_valueindefinitely #3580).value_typewas optional even though the API requires it, andproperty_name/value_typewere notForceNewdespite being immutable server-side.Read, so drift outside Terraform errored instead of removing the resource from state; aCustomizeDiffreferencing a non-existentslugfield; a redundant double-read inUpdate; and no validation ofallowed_valuesagainstvalue_type.Per @stevehipwell's review feedback, this PR adds a correctly-named replacement rather than patching the old resource.
After the change?
New resource and data source
github_organization_repository_custom_property(singular), written from scratch againstARCHITECTURE.mdand thegithub_repository_custom_propertyrefactor in #3476:*ContextCRUD returningdiag.Diagnostics,tflogstructured logging, andcheckOrganizationOKon every entry point.errors.AsType[*github.ErrorResponse]404 handling:Readremoves the property from state,Deletetreats it as success.ForceNewon the immutableproperty_nameandvalue_type.value_typeandvalues_editable_byagainst their allowed sets, non-emptyproperty_nameand non-empty list elements,allowed_valuesrequired forsingle_select/multi_selectand rejected for the other types,default_valuelimited to one element for everything butmulti_select, andtrue_falsedefaults constrained to exactly"true"/"false".default_valueis a list of strings, which is what makes [BUG]: GitHub_organization_custom_properties #2806 and Proposal: Separateresource_github_organization_custom_propertiesinto separate resources pervalue_type#3191 work.github.CustomProperty.DefaultValueis polymorphic —multi_selectcarries a JSON array,true_falsea stringified bool, the rest plain strings — so requests send an array formulti_selectand a bare string otherwise, and responses are normalised through the matching go-github accessor pervalue_type, raising an error rather than silently yielding"". Same "value is a collection, cardinality depends on the type" shape fix: Refactor repository custom property #3476 uses forproperty_value.Timeoutsblock, and an import function that seedsproperty_namefrom the import ID.Descriptionon every schema attribute.Deprecation: the existing
github_organization_custom_propertiesresource and data source each gain aDeprecationMessagepointing at the new name. Their CRUD is otherwise untouched, so existing state and configurations keep working with only a warning.Shared helpers: the polymorphic-value handling lives in a new
github/util_custom_property.go.parseRepositoryCustomPropertyValueToStringSlicemoves there too — it was already shared betweengithub_repository_custom_propertyand its plural data source, so this is the only change to a pre-existing Go file beyond the deprecation notices and the provider registrations.Tests: 18
t.Runsubtests usingConfigStateChecks/ConfigPlanChecks, covering all fivevalue_types (including scalar and multi-element defaults), create/update/import, bothForceNewpaths, every validation rule, out-of-band deletion — both that a subsequent plan recreates rather than errors, and that destroying an already-deleted property succeeds — and thevalues_editable_byscenarios. Fixtures are created through the API with randomized names so the suite is safe to run in parallel against an org that already has properties.Docs: no per-resource doc templates are needed — both pages render from the repo-level
templates/{resources,data-sources}.md.tmplfallbacks plus the schemaDescriptions. Examples are in the auto-discovered layout includingimport.shandimport-by-string-id.tf;RESOURCES.mdandARCHITECTURE.mdlist the new resource/data source and flag the deprecated pair.Note
On #3191: rather than splitting into one resource per
value_typeas originally proposed, the single resource now models every type correctly — including the array defaults that issue cites as its concrete example. Happy to revisit the split if you'd still prefer it.Note
One known limitation: a
default_valuecan be changed but not removed.CustomProperty.DefaultValueis ananyfield withomitempty, so a nil value is omitted from the request rather than sent as JSONnulland GitHub keeps the previous default. The attribute isComputed, so the old value is absorbed back into state without erroring. This is documented in the attribute description.Note
On #3580: the perpetual
true_falsedrift is fixed in the new resource, which reads every default back through the accessor matching itsvalue_type. The deprecated resource and data source keep their existing read path, so they still exhibit the drift until they are removed — anyone hitting that bug needs to move to the new name. Happy to patch the deprecated pair in place too, here or as a follow-up, if you'd rather not make migration a prerequisite for the fix.Pull request checklist
Does this introduce a breaking change?
The old resource and data source remain fully functional and only emit a deprecation warning.