Datadog Monitor の定義を Terraform で管理できます。

が、Datadog側がJSONで定義されており少々書き難いのと、 Monitor毎に同じ記載を繰り返す部分(通知本文や通知先)をテンプレート化できないものかと思い、考えてみた結果をメモ。

Terraform の Template Provider で実現します。 バージョンによっては動作しないですし、もっと良い方法・書き方が有りそうではあります。

目次

要件

  • 複数 Monitor の通知本文、通知先を一括変更したい
  • 通知先をアラートレベルにより変更したい
  • 通知本文をヒアドキュメント化したい
    • 改行文字(\n)入れつつ、1行で書くのを止めたい

環境

$ terraform version
Terraform v0.11.3
+ provider.datadog v1.0.3
+ provider.template v1.0.0

ファイル構成

使用したファイルは tf-dd-prov-monitor-template に上げています。

.
├── datadog_key.auto.tfvars      # APIKey定義
├── datadog_monitor.auto.tfvars  # 通知先定義
├── datadog_monitor.tf           # provider定義
├── datadog_monitor_template.tf  # template定義
├── ec2.tf
├── templates                    # テンプレートファイル群
│   ├── message.tmpl             # 通知本文用
│   └── notify.tmpl              # 通知先用
├── terraform.tfstate
└── terraform.tfstate.backup

以下、上から順にファイル内容の説明です。

datadog_key.auto.tfvars

Datadog の API Key、 Application Key 値を設定します。 git 管理する場合等は .gitignore に入れる候補になるかと思います。

datadog_api_key=""
datadog_app_key=""

datadog_monitor.auto.tfvars

通知先のリストを設定します。@slack-〜 が通知先です。(例ではSlackのみですがメールアドレス等や他インテグレーションでも良いです)
アラートレベルにより通知先が変更できるようにします。

# all
notify_all = [
  "@slack-alert0",
  "@slack-alert-all",
]
# only alert
notify_is_alert = [
  "@slack-alert1",
  "@slack-alert-only",
]
notify_is_alert_recovery = []
・・・

datadog_monitor.tf

Datadog Provider を定義します。 公式Doc通りです。

# Variables
variable "datadog_api_key" {}
variable "datadog_app_key" {}

# Configure the Datadog provider
provider "datadog" {
  version = "~> 1.0"
  api_key = "${var.datadog_api_key}"
  app_key = "${var.datadog_app_key}"
}

datadog_monitor_template.tf

今回の肝になるテンプレート定義です。 定義した通知先リスト変数を並べて、テンプレートに渡します。

# 通知先リスト変数定義
variable "notify_all" { type = "list" }
variable "notify_is_alert" { type = "list" }
variable "notify_is_alert_recovery" { type = "list" }
・・・
# 通知先リストを文字列置換
locals {
  notify_all_join = "${ length(var.notify_all) == 0 ? "" : join(" ", var.notify_all) }"
  notify_all = " ${local.notify_all_join} "
  # is
  notify_is_alert_join = "${ length(var.notify_is_alert) == 0 ? "" : join(" ", var.notify_is_alert) }"
  notify_is_alert = " {{#is_alert}} ${local.notify_is_alert_join} {{/is_alert}} "
  notify_is_alert_recovery_join = "${ length(var.notify_is_alert) == 0 ? "" : join(" ", var.notify_is_alert) }"
  notify_is_alert_recovery = " {{#is_alert_recovery}} ${local.notify_is_alert_recovery_join} {{/is_alert_recovery}} "
・・・
}
# テンプレートファイルを定義
## 変数渡し無し
data "template_file" "message" {
  template = "${file("./templates/message.tmpl")}"
}
## 変数渡し有り
data "template_file" "notify" {
  template = "${file("./templates/notify.tmpl")}"
  vars {
    notify_all = "${ local.notify_all_join == "" ? "" : local.notify_all }"
    # is
    notify_is_alert = "${ local.notify_is_alert_join == "" ? "" : local.notify_is_alert }"
    notify_is_alert_recovery = "${ local.notify_is_alert_recovery_join == "" ? "" : local.notify_is_alert_recovery }"
・・・
  }
}

templates/

例では2つしか置いてませんが、定義を増やす事でテンプレートは増やせます。

message.tmpl

通知本文用のテンプレート、変数引渡し無しverの例になります。

Metric Value: {{value}} {{comparator}} threshold: {{#is_warning}}{{warn_threshold}}{{/is_warning}}{{#is_warning_recovery}}{{warn_threshold}}{{/is_warning_recovery}}{{#is_alert}}{{threshold}}{{/is_alert}}{{#is_alert_recovery}}{{threshold}}{{/is_alert_recovery}}

- Host: {{host.name}}

notify.tmpl

通知先用のテンプレート例です。変数引渡しを行い、アラートレベルにより通知先リストを設定させます。

More information: [Ops Guide](http://example.com)

Notify:${notify_all}${notify_is_alert}${notify_is_alert_recovery}${notify_is_warning}${notify_is_warning_recovery}${notify_is_recovery}${notify_is_no_data}${notify_is_not_alert}${notify_is_not_alert_recovery}${notify_is_not_warning}${notify_is_not_warning_recovery}${notify_is_not_recovery}${notify_is_not_no_data}

作成例

ec2.tf に datadog_monitor の resource 定義をします。
message、escalation_message にテンプレートを設定(data.template_file.〜.rendered 部分)します。

例では2つの resource を定義し、message に個別の記述と、テンプレート記述設定をしています。

resource "datadog_monitor" "ec2_cpuutilization" {
  type = "query alert"
  name = "[TEST] EC2 CPU Utilization"
  query = "max(last_5m):max:aws.ec2.cpuutilization{name:test-instance-al2-0} by {host,name,region} > 99"
  message = "# [TEST] EC2 CPU Utilization\n${data.template_file.message.rendered}${data.template_file.notify.rendered}"
  escalation_message = "**Renotify**\n# [TEST] EC2 CPU Utilization\n${data.template_file.message.rendered}${data.template_file.notify.rendered}"
・・・
}

resource "datadog_monitor" "ec2_status_check_failed" {
  type = "query alert"
  name = "[TEST] EC2 StatusCheckFailed"
  query = "min(last_15m):max:aws.ec2.status_check_failed{name:test-instance-al2-0} by {host,name,region} > 0"
  message = "# [TEST] EC2 StatusCheckFailed\n${data.template_file.message.rendered}${data.template_file.notify.rendered}"
  escalation_message = "**Renotify**\n# [hoge] EC2 StatusCheckFailed\n${data.template_file.message.rendered}${data.template_file.notify.rendered}"
・・・
}

作成結果

通知本文の一部を共通化し、一括更新を行うことができるようになりました。
懸念は Terraform の更新頻度が高いため、仕様変更により動作しなくなる可能性でしょうか。
Datadog側標準機能でテンプレート化、通知先グループの設定などを実装して欲しい所です。

元記事はこちら

Terraform Datadog Provider を使用した Monitor のテンプレート化