見出し画像

Lambda+SNSでGmailに通知を行うように設定しました

OpenWeatherMapのAPIを使って天気情報を取得するLambda関数を実装できたので、その結果をAmazon SNSで通知する仕組みを作成しました。
通知先としては利便性を考えるとLINEなどにしたいと思いましたが、まずはすぐに実装できそうなGmailのメールアドレスにしました。

1. Lambda関数にSNSの通知処理を追加

まず、Lambda関数にSNSの通知処理を追加しました。
同じことをCloudWatch Eventを使っても実現できそうですが、今回はboto3でSNSのpublishを実行するようにしました。

def send_notification(sns_topic_arn, message):
    sns = boto3.client('sns')
    sns.publish(
        TopicArn=sns_topic_arn,
        Message=message
    )

上記のメソッドをLambdaの処理に加えました。

import json
import requests
import boto3
from mypy_boto3_ssm import SSMClient
import os
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def get_weather(api_key, location):
    url = f'https://api.openweathermap.org/data/2.5/weather?q={location}&appid={api_key}&units=metric'

    try:
        response = requests.get(url)
        response.raise_for_status()
        weather_data = response.json()

        temperature = weather_data['main']['temp']
        weather_description = weather_data['weather'][0]['description']

        return {
            'temperature': temperature,
            'description': weather_description
        }

    except requests.exceptions.RequestException as e:
        return {'error': str(e)}
    except Exception as e:
        return {'error': str(e)}

def get_api_key():
    profile_name = os.getenv('AWS_PROFILE')
    session = boto3.Session(profile_name=profile_name)

    ssm: SSMClient = session.client('ssm', region_name='ap-northeast-1')

    response = ssm.get_parameter(
        Name='/weather-checker/api-key',
        WithDecryption=True
    )
    return response['Parameter']['Value']

def send_notification(sns_topic_arn, message):
    sns = boto3.client('sns')
    sns.publish(
        TopicArn=sns_topic_arn,
        Message=message
    )

def lambda_handler(event, context):
    api_key = get_api_key()
    location = 'Naha,Okinawa,JP' # 天気情報を取得したい場所。値は外部から受け取れるように変更予定
    sns_topic_arn = os.getenv('SNS_TOPIC_ARN')

    weather_info = get_weather(api_key, location)

    if 'error' in weather_info:
        logger.error(f"Failed to fetch weather data: {weather_info}")
        send_notification(sns_topic_arn, f"Weather fetch failed: {weather_info}")
        return {
            'statusCode': 500,
            'body': json.dumps(weather_info)
        }

    logger.info(f"Successfully fetched weather data: {weather_info}")
    send_notification(sns_topic_arn, f"Current weather in {location}: {weather_info['temperature']}°C, {weather_info['description']}")


    return {
        'statusCode': 200,
        'body': json.dumps({
            'location': location,
            'temperature': weather_info['temperature'],
            'description': weather_info['description']
        })
    }

if __name__ == "__main__":
    result = lambda_handler(None, None)
    print(result)

2. SNSトピックとサブスクリプションの設定

次に、SNSトピックを作成し、指定したメールアドレスに通知が送られるようにSNSサブスクリプションを設定しました。これもTerraformで管理しています。

resource "aws_sns_topic" "weather_notification" {
  name = "weather_notification"
}

resource "aws_sns_topic_subscription" "email_subscription" {
  topic_arn = aws_sns_topic.weather_notification.arn
  protocol  = "email"
  endpoint  = var.to_email
}

メールアドレスはvariables.tfにvariableを定義して、tfvarsファイルから渡すようにしています。

variable "to_email" {
  description = "The email address to notify"
  type        = string
}

3. Lambdaのデプロイと実行

Lambdaで使うPythonファイルに変更を加えたのでECRリポジトリにDockerイメージとしてLambda関数をデプロイしました。デプロイにはMakefileにコマンドを用意して`make deploy`コマンドを使用しました。

# Makefile

# Variables
AWS_REGION=ap-northeast-1
AWS_ACCOUNT_ID ?= $(shell echo $$AWS_ACCOUNT_ID)
ECR_REPOSITORY_NAME=weather-checker
IMAGE_NAME=$(AWS_ACCOUNT_ID).dkr.ecr.$(AWS_REGION).amazonaws.com/$(ECR_REPOSITORY_NAME):latest
AWS_PROFILE ?= aws_sso

# Login to ECR
login:
	@echo "Logging into ECR..."
	aws ecr get-login-password --region $(AWS_REGION) --profile $(AWS_PROFILE) | docker login --username AWS --password-stdin $(AWS_ACCOUNT_ID).dkr.ecr.$(AWS_REGION).amazonaws.com

# Build the Docker image
build:
	@echo "Building Docker image..."
	docker build -t $(ECR_REPOSITORY_NAME) .

# Tag the Docker image
tag:
	@echo "Tagging Docker image..."
	docker tag $(ECR_REPOSITORY_NAME):latest $(IMAGE_NAME)

# Push the Docker image to ECR
push:
	@echo "Pushing Docker image to ECR..."
	docker push $(IMAGE_NAME)

update-lambda-function:
	@echo "Updating Lambda function..."
	aws lambda update-function-code --function-name weather_checker --image-uri $(IMAGE_NAME) --region $(AWS_REGION) --profile $(AWS_PROFILE)


# All-in-one command to build, tag, and push the Docker image
deploy: login build tag push update-lambda-function
	@echo "Docker image successfully pushed to ECR."
make deploy

4. Lambdaのタイムアウト値の変更

デプロイ後にAWSのコンソール画面からLambda関数を実行してみたところ、3秒以内に処理が終わらないことがあったため、タイムアウトの設定を60秒に変更しました。

resource "aws_lambda_function" "weather_checker" {
  timeout = 60
}

5. コンソールからLambdaを実行しSNS通知を確認

AWSコンソールからLambda関数を実行し、SNSトピックを介して指定したメールアドレスに通知が届くことを確認しました。まだシンプルなメッセージを送っているだけですが、特に問題なく動作しました。

sns.amazonaws.comから通知が届いた

終わりに

今回で、AWS LambdaとSNSを組み合わせて、外部APIから天気情報を取得し、SNS経由でその結果をメールで通知するところまで実装できて、ツールとして形になってきました。
次はこのLambdaをスケジュール実行して朝にその日の天気を通知するようにしてみたいと思います。

この記事が気に入ったらサポートをしてみませんか?