Serverless Framework で .env ファイルを使って Python で記述した Lambda 関数の環境変数を設定する方法

2023.11.07

はじめに

アノテーション テクニカルサポートの川崎です。

社内育成の素材として、Serverless Framework を使用してデプロイするアプリを、アプリ作成の課題として、チームのメンバーに取り組んでもらっております。

その過程で経験したことを、Serverless Framework のデプロイ手順としてまとめてみます。

今回は、トークンやAPIキーを、最低限、コードのバージョン管理に含めずに利用する方法を確認しました。

git で管理するべきでない機密情報を取り扱う際に、よく利用される手段の1つとして .env があります。 Serverless Framework で .env ファイルを使い、環境変数を設定する手順について確認しました。

ご注意

このブログでは機密情報を .env ファイルに平文のまま記載する方法を説明しています。

AWS の SSM パラメータストアや Secrets Manager を利用し、暗号化することで、 トークンやAPIキーをよりセキュアに処理する手段が用意されていますので、必要に応じてご利用ください。

.env を利用する2つの方法

Serverless Framework には serverless-dotenv-plugin というプラグインがあります。 一方で、Serverless Framework の v3 では .env の利用はネイティブサポートされており、 プラグインを使わなくても .env を利用することができます。

以下では、プラグインを使わない方法を説明していきます。

  • .env のネイティブサポートによる方法 ← このブログで説明する方法
  • serverless-dotenv-plugin を使う方法

.env のネイティブサポート (serverless-dotenv-plugin を使わない方法)

serverless.yml で「useDotenv: true」を指定することで、 .env から環境変数を読み込む機能を有効化できます。

それでは、手順を見ていきます。

テンプレート「aws-python3」を指定して、Serverless サービスを新規作成します。

% mkdir dotenv1
% cd dotenv1
% sls create --template aws-python3
% rm handler.py
% touch lambda_function.py

テンプレートから生成された handler.py は削除し、lambda_function.py というファイル名で、Lambda Python のサンプルコードを記述します。

今回は、Slack APIを用いて、指定のチャンネルからメッセージを取得する Python の Lambda 関数を作成します。

Slack API のアプリを作成する手順は、本ブログでは触れませんので、 下記ブログ記事などを参考にしていただければと思います。 (Slack API のアプリのスコープも設定する必要があります。)

Slack に関連する処理は、別ファイル slack_requests.py に切り出しています。

5-6行目で、Lambda 関数の環境変数から TOKEN と CHANNEL_ID の値を取得します。 TOKEN と CHANNEL_ID を指定して、Slack API の conversations.history にアクセスし、メッセージを取得します。

lambda_function.py

from slack_requests import get_messages_from_channel

def lambda_handler(event, context):
    # チャンネルから最新のメッセージを取得する
    messages = get_messages_from_channel()
    latest_message = messages[0] if messages else None
    return None

slack_requests.py

import os
import json
import requests

TOKEN = os.environ["SLACK_BOT_TOKEN"]
CHANNEL_ID = os.environ["CHANNEL_ID"]

def get_messages_from_channel():
    url = "https://slack.com/api/conversations.history"
    headers = {"Authorization": f"Bearer {TOKEN}"}
    payload = {"channel": CHANNEL_ID}
    response = requests.get(url, headers=headers, params=payload)
    return response.json().get("messages")

テンプレートから生成された serverless.yml を以下の内容で上書きします。

4行目で「useDotenv: true」を指定して、.env から環境変数を読み込む機能を有効にしています。 デフォルトでは false です。

14-16行目で、Lambda 関数の環境変数を追加しています。

以上の設定を行うことで、.env ファイルに指定した内容を読み込んで、Lambda 関数の環境変数に指定することができます。便利ですね!

※ .env ファイルに指定する内容は、後述します。

serverless.yml

service: dotenv1
frameworkVersion: '3'

useDotenv: true

provider:
  name: aws
  runtime: python3.11
  region: ap-northeast-1

functions:
  slack-request:
    handler: lambda_function.lambda_handler
    environment:
      SLACK_BOT_TOKEN: ${env:SLACK_BOT_TOKEN}
      CHANNEL_ID: ${env:CHANNEL_ID}

plugins:
  - serverless-python-requirements

serverless.yml の設定ができたら、serverless-python-requirements プラグインをインストールします。

Python を使う場合は、ほぼ必須のプラグインですね!

利用する外部モジュール名を、requirements.txt ファイルに記述しておきます。

% sls plugin install -n serverless-python-requirements
% echo requests > requirements.txt

.env ファイルを作成し、SLACK_BOT_TOKEN、CHANNEL_ID の値を記述します。

% touch .env

.env

SLACK_BOT_TOKEN=xoxb-xxxxxxxxxxxxx-xxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx
CHANNEL_ID=Cxxxxxxxxxx

git 管理から除外するために、忘れずに .gitignore ファイルに「.env」を追記しておきます。

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# Serverless directories
.serverless
.env

上記の設定ができたら、デプロイを実行します。

% sls deploy --aws-profile sls-user

デプロイが成功したら、invoke コマンドで、デプロイした Lambda 関数を実行してみます。

% sls invoke -f slack-request --aws-profile sls-user
Running "serverless" from node_modules
{
    "bot_id": "XXXXXXXXXXX",
    "type": "message",
    "text": "Slack のテストメッセージ",
    "user": "XXXXXXXXXXX",
    "ts": "1698998672.511469",
    "app_id": "XXXXXXXXXXX",
    "blocks": [
        {
            "type": "rich_text",
            "block_id": "xxxxx",
            "elements": [
                {
                    "type": "rich_text_section",
                    "elements": [
                        {
                            "type": "text",
                            "text": "Slack のテストメッセージ"
                        }
                    ]
                }
            ]
        }
    ],
    "team": "XXXXXXXXXXX",
    "bot_profile": {
        "id": "XXXXXXXXXXX",
        "deleted": false,
        "name": "sample-channel1",
        "updated": 1692781566,
        "app_id": "XXXXXXXXXXX",
        "icons": {
            "image_36": "https://a.slack-edge.com/xxxxx/img/plugins/app/bot_36.png",
            "image_48": "https://a.slack-edge.com/xxxxx/img/plugins/app/bot_48.png",
            "image_72": "https://a.slack-edge.com/xxxxx/img/plugins/app/service_72.png"
        },
        "team_id": "XXXXXXXXXXX"
    }
}

Lambda 関数が実行され、指定のチャンネルから最新のメッセージを取得できました!

まとめ

Serverless Framework の v3 では .env の利用方法として、2つの方法があり、 本ブログではネイティブサポートによる方法を説明しました。

  • .env のネイティブサポートによる方法
  • serverless-dotenv-plugin を使う方法

最初は、どちらを使えばいいのかわからず、少々混乱したのですが、 手順としては、シンプルな方法で実現されているのが確認できました。

参考資料

[1] Resolution of environment variables

[2] Serverless Framework - Upgrading to v3

[3] Serverless Framework - AWS Lambda Guide - Serverless.yml Reference

アノテーション株式会社について

アノテーション株式会社は、クラスメソッド社のグループ企業として「オペレーション・エクセレンス」を担える企業を目指してチャレンジを続けています。「らしく働く、らしく生きる」のスローガンを掲げ、様々な背景をもつ多様なメンバーが自由度の高い働き方を通してお客様へサービスを提供し続けてきました。現在当社では一緒に会社を盛り上げていただけるメンバーを募集中です。少しでもご興味あれば、アノテーション株式会社WEBサイトをご覧ください。