Autogen config.yml authoring and schema review
tools/codegen/config.yml tells the codegen pipeline which API endpoints map to each CRUD operation and how to shape the resulting Terraform schema. This skill holds all the judgment for writing an entry and for reviewing what the generator produced. Reference existing entries in config.yml for patterns matching your resource's shape.
Writing an entry
Search tools/codegen/atlasapispec/multi-version-api-spec.flattened.yml for the resource's path segment and map:
- GET (single item) →
read, POST →create, PATCH or PUT →update, DELETE →delete, GET (list) →datasources.list. version_header: theapplication/vnd.atlas.<date>+jsoncontent type on those paths. Omitting it lets the pipeline derive the version from the read operation, so set it only when the resource must be pinned to a specific version.- Naming: the config key must not use the
_apisuffix, which is reserved for internal-only resources that are never registered in the provider. A user-facingmongodbatlas_<name>resource uses the plain name (e.g.metric_integration).
For the typical shape of a project-scoped entry — CRUD operations plus a datasources block with its own read/list and schema options — read a real entry such as log_integration or metric_integration in config.yml rather than working from a template.
aliases
- Any resource under
/groups/{groupId}/must aliasgroupId: projectId, in bothschema.aliasesanddatasources.schema.aliases, producing the standardproject_idattribute. - The resource's ID attribute name should match the API path parameter. If the path param and the response body disagree (path
{myResourceId}, bodyid), that is an API bug — report it to the owning API team, and alias to the more descriptive name (id: myResourceId) as a stopgap. - Aliases match on the full camelCase API path, so the plural surface may need its own nested entries (e.g.
results.id: integrationId— seelog_integration). When the list results embed the scope field, keep it and alias it (results.groupId: projectId— seeai_model_api_key) rather than ignoring it.
ignores
The generator already drops top-level links and pagination fields (total_count, envelope, items_per_page, page_num, pretty, include_count) everywhere — see commonIgnoredAttributes in tools/codegen/codespec/config.go. Do not re-list them. Ignore matching is exact-path, so the nested results.links on list data sources still needs an explicit entry, plus any other field that doesn't belong in the Terraform schema.
Secrets returned only in the create response (never populated on read or list) should be ignored on data sources, where the attribute would always be null (ignores: ["secret", "results.secret"] with a rationale comment — see service_account_secret or ai_model_api_key). On the resource itself the field stays, marked sensitive.
wait
Add wait blocks only when the API is async (the operation returns before completing). Look for state properties like stateName or status in the response schema. Follow the anchor/merge pattern in search_deployment_api in config.yml: define the wait once on create, reuse it on update (*anchor), and merge with overrides on delete (<<: *anchor), where DELETED is the special target state representing a 404/empty response.
Overrides
The Override struct in tools/codegen/config/config_model.go is the source of truth for the available set; config.yml has real examples of each. Overrides go under schema.overrides (resource) and datasources.schema.overrides (data sources), and an override can hit the same attribute on more than one surface.
Evaluate these two first — they are the easiest to miss and the most impactful when wrong:
computability→ optional + computed: if the API returns a default when the attribute is omitted, mark it optional + computed so an absent config value does not produce a perpetual non-empty plan (seeuse_legacy_path_structureunderlog_integration). The "omitting the value" acceptance test inacceptance-test-patternsverifies exactly this.overrides: <attr>: # Optional but Atlas returns the default value when the attribute is omitted. computability: optional: true computed: truesensitive: if the field carries a secret (token, password, key, credential, connection string), mark it sensitive.
Then consider:
- Collection
type: decide list vs set from the array's semantics, per IPA-124 List vs Set. A list means order is meaningful and elements may repeat: the server returns a stable order and preserves the order the client provided (e.g.environments). A set means order is not meaningful and elements are unique: the server may return elements in any order and never returns duplicates (e.g.roles) — that needstype: set, since codegen defaults arrays to List absent anx-xgen-array-semantic: setdeclaration. Also align the same field across surfaces: the resource, singular data source, and plural data source are generated from different endpoints, so a field can come out as List on one and Set on another (seeroles: type: setunderservice_account_project_assignment). immutable_computed(generatesUseStateForUnknown; resource schemas only): apply to server-generated fields that are stable after creation — IDs,created_at/created_by, masked secret variants — to reduce plan verbosity. On a one-time-returned secret it also preserves the stored value across updates (seeai_model_api_key, wheresecretcarries it for exactly that reason).description: each surface inherits wording from its own endpoint schema. Use the shared YAML anchors for standard fields (*project_id_description); otherwise prefer reporting wording inconsistencies upstream over local overrides.
Remaining override fields, one line each — read the struct comments in config_model.go before using them: request_body_usage (when a field must be sent or omitted differently on update), skip_state_list_merge, plan_modifiers, validators, ignore_validators. SchemaOptions.discriminator_types is a documented stopgap for oneOf/anyOf variants.
Overrides are stopgaps — pair each with an upstream flag
The codegen consumes spec annotations natively (tools/codegen/codespec/api_spec_schema.go), so the durable fix is the annotation, not the override. Whenever an override compensates for missing spec information, report to the owning API team that the spec should carry the annotation, and surface the report in the PR description. Once the spec is fixed, the override should be dropped on the next regeneration.
| Local override | Spec annotation to request | Reference |
|---|---|---|
type: set |
x-xgen-array-semantic: set |
IPA-131 |
computability optional+computed |
x-xgen-server-computed-when-client-omitted: true (escape hatch for legacy APIs; not allowed on booleans) |
IPA-131 |
immutable_computed |
x-xgen-server-computed-immutable: true |
IPA-131 |
sensitive: true |
format: password |
IPA-117 |
| ID alias for path/body mismatch | Consistent property naming (API bug) | — |
Post-generation schema review
The pipeline translates the spec faithfully, but the spec may not reflect how the API behaves or what Terraform users expect. After generating, read tools/codegen/models/<name>.yaml, internal/serviceapi/<package>/resource_schema.go, and the relevant spec paths, and check for:
- Should-be-updatable fields marked create-only (
create_only: true, which codegen derives fromreq_body_usage: omit_in_update_bodyoromit_always): often the PATCH body is narrower than POST in the spec even though the API accepts the field on update. - Server-generated fields not computed:
id,created_at,state,statusmarked required/optional are likely wrong — response-required does not mean user-provided. - User-configurable fields wrongly computed: the inverse — compare the field's presence in the POST/PATCH request bodies.
- Missing
sensitivemarkers on secret-looking fields. - Discriminator scoping: a field required for one oneOf/anyOf variant must not be required for all variants.
Report findings with severity tags ([ERROR] wrong computed/required classification, [WARNING] likely-updatable or likely-sensitive, [INFO] cosmetic), fix what an override can fix, and flag the rest upstream as above.