Lambda with SES

在 Lambda 裡使用 Python (當然,其他語言亦可)和 SES 服務寄信通知

  • 要先在 SES 裡驗證收信人和發信人的信箱

  • 在 Lambda 頁面設置好環境變數(MYAWS_REGION、MYAWS_ACCESS_KEY_ID、MYAWS_SECRET_ACCESS_KEY、RECIPIENT、SENDER)

  • 如果手上的 IAM 都沒有 SES 的權限,只有自己有的話,可以在 call boto3.client 時加入 ACCESS_KEY_ID 和 SECRET_ACCESS_KEY。如果沒加,會帶入 Lambda 預設的 IAM。

    • 這裡有一個坑,環境變數名字不能命名為 AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY,因為這恰好是 Lambda 會幫忙填好的資訊(即預設的 Lambda IAM ),同名的結果會導致讀不到其他環境變數(原因未知,但不要撞名就對了)
import os
import json
import boto3
from botocore.exceptions import ClientError

def send_mail(subject:str, content:str, defaultIAM:bool=False):
    SENDER = os.environ["SENDER"]
    RECIPIENT = os.environ["RECIPIENT"]
    AWS_REGION = os.environ["AWS_REGION"]
    SUBJECT = subject
    CHARSET = "utf-8"
    if not defaultIAM:
        AWS_ACCESS_KEY_ID = os.environ["AWS_ACCESS_KEY_ID"]
        AWS_SECRET_ACCESS_KEY = os.environ["AWS_SECRET_ACCESS_KEY"]
        client = boto3.client('ses',region_name=AWS_REGION, aws_access_key_id=AWS_ACCESS_KEY_ID, aws_secret_access_key=AWS_SECRET_ACCESS_KEY)
    else:
        client = boto3.client('ses', region_name=AWS_REGION)
    try:
        response = client.send_email(
            Destination={
                'ToAddresses': [
                    RECIPIENT,
                ],
            },
            Message={
                'Body': {
                    'Html': {
                        'Charset': CHARSET,
                        'Data': content,
                    },
                    'Text': {
                        'Charset': CHARSET,
                        'Data': content,
                    },
                },
                'Subject': {
                    'Charset': CHARSET,
                    'Data': SUBJECT,
                },
            },
            Source=SENDER,
        )
    except ClientError as e:
        print(e.response['Error']['Message'])
    else:
        print("Email sent! Message ID:"),
        print(response['MessageId'])
    
def lambda_handler(event, context=None):
    send_mail("SES test", str(event))

send_mail 改寫自doc

Lambda with Slack

  • 在 Lambda 頁面設置好環境變數(SLACK_URL)
  • 因呼叫 slack API 會使用 requests 這個 library,但其並非 python 內建之函式庫,也沒辦法 pip install,解決方案有二:
    • from botocore.vendored import requests,其用法和 requests 一樣,但該函式庫已在 2019 年 10 月被移除,故最新的 python3.8 並不支援,只能使用 python3.6/3.7
    • pip install -t requests .再將 requests 的包和其他程式碼打包成 zip 上傳到 Lambda
import json
from botocore.vendored import requests # or import requests
import os

def slack_notify(url:str, content:str):
    dict_headers = {'Content-type': 'application/json'}
    dict_payload = {'text': content}
    json_payload = json.dumps(dict_payload)
    resp = requests.post(url, data=json_payload, headers=dict_headers)

def lambda_handler(event, context):
    url = os.environ['SLACK_URL']
    slack_notify(url, str(event))

Lambda with CloudWatch Logs

  • 資料會被用 zip 壓縮,並以 base64 編碼,所以要先解碼
  • 一開始拿到的資料應該會長:
{
  "awslogs": {
    "data": "ewogICAgIm1lc3NhZ2VUeXBlIjogIkRBVEFfTUVTU0FHRSIsCiAgICAib3duZXIiOiAiMTIzNDU2Nzg5MDEyIiwKICAgICJsb2dHcm91cCI6I..."
  }
}

解碼後長:

{
    "messageType": "DATA_MESSAGE",
    "owner": "123456789012",
    "logGroup": "/aws/lambda/echo-nodejs",
    "logStream": "2019/03/13/[$LATEST]94fa867e5374431291a7fc14e2f56ae7",
    "subscriptionFilters": [
        "LambdaStream_cloudwatchlogs-node"
    ],
    "logEvents": [
        {
            "id": "34622316099697884706540976068822859012661220141643892546",
            "timestamp": 1552518348220,
            "message": "REPORT RequestId: 6234bffe-149a-b642-81ff-2e8e376d8aff\tDuration: 46.84 ms\tBilled Duration: 100 ms \tMemory Size: 192 MB\tMax Memory Used: 72 MB\t\n"
        }
    ]
}

參考

串接 CloudWatch Logs 的程式碼:

import json
from botocore.vendored import requests # or import request
import os
import base64
import gzip

def slack_notify(url:str, content:str):
    dict_headers = {'Content-type': 'application/json'}
    dict_payload = {'text': content}
    json_payload = json.dumps(dict_payload)
    resp = requests.post(url, data=json_payload, headers=dict_headers)

def lambda_handler(event, context):
    url = os.environ['SLACK_URL']
    cloudwatch_event = event["awslogs"]["data"]
    decode_base64 = base64.b64decode(cloudwatch_event)
    decompress_data = gzip.decompress(decode_base64)
    log_data = json.loads(decompress_data)
    slack_notify(url, str(log_data))