Coding Standards
May 31, 2026 ยท View on GitHub
This document outlines the coding standards and conventions used in the terraform-provider-elasticstack repository.
General Principles
- Write idiomatic Go.
Project Structure
- Use the Plugin Framework for all new resources (not SDKv2)
- Follow the code organization pattern of the
system_userresource for new Plugin Framework resourcestestdata/- This directory contains Terraform definitions used within the resource acceptance tests. In most cases, this will contain a subdirectory for each test, which then contain subdirectories for individual named test steps.acc_test.go- Contains acceptance tests for the resourcecreate.go- Contains the resourcesCreatemethod and any required logic. Depending on the underlying API, the create and update handlers may share a single code path.delete.go- Contains the resourcesDeletemethod.models.go- Contains Golang models used by the resource. At a minimum this will contain a model for reading plan/config/state from the Terraform plugin framework. Any non-trivial models should also define receivers for translating between Terraform models and API client models.read.go- Contains the resourcesReadmethod. This should also define an internalreadfunction that can be re-used by the create/update paths to populate the final Terraform state after performing the create/update operation.resource.go- Contains:- A factory function for creating the resource (e.g
NewSystemUserResource) Metadata,Configure, and optionallyImportStatefunctions.- Type assertions ensuring the resource fully implement the relevant Plugin Framework interfaces (e.g
var _ resource.ResourceWithConfigure = &systemUserResource{})
- A factory function for creating the resource (e.g
schema.go- Contains theSchemafunction fully defining the resources schemaupdate.go- Contains theUpdatemethod. Depending on the underlying API this may share significant logic with theCreatemethod.- Some resources may define other files, for example:
models_*.go- Complex APIs may result in significant model related logic. Split these files as appropriate if they become large.- Custom plan modifiers, validators and types - Resource specific plan modifiers and custom types should be contained within the resource package.
state_upgrade.go- Resources requiring state upgrades should place theUpgradeStatemethod within this file.
- Avoid adding extra functionality to the existing
utilspackage. Instead:- Code should live as close to the consumers.
- Resource, area, application specific shared logic should live at that level. For example within
internal/kibanafor Kibana specific shared logic. - Provider wide shared logic should be packaged together by a logical concept. For example diagutil contains shared code for managing Terraform Diagnostics, and translating between errors, SDKv2 diags, and Plugin Framework diags.
- Prefer using existing util functions over longer form, duplicated code:
typeutils.IsKnown(val)instead of!val.IsNull() && !val.IsUnknown()utils.ListTypeAsinstead ofval.ElementsAsor similar for other collection typestypeutils.StringishValueinstead of casting to a string e.g.types.StringValue(string(apiResp.Id)). Usetypeutils.StringishPointerValuefor pointers
- The final state for a resource should be derived from a read request following a mutative request (eg create or update). We should not use the response from a mutative request to build the final resource state.
Schema Definitions
- Use custom types to model attribute specific behaviour.
- Use
jsontypes.NormalizedType{}custom type for string attributes containing JSON blobs. - Use
customtypes.DurationType{}for duration-based string attributes. - Use
customtypes.JSONWithDefaultsType{}to allow users to specify only a subset of a JSON blob.
- Use
- Always include comprehensive descriptions for all resources, and attributes.
- Long, multiline descriptions should be stored in an external markdown file, which is imported via Golang embedding. For example.
- Use schema validation wherever possible. Only perform validation within create/read/update functions as a last resort.
- For example, any validation that relies on the actual Elastic Stack components (e.g Elasticsearch version) can only be performed during the create/read/update phase.
- Kibana and Fleet resources will be backed by the Kibana API. The schema definition should closely follow the defined API request/response models defined in the Kibana API specification.
- Further details may be found in the API documentation
- Elasticsearch resources will be backed by the go-elasticsearch client.
- Further details may be found in the API documentation
- Use
EnforceMinVersionto ensure the backing Elastic Stack applications support the defined fields.- The provider supports a wide range of Stack versions, and so newer features will not be available in all versions.
- See
assertKafkaSupportfor an example of how to handle the use of unsupported attributes.
Write-only secret attributes
Resources exposing WriteOnly + Sensitive attributes (passwords, tokens, API secrets) MUST detect drift using the shared internal/utils/writeonlyhash helper. The helper stores bcrypt hashes of applied values in resource private state, surfacing in-config edits as plan-time updates without revealing the value. See writeonly-secret-hashing.md for the contract, threat model, and a worked adoption example.
JSON Handling
- Use
jsontypes.NormalizedType{}for JSON string attributes to ensure proper normalization and comparison. - Use
customtypes.JSONWithDefaultsType{}if API level defaults may be applied automatically.
Testing
- Use table-driven unit tests when possible with
t.Run()for test cases - Use testify library (
assert,require) for test assertions - Ensure that every resource attribute is covered by at least one acceptance test case whenever possible.
- Features that require external services are likely the only excuse to not include acceptance test coverage.
- Organize acceptance tests in
acc_test.gofiles - Test Terraform code should be vanilla, valid Terraform
- Store test Terraform modules in
testdata/<test_name>/<step_description>directories. - Define any required variables within the module
- Reference the test code via
ConfigDirectory: acctest.NamedTestCaseDirectory("<step description>") - Define any required variables via
ConfigVariables
- Store test Terraform modules in
- Resources should include tests for the following
- Creating a resource
- Updating a resource
- Deleting a resource
- Importing a resource
- Creating a resource in another space (if applicable)
API Client Usage
- Use generated API clients from
generated/kbapi/for new Kibana API interactions