Skip to content

[BUG] monitor: documentation vs. API structure mismatch causes drift #251

Open
@lase-netlight

Description

@lase-netlight

What is the bug?

The OpenSearch Provider documentation for the opensearch_monitor resource is out of sync with the actual API requirements. When using the documented structure, Terraform always reports drift and attempts to modify the resource on every plan, even when no changes are made.

It looks like most of the fields for which Terraform attempts modification do not need to be user accessible. Since the resource is fully defined through a JSON, it is not possible to ignore these changes using a lifecycle definition.

As a workaround, I was able to create a correct JSON structure which so far does not drift, attached below. This workaround is however brittle and not documented at the moment.

How can one reproduce the bug?

  1. Create an OpenSearch monitor following the documented structure from the provider documentation
  2. Run terraform apply to create the monitor
  3. Run terraform plan without changing anything
  4. Observe that Terraform shows differences and wants to update the monitor

What is the expected behavior?

After creating a monitor with Terraform, subsequent terraform plan with no configuration changes should report "No changes." The provider should either:

  1. Transform the user-defined configuration to match OpenSearch's internal representation
  2. Or, correctly document the actual structure required by the OpenSearch API

What is your host/environment?

  • OpenSearch 2.17
  • terraform-provider-opensearch version 2.3.1
  • macOS environment

Do you have any additional context?

The major discrepancies between documentation and required structure:

  1. Undocumented required fields: Documentation doesn't mention monitor_type, owner, data_sources, and delete_query_index_in_every_run that the API adds.

  2. Trigger structure: Documentation shows a flat structure:

    "triggers": [{"name": "trigger", "condition": {...}, "actions": [...]}]

    But API requires:

    "triggers": [{"query_level_trigger": {"name": "trigger", "condition": {...}, "actions": [...]}}]
  3. Query format differences:

    • Documentation shows: "range": {"time": {"gte": "now-24h", "lte": "now"}}

    • API requires: "range": {"time": {"from": "now-24h", "to": "now", "include_lower": true, "include_upper": true, "boost": 1}}

    • Documentation shows: "term": {"field": "value"}

    • API requires: "term": {"field": {"value": "value", "boost": 1}}

Fixed working configuration example:

resource "opensearch_monitor" "example" {
  body = jsonencode({
    type = "monitor"
    name = "example-monitor"
    enabled = true
    monitor_type = "query_level_monitor"
    owner = "alerting"
    data_sources = {
      alerts_history_index = ".opendistro-alerting-alert-history-write"
      alerts_history_index_pattern = "<.opendistro-alerting-alert-history-{now/d}-1>"
      alerts_index = ".opendistro-alerting-alerts"
      comments_index = ".opensearch-alerting-comments-history-write"
      comments_index_pattern = "<.opensearch-alerting-comments-history-{now/d}-1>"
      findings_enabled = false
      findings_index = ".opensearch-alerting-finding-history-write"
      findings_index_pattern = "<.opensearch-alerting-finding-history-{now/d}-1>"
      query_index = ".opensearch-alerting-queries"
      query_index_mappings_by_type = {}
    }
    delete_query_index_in_every_run = false
    
    schedule = {
      cron = {
        expression = "0 8 * * *"
        timezone = "UTC"
      }
    }
    
    inputs = [{
      search = {
        indices = ["metrics-*"]
        query = {
          size = 0
          query = {
            bool = {
              adjust_pure_negative = true
              boost = 1
              filter = [
                {
                  range = {
                    time = {
                      boost = 1
                      from = "now-24h"
                      to = "now"
                      include_lower = true
                      include_upper = true
                    }
                  }
                },
                {
                  term = {
                    name = {
                      value = "documents_indexed_example-index"
                      boost = 1
                    }
                  }
                }
              ]
            }
          }
          aggregations = {
            metric_value = {
              "sum": {
                field = "value"
              }
            }
          }
        }
      }
    }]
    
    triggers = [{
      query_level_trigger = {
        id = "custom-trigger-id-123456"  // Can be self-generated
        name = "example-trigger"
        severity = "1"
        condition = {
          script = {
            source = "return ctx.results[0].aggregations.metric_value.value < 100"
            lang = "painless"
          }
        }
        actions = [{
          id = "custom-action-id-123456"  // Can be self-generated
          name = "notify_sns"
          destination_id = "your-destination-id"
          message_template = {
            source = "Alert: Value below threshold"
            lang = "mustache"
          }
          subject_template = {
            source = "[ENV] Metric Alert: example-monitor"
            lang = "mustache"
          }
          throttle_enabled = false
        }]
      }
    }]
  })
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    Status

    🆕 New

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions