Using Contact Routing in Sensu Enterprise

Prerequisites

Before diving into this guide, we recommend having the following components ready:

If you’ve not already signed up for Sensu Enterprise, you can do so via this link.

In this guide, we’ll be using Slack and Email handlers.

Overview

Every incident, outage or event has an ideal first responder: a team or individual with the knowledge to triage and address the issue. Sensu Enterprise contact routing makes it possible to assign checks to specific teams and individuals, reducing mean time to response and recovery (MTTR). Contact routing works with all of the Sensu Enterprise third-party notification and metric integrations.

NOTE: Sensu Enterprise supports contact routing for all integrations except EC2, Puppet, Chef, Flapjack, and Event Stream.

In this guide we’ll cover configuring and using Sensu Enterprise Contact Routing.

Contact Routing Basics

In Sensu Enterprise Contact Routing, contacts are composed of a name and configuration overrides for one or more of Sensu Enterprise’s built-in integrations or handler configurations. A contact in Sensu Enterprise is similar to a contact on your phone, in which it has a name and one or more identifiers for various communication channels (e.g. phone number, email address, etc). As an example, a Sensu contact may have the following attributes: email address, slack channel, pagerduty service_key, etc.

Contact Configuration

Configuring a contact requires you to define the name of the contact and the services plus configuration override(s) for that contact under the contacts scope.

{
  "contacts": {
    "support": {
      "email": {
        "to": "support@sensuapp.com"
      },
      "slack": {
        "channel": "#support"
      }
    }
  }
}

In the example above we have a contact named support. For support we have an override configuration for email and slack. In our example we’re having this contact email support@sensuapp.com instead of the email integration’s default to address. For Slack, we’re defining the channel the event should post to, in this case #support.

Check Configuration

Once a contact has been defined, we can now specify which contact(s) to use in a check configuration. The use case for having a contact defined on a check could be that the check is owned by a particular team or group. For instance:

{
  "checks": {
    "example_check": {
      "command": "example_script.rb",
      "handlers": [
        "email"
      ],
      "contacts": [
        "support"
      ]
    }
  }
}

Client Configuration

A client definition may include a list of contacts. Providing a list of contacts in the client definition ensures that those contacts receive notifications for check results which do not explicitly define a list of contacts.

NOTE: Contacts specified in a check definition override contacts specified in the client definition for any check results generated by that client/check pair.

{
  "client": {
    "name": "ec2-west-id-12345",
    "address": "8.8.8.8",
    "subscriptions": [
      "production",
      "aws"
    ],
    "contact": "aws-team"
  }
}

Example Implementations

The following section describes different scenarios and illustrates how contact routing affects notifications being sent in each. We’ll be using Email, Slack and PagerDuty as our example integrations.

Global Configuration

Below is the base configuration for the integrations we’ll be working with in our examples:

{
  "slack": {
    "webhook_url": "https://hooks.slack.com/services/foo/bar/foobar",
    "username": "sensu",
    "channel": "#alerts",
    "timeout": 10
  },
  "email": {
    "smtp": {
      "address": "smtp.example.com",
      "port": 587,
      "openssl_verify_mode": "none",
      "enable_starttls_auto": true,
      "authentication": "plain",
      "user_name": "postmaster@example.com",
      "password": "SECRET"
    },
    "to": "default@example.com",
    "from": "noreply@example.com",
    "timeout": 10
  },
  "pagerduty": {
    "service_key": "examplekey ",
    "timeout": 10
  }
}

Single Handler

Single Handler with a Single Matching Contact

For our first configuration we have a single handler email and a contact support configured. The support contact has a configuration override for email that will change the default to email address from “default@example.com” to “support@sensuapp.com”.

Check configuration:

{
  "checks": {
    "example_check": {
      "command": "example_script.rb",
      "handlers": [
        "email"
      ],
      "contacts": [
        "support"
      ]
    }
  }
}

Contact configuration:

{
  "contacts": {
    "support": {
      "email": {
        "to": "support@sensuapp.com"
      }
    }
  }
}

Because the support contact’s configuration provides overrides matching the handler being used, any event generated for this check will use this contact’s configuration.

Contact routing example of a single handler with a single matching contact

Single Handler with a Single Non-Matching Contact

This configuration has a single handler email and a contact support configured. The support contact does not have a configuration override for email.

Check configuration:

{
  "checks": {
    "example_check": {
      "command": "example_script.rb",
      "handlers": [
        "email"
      ],
      "contacts": [
        "support"
      ]
    }
  }
}

Contact configuration:

{
  "contacts": {
    "support": {
      "pagerduty": {
        "service_key": "foobarkey"
      }
    }
  }
}

Although the contact support is defined under contacts and specified correctly in the check definition, the contact does not provide configuration for the email integration. This means no change will be made to the default email configuration. In this case, an email will be sent to “default@example.com”.

Contact routing example of a single handler with a single non-matching contact

Single Handler with Multiple Matching Contacts

In this configuration we have a single handler, email, and multiple contacts, support and dev, configured. The support and dev contacts have a configuration override for email handler to send their emails to “support@sensuapp.io” and “dev@sensuapp.io” respectively.

Check configuration:

{
  "checks": {
    "example_check": {
      "command": "example_script.rb",
      "handlers": [
        "email"
      ],
      "contacts": [
        "support",
        "dev"
      ]
    }
  }
}

Contact configurations:

{
  "contacts": {
    "support": {
      "email": {
        "to": "support@sensuapp.io"
      }
    },
    "dev": {
      "email": {
        "to": "dev@sensuapp.io"
      }
    }
  }
}

In this instance, although we are using only one handler email, Sensu runs the handler multiple times to send an email to “support@sensuapp.io” and to “dev@sensuapp.io”.

Contact routing example of a single handler with multiple matching contacts

Single Handler with Multiple Non-Matching Contacts

For this configuration we have a single handler, email, and multiple contacts, support and dev, configured. The support contact has an override configuration for slack and the dev contact has an override configuration for pagerduty, neither contact has an override configuration for email.

Check configuration:

{
  "checks": {
    "example_check": {
      "command": "example_script.rb",
      "handlers": [
        "email"
      ],
      "contacts": [
        "support",
        "dev"
      ]
    }
  }
}

Contact configurations:

{
  "contacts": {
    "support": {
      "slack": {
        "channel": "#support"
      }
    },
    "dev": {
      "pagerduty": {
        "service_key": "foobarkey"
      }
    }
  }
}

In this instance, since the contacts defined do not have an override configuration for email, the default configuration for email will be used. In our example an email will be sent to “default@example.com

Contact routing example of a single handler with multiple non-matching contacts

Single Handler with Some Matching Contacts

In this configuration we have a single handler, email, and multiple contacts, support, dev and eng, configured. The support contact has an override configuration for email, the dev contact has an override configuration for pagerduty, and the eng contact has an override configuration for slack.

Check configuration:

{
  "checks": {
    "example_check": {
      "command": "example_script.rb",
      "handlers": [
        "email"
      ],
      "contacts": [
        "support",
        "dev"
      ]
    }
  }
}

Contact configurations:

{
  "contacts": {
    "support": {
      "email": {
        "to": "support@sensuapp.io"
      }
    },
    "dev": {
      "pagerduty": {
        "service_key": "foobarkey"
      }
    },
    "eng": {
      "slack": {
        "channel": "#engineering"
      }
    }
  }
}

In this instance, Sensu generates two emails: one email to the support contact email at “support@sensuapp.io” and the second email to our global configuration email at “default@example.com”.

The reason for this is that since we have one or more contacts that do not have a configuration override for email, Sensu uses the default configuration.

Contact routing example of a single handler with some matching contacts

Multiple Handlers

Multiple Handlers with a Matching Contact

In this configuration we have two handlers, email and slack, and a contact, support, configured. The support contact has a configuration override for email that changes the default to email address from “default@example.com” to “support@sensuapp.io” and a configuration override for slack to change the channel from “#alerts” to “#support”

Check configuration:

{
  "checks": {
    "example_check": {
      "command": "example_script.rb",
      "handlers": [
        "email",
        "slack"
      ],
      "contacts": [
        "support"
      ]
    }
  }
}

Contact configuration:

{
  "contacts": {
    "support": {
      "email": {
        "to": "support@sensuapp.com"
      },
      "slack": {
        "channel": "#support"
      }
    }
  }
}

Because the contact configuration matches both handlers being used, any event generated for this check uses this contact’s configuration.

Contact routing example of multiple handlers with a matching contact

Multiple Handlers with a Single Matching Contact

In this configuration we have two handlers,email and slack, and a contact, support, configured. The support contact only has a configuration override for email that changes the default to email address from “default@example.com” to “support@sensuapp.com”. The support contact does not have a handler override for the slack handler.

Check configuration:

{
  "checks": {
    "example_check": {
      "command": "example_script.rb",
      "handlers": [
        "email",
        "slack"
      ],
      "contacts": [
        "support"
      ]
    }
  }
}

Contact configuration:

{
  "contacts": {
    "support": {
      "email": {
        "to": "support@sensuapp.com"
      }
    }
  }
}

Because the contact configuration only has a configuration override for the email handler, Sensu overrides only the email handler and uses the default configuration for slack.

Contact routing example of multiple handlers with one matching contact

Multiple Handlers with a Single Non-Matching Contact

Similar to the previous configuration, we have two handlers, email and slack, and a contact, support, configured. The support contact has a configuration override for pagerduty that changes the default service_key. The support contact does not have a handler override for the slack or email handler.

Check configuration:

{
  "checks": {
    "example_check": {
      "command": "example_script.rb",
      "handlers": [
        "email",
        "slack"
      ],
      "contacts": [
        "support"
      ]
    }
  }
}

Contact configuration:

{
  "contacts": {
    "support": {
      "pagerduty": {
        "service_key": "foobarkey"
      }
    }
  }
}

Since the contact does not have a configuration override for email or slack, Sensu uses the default configuration for those handlers instead.

Contact routing example of multiple handlers with a single non-matching contact

Multiple Handlers with Multiple Matching Contacts

In this example we have two handlers configured for our check, email and slack. We have two contacts being used, support and dev, and both contacts have configuration override for both email and slack.

Check configuration:

{
  "checks": {
    "example_check": {
      "command": "example_script.rb",
      "handlers": [
        "email",
        "slack"
      ],
      "contacts": [
        "support",
        "dev"
      ]
    }
  }
}

Contact configurations:

{
  "contacts": {
    "support": {
      "email": {
        "to": "support@sensuapp.io"
      },
      "slack": {
        "channel": "#support"
      }
    },
    "dev": {
      "email": {
        "to": "dev@sensuapp.io"
      },
      "slack": {
        "channel": "#dev"
      }
    }
  }
}

Since both contacts match and both contacts have configuration overrides for both handlers, four handler events are generated. Two emails will be sent, one for the support contact tosupport@sensuapp.io” and one to the dev contact todev@sensuapp.io”. The same is true for the slack handler with the support contact creating a message in the “#support” channel and the dev contact creating a message in the “#dev” channel.

Contact routing example of multiple handlers with multiple matching contacts

Multiple Handlers with Multiple Non-Matching Contacts

In this example we have two handlers configured for our check, email and slack. We have two contacts being used, support and dev; both contacts have a configuration override for pagerduty, but do not include any override for slack or email.

Check configuration:

{
  "checks": {
    "example_check": {
      "command": "example_script.rb",
      "handlers": [
        "email",
        "slack"
      ],
      "contacts": [
        "support",
        "dev"
      ]
    }
  }
}

Contact configurations:

{
  "contacts": {
    "support": {
      "pagerduty": {
        "service_key": "supportfoobarkey"
      }
    },
    "dev": {
      "pagerduty": {
        "service_key": "devfoobarkey"
      }
    }
  }
}

Since both contacts match and neither contact provides configuration overrides for either handler, Sensu generates two handler events and uses the default configuration for both.

Contact routing example of multiple handlers with multiple non-matching contacts

Multiple Handlers with Some Matching Contacts

In this example we have two handlers configured for our check, email and slack. We have three contacts being used, support, dev and eng. Support has a contact override for email; dev has a contact override for slack; eng has a contact override for pagerduty which is not a handler that the check is using.

Check configuration:

{
  "checks": {
    "example_check": {
      "command": "example_script.rb",
      "handlers": [
        "email",
        "slack"
      ],
      "contacts": [
        "support",
        "dev",
        "eng"
      ]
    }
  }
}

Contact configurations:

{
  "contacts": {
    "support": {
      "email": {
        "to": "support@sensuapp.io"
      }
    },
    "dev": {
      "slack": {
        "channel": "#dev"
      }
    },
    "eng": {
      "pagerduty": {
        "service_key": "foobarkey"
      }
    }
  }
}

With some contacts providing at least one applicable settings override, we expect Sensu to generate four different handler events.

For the email handler, Sensu sends an email to the support contact override, “support@sensuapp.io”. Because neither the dev nor eng contacts have an override for email, Sensu sends a single additional email to our default email, “default@example.com”.

For the slack handler, similar to our email handler, Sensu creates a message in the #dev channel for our dev contact. Because neither the support or eng contacts provide an override for slack, Sensu sends a single additional notification to our default slack channel, “#alerts”.

Contact routing example of multiple handlers with some matching contacts

Wrapping Up

Contact Routing and Sensu Event Pipeline

When a check is executed and an event is generated, Sensu sends a copy of of the event to each defined handler, allowing the workflow for each handler to be managed independently.

Contact routing and the Sensu monitoring event pipeline

Each handler evaluates the event to determine which contacts should be notified. For each contact defined in the event, the handler generates a notification using either the contact’s override configuration or the default handler configuration. For events which define multiple contacts without applicable overrides, only a single notification is generated using the default handler configuration.

Resources

For further reference please see our Contact Routing documentation. If you find any issues or question, feel free to reach out in our Community Slack, or open an issue on Github.