When building apply rules for your Icinga 2 configuration there are a few common pitfalls you should avoid:

1. Using apply when you’re really just creating a single object

Rule-based configs are great at simplifying your config. However, there are times when you really just want to create a single object. One common anti-pattern I’ve come across is this:

apply Service "ntp" {
  ...

  assign where host.name == "ntp1.example.org"
}

Now, obviously this will work as intended, however there are two significant problems: Writing a filter rule for a single host is unnecessarily complicated and additionaly there is a significant performance penalty because this rule has to be evaluated for all of your hosts.

A much simpler way to achieve this is to just use a simple object declaration:

object Service "ntp" {
  host_name = "ntp1.example.org"

  ...
}

2. Using too many assign where rules

Apply rules are intended to be used to make your config more general by putting your hosts into certain classes (“all ntp servers”, “all database servers”, etc.) and then assigning services and notifications to each member of a certain class. However, for some reason people sometimes do this instead:

apply Service "web" {
  ...

  assign where host.name == "web1.example.org"
  assign where host.name == "web2.example.org"
  assign where host.name == "web3.example.org"
  ...
  assign where host.name == "web73.example.org"
}

The obvious problem here is that this is a maintenance nightmare – and as we’ve already learned “assign where” rules aren’t exactly free in terms of performance.

Unlike in our first example the solution isn’t to unroll this filter by creating an “object” definition for each of the hosts. Instead you should use some of the great filtering capabilities that come with Icinga 2. Here’s a short list of just some of the filters that are available:

1. CIDR matching (using the cidr_match function)
2. Regular expressions (using the regex function)
3. Wildcard matches (using the match function)
4. and last but not least: custom variables

In this particular example I’m going to use wildcard matching and custom variables:

object Host "web1.example.org" { }
object Host "web2.example.org" { }
object Host "web3.example.org" { vars.no_web_check = false }

apply Service "web" {
  ...

  assign where match("web*.example.org", host.name)
  ignore where host.vars.no_web_check
}

This assumes that all of your “web” hosts should have a “web” service by default. Using “ignore where” we can make sure that certain hosts don’t get the service.

3. Reusing “assign where” filters

This is pretty much the opposite problem compared to our previous example. Instead of using the same filter expression dozens of times in the same apply rule this is about unnecessarily repeating the filter in multiple apply rules:

apply Service "mysql" {
  ...

  // All db hosts except those in the dev subnet
  assign where match("db*.example.org", host.name) && !cidr_match("172.16.23.0/24", host.address)
}

apply Service "postgresql" {
  ...

  // All db hosts except those in the dev subnet
  assign where match("db*.example.org", host.name) && !cidr_match("172.16.23.0/24", host.address)
}


apply Service "mssql" {
  ...

  // All db hosts except those in the dev subnet
  assign where match("db*.example.org", host.name) && !cidr_match("172.16.23.0/24", host.address)
}

Code reuse is a best common practice when it comes to writing software. This also applies to Icinga 2 and makes your config much more maintainable and pleasant to work with.

Here’s how you can re-use your filter expression in multiple apply rules:

globals.is_prod_database_host = function(host) {
  // All db hosts except those in the dev subnet
  return match("db*.example.org", host.name) && !cidr_match("172.16.23.0/24", host.address)
}

apply Service "mysql" {
  ...

  assign where is_prod_database_host(host)
}

apply Service "postgresql" {
  ...

  assign where is_prod_database_host(host)
}


apply Service "mssql" {
  ...

  assign where is_prod_database_host(host)
}

By using descriptive function names you also gain the advantage of making your code… er, config more readable.