ログにエラーが出たときに AWS Chatbot で Slack に通知してみる
はじめに
CloudWatch のログにエラーなど特定の文字列がある場合に PagerDuty を鳴らすまでも無いけど通知は受けたいというケースがあったので AWS Chatbot を使って Slack に通知をしてみようと思います。
今回の構成
連携の流れは以下です。
- 適当な Lambda を作成して CloudWatch にログを出します。
- カスタムメトリクスフィルターで閾値が1以上になったら SNS topic にメッセージを送信します。
- SNS topic のサブスクリプションとして Slack と連携した Chatbot が Slack に通知します。
サンプルのファイル構成は以下です。
./
├── README.md
├── alarm.tf
├── chatbot.tf
├── functions
│ └── main.py
├── lambda.tf
├── log.tf
├── main.tf
├── metric.tf
├── outputs
│ └── functions.zip
├── topic.tf
└── variables.tf
実際に作成したものは以下で公開しています。
ログにエラーを吐く Lambda を作成する
Lambda がログを出力するロググループを作成します。
resource "aws_cloudwatch_log_group" "default" {
name = "/aws/lambda/chatbot-sample-lambda"
retention_in_days = 7
}
Lamda を作成します。
data "archive_file" "lambda" {
type = "zip"
source_dir = "${path.module}/functions"
output_path = "${path.module}/outputs/functions.zip"
}
resource "aws_lambda_function" "default" {
depends_on = [
aws_cloudwatch_log_group.default,
]
function_name = "chatbot-sample-lambda"
runtime = "python3.12"
handler = "main.lambda_handler"
filename = data.archive_file.lambda.output_path
source_code_hash = data.archive_file.lambda.output_base64sha256
role = aws_iam_role.default.arn
}
resource "aws_iam_role" "default" {
name = "chatbot-sample-lambda-role"
assume_role_policy = data.aws_iam_policy_document.assume_lambda.json
inline_policy {
name = "chatbot-sample-lambda-policy"
policy = data.aws_iam_policy_document.default.json
}
}
data "aws_iam_policy_document" "default" {
statement {
effect = "Allow"
actions = [
"logs:CreateLogStream",
"logs:PutLogEvents"
]
resources = [
"${aws_cloudwatch_log_group.default.arn}:*",
]
}
}
data "aws_iam_policy_document" "assume_lambda" {
statement {
actions = [
"sts:AssumeRole"
]
principals {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
}
}
実際に Lambda で実行する python です。標準出力に Error: xxxxxxxxx
を出力します。
今回はこの Error
の文字で通知を行います。
import json
def lambda_handler(event, context):
print("Error: xxxxxxxxx")
return {
'statusCode': 200,
'body': json.dumps('Chatbot sample lambda function!')
}
SNS Topic を作成する
アラームを受けるトピックを作成します。
resource "aws_sns_topic" "default" {
name = "terraform-chatbot-sample-sns-topic"
}
Chatbot を作成する
Chatbot を作成します。 slack_channel_id
, slack_workspace_id
は引数で受け取ります。
Slack との連携や ワークスペースID、 チャンネルIDの取得方法は次のページを参考にさせていただきました。
resource "awscc_chatbot_slack_channel_configuration" "default" {
configuration_name = "terraform-chatbot-sample-channel"
iam_role_arn = awscc_iam_role.default.arn
slack_channel_id = var.slack_channel_id
slack_workspace_id = var.slack_workspace_id
sns_topic_arns = [aws_sns_topic.default.arn]
}
# Chatbotが使用するIAMロールを作成するリソース
resource "awscc_iam_role" "default" {
role_name = "Terraform-ChatBot-Sample-Channel-Role"
# このポリシーはChatbotサービスがこのロールを引き受けることを許可する
assume_role_policy_document = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "chatbot.amazonaws.com"
}
},
]
})
# このロールにアタッチする管理ポリシーのARN
managed_policy_arns = ["arn:aws:iam::aws:policy/AWSResourceExplorerReadOnlyAccess"]
}
カスタムメトリクスフィルターを作成する
ログに出力された Error
を検知するためにカスタムメトリクスフィルターを作成します。
resource "aws_cloudwatch_log_metric_filter" "default" {
name = "ErrorFromChatbotSampleLambda"
pattern = "Error"
log_group_name = aws_cloudwatch_log_group.default.name
metric_transformation {
name = "ErrorFromChatbotSampleLambda"
namespace = "chatbot-sample-lambda"
value = "1"
unit = "Count"
default_value = "0"
}
}
アラームを作成する
カスタムメトリクスフィルターで Error のカウントが 1 になったら SNS Topic に通知するアラームを作成します。
resource "aws_cloudwatch_metric_alarm" "default" {
alarm_name = "ErrorFromChatbotSampleLambdaAlarm"
alarm_description = "This metric monitors the error from chatbot sample lambda"
evaluation_periods = 1
datapoints_to_alarm = 1
namespace = aws_cloudwatch_log_metric_filter.default.metric_transformation[0].namespace
metric_name = aws_cloudwatch_log_metric_filter.default.metric_transformation[0].name
period = 60
statistic = "Sum"
threshold = 1
comparison_operator = "GreaterThanOrEqualToThreshold"
alarm_actions = [aws_sns_topic.default.arn]
}
Terraform を実行する
今回は variables.tf で以下を指定しているので slack_channel_id
と slack_workspace_id
を指定して plan
, apply
する
variable "region" {
description = "AWS region"
default = "ap-northeast-1"
type = string
}
variable "slack_channel_id" {
description = "value of slack channel id"
type = string
}
variable "slack_workspace_id" {
description = "value of slack workspace id"
type = string
}
% terraform init
% terraform validate
% terraform fmt -recursive
% terraform plan -var 'slack_channel_id=xxxxxxxxxxx' -var 'slack_workspace_id=xxxxxxxxxxxxxxxxx'
% terraform apply -var 'slack_channel_id=xxxxxxxxxxx' -var 'slack_workspace_id=xxxxxxxxxxxxxxxxx'
これで Lambda を実行するとログに Error の文字が出力されて Slack に通知が来る様になります💪
お掃除
不要になったら忘れずにお掃除します。
% terraform destroy -var 'slack_channel_id=xxxxxxxxxxxxxxxxx' -var 'slack_workspace_id=xxxxxxxxxxxxxxxxx'
メモ
AWS Chatbot は AWS Provider で作成することができず、awscc という、AWS Cloud Control API を使用するところで戸惑いました。
また、事前にコンソール上でも同じ構成を作成していたのですが、 デフォルトの状態で画面から作成していくと Chatbot の権限が広めに設定されていたので最小権限にするところで少し悩みました🐹
おわりに
実際にやってみると AWS Chatbot 以外のところは今までやってきたことの組み合わせだったのであまり詰まることなく Terraform 化できました!
Discussion