elasticsearchelastic-stackelasticsearch-watcher

Can I pass an HTTP response from one action to another in Elasticsearch Watch?


I'm trying to register a ticket in a ticketing system and save the ticket ID in an Elasticsearch document using Watcher actions. The ticket ID is passed in the response to the "register new ticket" request, but I don't know how would I access the response from another action.

I would've used input chaining, but I have to iterate through the query results when registering tickets, which, as far as I know, is only possible in actions. I guess what I need is something like action chaining, but I'm not sure if anything like that exists.

Here is the current watch code:

{
  "trigger": {
    "schedule": {
      "interval": "1m"
    }
  },
  "input": {
    "search": {
      "request": {
        "search_type": "query_then_fetch",
        "indices": [
          ".alerts-security.alerts-default"
        ],
        "rest_total_hits_as_int": true,
        "body": {
          "query": {
            "bool": {
              "filter": [
                {
                  "range": {
                    "@timestamp": {
                      "gte": "now-1m",
                      "lte": "now"
                    }
                  }
                },
                {
                  "match": {
                    "kibana.alert.workflow_status": "open"
                  }
                }
              ]
            }
          }
        }
      }
    }
  },
  "condition": {
    "compare": {
      "ctx.payload.hits.total": {
        "gt": 0
      }
    }
  },
  "actions": {
    "otobo_webhook": {
      "transform": {
        "script": {
          "source": """
          ['items': ctx.payload.hits.hits.collect(alert -> [
            'id': alert._id,
            'timestamp': alert._source['@timestamp'],
            'rule_name': alert._source['kibana.alert.rule.name'],
            'rule_note': alert._source['kibana.alert.rule.note'],
            'host_name': alert._source['host.name']
          ])]
        """,
          "lang": "painless"
        }
      },
      "foreach": "ctx.payload.items",
      "max_iterations": 500,
      "webhook": {
        "scheme": ,
        "host": ,
        "port": ,
        "method": ,
        "path": ,
        "params": {},
        "headers": {},
        "body": """
          {
            "UserLogin": ,
            "Password": ,
            "Ticket": {
              "Title": "Elastic Alert (generated by rule {{ctx.payload.rule_name}})",
              /* some more ticket data */
            },
            "Article": {
              "CommmunicationChannel": "Email",
              "From": "test@test.pl",
              "Subject": "Elastic Alert (generated by rule {{ctx.payload.rule_name}})",
              "Body": "<p>Rule \"{{ctx.payload.rule_name}}\" emmitted an alert at {{ctx.payload.timestamp}} with a note: \"{{ctx.payload.rule_note}}\". Device: {{ctx.payload.host_name}}.</p>",
              "ContentType": "text/html charset=utf-8"
            }
          }
          """
      }
    }
  }
}

Now I need to add another action which would use the http response data from the first action.


Solution

  • I haven't been able to use the HTTP response data, but I found a workaround. Since I add the ticket ID to the alert, I query for documents without this field and I'm using input chaining to leave just the 1st result. Now, if there are no alerts found the watch will crash. Otherwise, I register a ticket, still in the input chain and the only thing I perform for the action is update the alert with the ticket ID, simultaneously marking it as handled. The watch handles the alerts one-by-one until there are no left.

    Admittedly this is a hack and absolutely horrendous in terms of performance, but it works for my use case. For more complicated tasks I'm afraid that the Watcher is simply too limited and an external script is necessary.