AWSイベント監視 - Config + EventBridge + Lambdaでメールカスタマイズ

過去に類似のテーマで、CloudTrailによるイベント監視 + 通知メールカスタマイズをしてみた。今回はイベントソースをAWS Configにしてみる。 ...

December 26, 2021

CloudWatch LogsからS3にエクスポート(Lambda/Python)

CloudWatch Logsから、LmabdaでログをS3にエクスポートする。対象のロググループとバケット内の第一階層を引数で指定するようにした。今回の事例ではエクスポートの範囲は「前日0時〜実行当日の0時」となる。 参考 boto3 API Reference LambdaよりCloudWatchログをS3に保存方法紹介 ちなみに過去マネコンから実行する手順書いた。 CloudWatchLogsからS3へログをエクスポートする 今回の検証に使用したアイテム(個人メモ) アイテム 名称 Lambda用IAMロール lambda_basic_execution Lambda関数 log-export-function S3バケット log-export-xxxxxxxx Lambda用IAMロールの権限はlogsフルアクセスのみ。S3もいると思ってたがなくてもできた。バケットポリシー側で許可しているからか。S3バケット名は環境変数で指定した。ちなみに対象バケットは、動作不可になるためオブジェクトロックを設定しないこと。 S3バケットポリシー(log-export-xxxxxxxx) { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "logs.ap-northeast-1.amazonaws.com" }, "Action": "s3:GetBucketAcl", "Resource": "arn:aws:s3:::log-export-xxxxxxxx" }, { "Effect": "Allow", "Principal": { "Service": "logs.ap-northeast-1.amazonaws.com" }, "Action": "s3:PutObject", "Resource": "arn:aws:s3:::log-export-xxxxxxxx/*", "Condition": { "StringEquals": { "s3:x-amz-acl": "bucket-owner-full-control" } } } ] } Lambdaコード(Python3.9) import boto3 import collections from datetime import datetime, date, time, timedelta import os def lambda_handler(event, context): log_g = event....

December 19, 2021

Terraform loop処理の応用編(4) - Lambda

今日のTerraform loopネタはLambda関数作成。ログ監視の一貫なので、CloudWatchLogsのロググループとサブスクリプションフィルタ作成も一緒にやる。 この例でのディレクトリ構成は以下の通り。lambda/upload配下のzipファイルはTerraformにより生成されたもので、初回は空である。 work_dir ├── config.tf #初期化ファイル ├── lambda │ ├── code │ │ ├── func001 │ │ │ └── lambda-func001.py │ │ ├── func002 │ │ │ └── lambda-func002.py │ │ └── func003 │ │ └── lambda-func003.py │ └── upload │ ├── lambda-func001.zip │ ├── lambda-func002.zip │ └── lambda-func003.zip ├── lambda.auto.tfvars ├── lambda_cwl.tf ├── terraform.tfvars #regionのみ定義 └── variables.tf 最初に、すべて定数で記述したパターン。 lambda_logs.tf(定数バージョン) ################################################# # Lambda archive data ################################################# data "archive_file" "data-lambda-func001" { type = "zip" source_dir = "lambda/code/func001" output_path = "lambda/upload/lambda-func001....

December 12, 2021

AWSイベント監視 - CloudTrail + EventBridge + Lambdaでメールカスタマイズ(2)

以下過去記事の続き。この時はメール本文がJSON生データで送信された。これを、人間が状況を判別可能な状態までもっていきたい。 AWSイベント監視 - CloudTrail + EventBridge + Lambdaでメールカスタマイズ とういことで、再度検証。使用したAWS各種サービスのリソースは前回と同様で、Lambda関数のコードだけ入れ替え。何度も同じようなことをやっていて何がなんだか分からなくなっているがもうヤケクソ。 lambda_function.py (イベント監視メール通知用) import boto3 import json import os import re from botocore.exceptions import ClientError from datetime import datetime, timezone, timedelta from dateutil import parser print('Loading function') sns_arn = os.environ['SNS_TOPIC_ARN'] def lambda_handler(event, context): data = event s = json.dumps(data) e = json.loads(s) print(e) # eventから項目を抽出 dtl = e['detail'] #詳細(detail)を定義 e_type = e['detail-type'] # イベントタイプ 例:'AWS API Call via CloudTrail' t = e['time'] # 発生時刻 evt_name = dtl['eventName'] # イベント名 例:DeleteBucket evt_src = dtl['eventSource'] # イベントソース 例:s3....

November 21, 2021

CloudWatchアラーム + SNSからのメール本文をカスタマイズする(3)

表題のテーマ、過去にもCloudWatchアラーム通知メールのカスタマイズについて書いたが、表示時刻がUTCなのでJSTに変換しようと考えた。 過去記事 CloudWatchアラーム + SNSからのメール本文をカスタマイズする(2) CloudWatchアラーム + SNSからのメール件名をカスタマイズする CloudWatchアラームから渡されるeventの、元データの時刻表示は例えば'2021-10-24T09:35:10Z’となっている。これをJSTにするのに手っ取り早いのはpytzを使う方法だが、諸事情により標準ライブラリの範囲でやる必要がある。 で、試行錯誤。当初datetime型にしてからJSTに変換しようとしたがいいやり方が見つからなかったため「unixタイムスタンプに変換後、JSTに変換」とすることにした。 from datetime import datetime, timezone, timedelta from dateutil import parser JST = timezone(timedelta(hours=+9), 'JST') utcstr = '2021-10-24T09:35:10Z' utcstr_parsed = parser.parse(utcstr) #UNIXタイムスタンプに変換 ux_time = utcstr_parsed.timestamp() #int型にする epoch = int(ux_time) #JSTに変換 dt = datetime.fromtimestamp(epoch).replace(tzinfo=timezone.utc).astimezone(tz=JST) print(dt) 2021-10-25 03:35:10+09:00 当初JSTに変換した後の時間が変だ+18時間になってる何故だ、と悩んだが、拠点にした時間から+18時間になるのはおそらく実行環境がJSTだから。UTCの環境でやれば+9時間になるんだろう。くそ、こんなことで数時間週末を無駄にした。俺の休息時間はいつなんだ? ともあれ、修正したのが以下。コメントの「時刻変換」と、「件名に投入するアラーム名を抽出」を追加した。前回はメール件名規則を「任意の文字列 + 発生契機 + 対象リソース(dimention)」としていたが、発生契機はいらないから代わりにアラーム名にした。 lambda_function.py (時刻表示JSTバージョン) import boto3 import json import os import re from botocore.exceptions import ClientError from datetime import datetime, timezone, timedelta from dateutil import parser print('Loading function') sns_arn = os....

November 20, 2021

CloudWatchアラーム + SNSからのメール本文をカスタマイズする(2)

表題の件、過去記事ではメール件名カスタマイズが主題だったが、メール本文を人間が判読可能なフォーマットにすべく、Lambdaコードを改良してみた。これがSNSに渡り、整形された本文がメール送信される想定である。前回はメール件名を環境変数にしたが今回はコード内から値を取り出している。 類似の過去記事 CloudWatchアラーム + SNSからのメール件名をカスタマイズする lambda_function.py import boto3 import json import os import re from botocore.exceptions import ClientError print('Loading function') sns_arn = os.environ['SNS_TOPIC_ARN'] def lambda_handler(event, context): data = event s = json.dumps(data) e = json.loads(s) print(e) # eventから項目を抽出 t = e['time'] trig = e['detail-type'] alarm = e['resources'] #これはリスト。文字列にするにはalarm[0] # 「理由」となる詳細抽出 reason = e['detail']['state']['reason'] # リソース(ここではインスタンスID)を抽出し、文字列整形 resource = e['detail']['configuration']['metrics'][0]['metricStat']['metric']['dimensions'] res_str = json.dumps(resource) res = re.sub(r"[{}\"]", "", res_str) # 件名整形 subject_str = "本番環境 - アラーム " + trig + " - " + res # メッセージ本文整形 fix_msg = "以下のアラームが発生しました" + "\n" trig_msg = "発生契機:" + "\n" + trig time_msg = "発生時刻:" + "\n" + t alm_msg = "アラーム:" + "\n" + alarm[0] res_msg ="対象リソース:" "\n" + res dtl_msg ="理由:" "\n" + reason msg = fix_msg + "\n\n" + trig_msg + "\n\n" + time_msg + "\n\n" + alm_msg + "\n\n" + res_msg + "\n\n" + dtl_msg try: sns = boto3....

November 14, 2021

CloudWatchLogsのログ監視 - サブスクリプションフィルタ + Lambdaでメール送信(2)

表題の件、以下の過去記事に書いたが、この時点では送信される本文ががログメッセージだけとなっていて、通知メールとしては不十分なため本文もカスタマイズしてみた。 CloudWatchLogsのログ監視 - サブスクリプションフィルタ + Lambdaでメール送信 各種設定は冒頭の過去記事と同様のため割愛するとして、コードは変更前・後両方載せておく。 変更前:lambda_function.py(1) import base64 import json import zlib import datetime import os import boto3 from botocore.exceptions import ClientError print('Loading function') def lambda_handler(event, context): data = zlib.decompress(base64.b64decode(event['awslogs']['data']), 16+zlib.MAX_WBITS) data_json = json.loads(data) log_entire_json = json.loads(json.dumps(data_json["logEvents"], ensure_ascii=False)) log_entire_len = len(log_entire_json) print(log_entire_json) for i in range(log_entire_len): log_json = json.loads(json.dumps(data_json["logEvents"][i], ensure_ascii=False)) try: sns = boto3.client('sns') #SNS Publish publishResponse = sns.publish( TopicArn = os.environ['SNS_TOPIC_ARN'], Message = log_json['message'], Subject = os.environ['ALARM_SUBJECT'] ) except Exception as e: print(e) 参考...

November 14, 2021