Skip to content

[Bug]elasticstack_kibana_dashboard — "Provider produced inconsistent result after apply" for config_json #1759

@TinaHeiligers

Description

@TinaHeiligers

Describe the bug
When creating a dashboard with config_json = jsonencode(...) panels, the dashboard is created successfully (verified via GET /api/dashboards/{id}), but terraform reports:

Error: Provider produced inconsistent result after apply

.panels[0].config_json: was cty.StringVal("{...}"), but now cty.StringVal("{...}")

The API normalizes the config on read-back (adds defaults, reorders fields), so the JSON string returned differs from what was sent. The provider does a string comparison and treats any difference as an error.

To Reproduce
Resource: elasticstack_kibana_dashboard (experimental)
Provider: elastic/elasticstack (built from source)

  1. Fetch an existing dashboard via GET /api/dashboards/{id}
  2. Use the response's panel config verbatim in config_json = jsonencode(...)
  3. terraform apply — creates the dashboard but errors on read-back

Example HCL (minimal)

resource "elasticstack_kibana_dashboard" "example" {
  dashboard_id = "some-uuid"
  title        = "Example"

  panels = [
    {
      type = "lens"
      grid = { x = 0, y = 0, w = 24, h = 15 }
      config_json = jsonencode({
        "attributes" : {
          "title" : "",
          "type" : "xy",
          # ... config from GET /api/dashboards/{id} response
        }
      })
    },
  ]
}

``
Error: Provider produced inconsistent result after apply

When applying changes to elasticstack_kibana_dashboard.example,
provider "provider["registry.terraform.io/elastic/elasticstack"]"
produced an unexpected new value: .panels[0].config_json: was
cty.StringVal("{"attributes":{...}}"),
but now cty.StringVal("{"attributes":{...}}")

The two JSON strings differ because the API added default values and/or reordered fields.

**Expected behavior**
The JSON string differences should not throw an error.

**Debug output**
Run `terraform` command with `TF_LOG=trace` and provide extended information on TF operations. 

**Versions (please complete the following information):**
 - macOS 26.3 (25D125)
 - Terraform v1.14.2
 - Provider version (pre-release build from commit c5145cc7)
 - Elasticsearch snapshot version: 9.4.0
 - Kibana running from source on commit `0f031101269f971dab9db848f965ce173245e2c7`

**Additional context**
Related to https://github.com/elastic/kibana-team/issues/2647

## Why we need `config_json`

We tried using the typed config attributes (`xy_chart_config`, `datatable_config`, `metric_config`, etc.) but they cause HTTP 400 validation errors on create. The typed Go struct serialization path omits fields that the Kibana OAS schema requires (e.g., a `filters` array when `operation` is `"filters"`, or other required defaults).

Using `config_json` avoids this entirely because it passes the panel config verbatim to the API. The config data comes directly from `GET /api/dashboards/{id}` — so it's already in the exact schema the API expects.

## Affected dashboards

All 15 dashboards in the `kibana-serverless-o11y-terraform` repo are affected. Every panel using `config_json` triggers this error.

## Suggested Fix

Semantic JSON comparison (parsed object equality) instead of string comparison for `config_json` in the read/plan logic. The relevant code is in:

- `internal/kibana/dashboard/models_panels.go` — `toAPI()` / read-back path
- The provider should parse both JSON strings and compare the resulting objects, not the raw strings

Alternatively, the provider could normalize the JSON (sort keys, strip server-added defaults) before comparison.

## Related: Typed config HTTP 400 errors

Separately from the read-back bug, the typed config attributes (`xy_chart_config`, `datatable_config`, etc.) cause HTTP 400 errors because the Go struct serialization doesn't include all required OAS fields. Examples:

- `operation: "filters"` rows need a `filters` array — typed converter omits it
- `operation: "formula"` metrics may be missing required fields
- Various default values the API expects but the typed structs don't populate

This forces users to `config_json` as a workaround, which then hits the read-back bug above.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions