LocalStack による AWS 開発環境の構築（WSL2、ZScaler、Lambda、S3、CloudWatch、サブスクリプションフィルタ）

会社で AWS の勉強機会を頂けたので LocalStack(community) を使用した AWS のローカル開発環境構築をした時の手順や詰まった事を記載します。
今回コンテナやAWS、WSLに初めて触れたのでとても勉強になりました。
* 最終的に目指した構成
    * [Lambda関数_1](#lambda関数の作成_1)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/354847f8-35cd-b76c-24d8-1b2fc7830dca.png)
    * [Lambda関数_2](#lambda関数の作成_2)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/87c1edee-547a-587d-4b9c-65f5d0260a78.png)
---

* [制約事項](#制約事項)
* [WSLのセットアップ](#wslのセットアップ)
* [DNSサーバの設定](#dnsサーバの設定)
* [ルート証明書の設定](#ルート証明書の設定)
* [Python関連のインストール](#python関連のインストール)
* [AWSCLIのインストール](#awscliのインストール)
* [dockerのインストール](#dockerのインストール)
* [docker-composeのインストール](#docker-composeのインストール)
* [LocalStackの起動](#localstackの起動)
* [MySQLサーバの起動](#mysqlサーバの起動)
* [Lambda関数の作成_1](#lambda関数の作成_1)
* [Lambda関数の作成_2](#lambda関数の作成_2)

# 制約事項や独自の設定など
* ZScaler が入っているため、DSN サーバの設定やルート証明書の設定を随所で行っています。
これらの設定は通常であれば不要かもしれません。
* LocalStack(community) の制約のため、以下は独自の設定をしています。
    * RDS は PRO のみの機能なので MySQL コンテナを実行して代用しています。
    * サブスクリプションフィルタの --filter-pattern はPRO のみの機能なので空文字(全てを対象)としています。

# WSLのセットアップ
* WSL のインストール・初期設定を行います。
## Ubuntu 20.04 をインストールします。
```powershell
# ディストリビューション一覧を表示
wsl --list --online
# 今回は Ubuntu-20.04 をインストール
# 途中ユーザ名やパスワードを入力します
wsl --install --distribution Ubuntu-20.04
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/d83cb11a-d7d1-d035-651f-a0f828f842ca.png)

## root パスワードを設定しておきます。
```bash
# root ユーザのパスワード変更
sudo passwd root
# 変更したパスワードを使って root ユーザでログイン出来る事を確認
su
# ログイン出来たら root からログアウトする。
logout
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/6cd5861b-eaaf-7661-fa49-6f5638472f4d.png)
* root パスワードが分からなくなった場合、powershell から root ユーザで入って変更する事が出来ます。
```powershell
wsl --user root
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/dc1e8e16-fd6e-dfec-2289-aac9df290760.png)

# DNSサーバの設定
* ZScaler が入っている場合、名前解決出来ないようなので DNS 設定を行いました。
不要であればスキップで問題ありません。
## resolv.conf が自動生成（初期化）されないようにします。
```bash
sudo nano /etc/wsl.conf
```
```/etc/wsl.conf
[network]
generateResolvConf = false
```
## 名前解決出来ないため DNS サーバを設定します。
```bash
# DNS サーバの設定を書き換えます。
sudo nano /etc/resolv.conf
```
```/etc/resolv.conf
nameserver 8.8.8.8
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/6fdd70c5-f993-6fdf-4446-7627861f4dd3.png)

# ルート証明書の設定
* ZScaler などを入れている場合 SSL 通信に失敗するため、ZScaler のルート証明書を WSL に適用する必要があります。
不要であればスキップで問題ありません。
## エクスポート手順
1. win+R から「certlm.msc」を開く
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/86b0e167-a78e-f42e-de06-e1f1dadbcf00.png)
1. 「信頼されたルート証明機関」→「証明書」→「Zscaler Root CA」をダブルクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/feb10cfc-32f9-cffc-87f4-176fbdfeb211.png)
1. 「詳細」→「ファイルにコピー」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/4e618174-659b-c546-1507-3cf320dfd1a2.png)
1. 「次へ」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/6d9573f0-13a4-2bcb-7518-7d546e3b44d8.png)
1. 「Base 64 encoded X.509(.CER)(S)」→「次へ」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/756ffe5b-981e-f68a-cf5d-bb0e9ad4d881.png)
1. 任意の保存先・ファイル名を入力して「次へ」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/b73e69a4-f8be-a343-28c1-b73a63ea7392.png)
1. 「完了」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/a4f2650f-c278-c4d1-833f-a80af8fa72cf.png)
## インポート手順
1. 証明書を WSL へコピーする。（/mnt/c/ は Windows 側の C ドライブになります。）
    ```bash
    # .crt に変更して保存する。
    sudo cp -p /mnt/c/zscaler.cer /usr/local/share/ca-certificates/zscaler.crt
    ```
1. 証明書を読み込む
    ```bash
    # 証明書を更新する。
    sudo update-ca-certificates --fresh
    # 試しに SSL 通信を実行する。
    curl -O https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh
    ```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/37dc0b49-732e-1093-d4eb-9fe8564c8e26.png)

# Python関連のインストール
* pyenv, Python, poetry をインストールします。
## apt のソースリストを変更します。
※私は頻繁にタイムアウトになってしまったので更新しましたが、不要かもしれません。
```
# バックアップ
sudo cp  /etc/apt/sources.list /etc/apt/sources.list.backup
# 中身を更新
sudo nano /etc/apt/sources.list
```
```/etc/apt/sources.list
deb http://archive.ubuntu.com/ubuntu/ focal main restricted universe multiverse
deb-src http://archive.ubuntu.com/ubuntu/ focal main restricted universe multiverse

deb http://archive.ubuntu.com/ubuntu/ focal-updates main restricted universe multiverse
deb-src http://archive.ubuntu.com/ubuntu/ focal-updates main restricted universe multiverse

deb http://archive.ubuntu.com/ubuntu/ focal-security main restricted universe multiverse
deb-src http://archive.ubuntu.com/ubuntu/ focal-security main restricted universe multiverse

deb http://archive.ubuntu.com/ubuntu/ focal-backports main restricted universe multiverse
deb-src http://archive.ubuntu.com/ubuntu/ focal-backports main restricted universe multiverse

deb http://archive.canonical.com/ubuntu focal partner
deb-src http://archive.canonical.com/ubuntu focal partner
```
## パッケージ一覧を更新しておきます。
```bash
sudo apt update
sudo apt upgrade
```
## pyenv をインストールします。
```bash
curl https://pyenv.run | bash
```
## メッセージ通りに「~/.bashrc」に追記します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/a13cc4d8-f8ec-7c85-8a25-e740fe70e3a4.png)
```bash
sudo nano ~/.bashrc
source ~/.bashrc
# pyenv のバージョンを確認します。
pyenv --version
```
```~/.bashrc
(省略)
export PYENV_ROOT="$HOME/.pyenv"
command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
```
## pyenv が Python 導入時のビルドで使用するパッケージをインストールしておきます。
[Home · pyenv/pyenv Wiki · GitHub](https://github.com/pyenv/pyenv/wiki)
```bash
sudo apt update; sudo apt install build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev curl libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev
```
## pyenv を使って Python をインストールします。
```bash
# インストール可能な Python の一覧を出力します。
pyenv install --list | grep "3\.8\."
# 今回は 3.8.16 をダウンロードします。
pyenv install 3.8.16
# システムで使用する Python バージョンを設定します。
pyenv global 3.8.16
# Python のバージョンを確認します。
pyenv version
# Python が利用する SSL 証明書を設定します。
# (上で配備した zscaler.crt を指定。通常の環境では不要)
pip config set global.cert /usr/local/share/ca-certificates/zscaler.crt
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/a26c5aa3-91a7-c092-62b8-fabc5319aa64.png)
## poetry をインストールします。
```bash
curl -sSL https://install.python-poetry.org | python -
```
```powershell
# powershell から一度シャットダウンします。
wsl --shutdown
```
```bash
# シャッドダウン後 WSL でバージョンを確認します。
poetry --version
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/70d12a98-7523-1db0-0332-a9267e2af44b.png)

# AWSCLIのインストール
* CLI で AWS を操作するためのコマンドをインストールします。
## aws cli をインストールします。
```bash
sudo apt install awscli
```
## asc cli local をインストールします。
* localstack に向けて aws コマンドを実行する際に利用します。
```bash
pip install awscli-local==0.20
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/db2c0d3b-672b-cd13-00bf-0234dec294af.png)

# dockerのインストール
* 手順に従ってインストールします。
[Running Docker on WSL2 without Docker Desktop (the right way)](https://dev.to/felipecrs/simply-run-docker-on-wsl2-3o8)
下記に公式の手順もありますが、上記の手順はサービス立ち上げの自動化なども載っているのでそちらでインストールしました。
[Install Docker Engine on Ubuntu | Docker Documentation](https://docs.docker.com/engine/install/ubuntu/)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/1bd14de6-197e-02a4-ece4-6da657611262.png)

# docker-composeのインストール
* 手順に従ってインストールします。
[How To Install and Use Docker Compose on Ubuntu 20.04](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-compose-on-ubuntu-20-04)
    * インストールする際のバージョンは git から最新版のURLを取得しました。
        ```bash
        sudo curl -L "https://github.com/docker/compose/releases/download/v2.17.3/docker-compose-linux-x86_64" -o /usr/local/bin/docker-compose
        ```
        ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/1fdaf04c-c684-fe6e-dde9-ad13c10c608b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/0b75b782-2081-c4aa-4f9e-1da92dda2f31.png)

# LocalStackの起動
* AWS をローカルで再現するために LocalStack を起動します。
```bash
mkdir localstack
cd localstack
# 設定ファイルを編集します。
nano docker-compose.yml
# コンテナを起動します。
# docker-compose up はログ確認に使います。
# ログ確認が不要な場合は docker-compose up -d で起動するとバックグラウンドで実行されます。
docker-compose up
# 別ターミナルで起動している事を確認します。
curl http://localhost:4566/health
```
```docker-compose.yml
version: "3.8"

services:
  localstack:
    image: localstack/localstack:2.0.1
    ports:
      - "127.0.0.1:4566:4566"            # LocalStack Gateway​
    environment:
      - DEBUG=1
      - DOCKER_HOST=unix:///var/run/docker.sock
      # マウント先のSSL証明書を設定します。
      - REQUESTS_CA_BUNDLE=/usr/local/share/ca-certificates/zscaler.crt
      - CURL_CA_BUNDLE=/usr/local/share/ca-certificates/zscaler.crt
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
      # LocalStackコンテナから SSL 通信をする際にSSL証明書が必要になるのでマウントします。
      - "/usr/local/share/ca-certificates/zscaler.crt:/usr/local/share/ca-certificates/zscaler.crt"
    networks:
      aws-network:
        aliases:
          # エイリアスを設定しておきます。
          - localstack
# コンテナを所属させるネットワークを設定します。
networks:
  aws-network:
    name: aws-network
    driver: bridge
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/e87ac594-ecca-befb-bb73-83f9da21d3dd.png)

## MySQLサーバの起動
* RDS は LocalStackPro のみの機能なので、別コンテナで DB を立ち上げます。
```bash
mkdir mysql
cd mysql
# 設定ファイルを編集します。
nano docker-compose.yml
# コンテナを起動します。
docker-compose up -d
# MySQL client をインストールします。
sudo apt install mysql-client
# MySQLに接続出来る事を確認します。
mysql --user mysql --port 3306 --host 0.0.0.0 --password
```
```docker-compose.yml
version: "3.8"

services:
  mysql:
    platform: linux/x86_64
    image: mysql:latest
    restart: always
    container_name: mysql

    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: mysql_db
      MYSQL_USER: mysql
      MYSQL_PASSWORD: mysql
      MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
      TZ: Asia/Tokyo
    ports:
      - 3306:3306
    volumes:
      - ./docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
      - ./db:/var/lib/mysql
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    # LocalStackと同じネットワークに設定しておく。
    networks:
      - aws-network
networks:
  aws-network:
    name: aws-network
    driver: bridge
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/15c9eb17-1eed-611e-ddb2-259c6af474d5.png)

# Lambda関数の作成_1
* S3, MySQL との接続確認を行う Lambda 関数を作成します。
## Python プロジェクトを作成します。
```bash
mkdir lambda_test
cd lambda_test
poetry init
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/4fac4eaa-ecda-9fc8-f882-112b8247c6c3.png)

## Lambda で実行する Python ファイルを作成します。
```bash
mkdir lambda_test
nano lambda_test/lambda_test.py
```
```lambda_test.py
import mysql.connector
import json
import boto3
from boto3.session import Session
from datetime import datetime

import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)

DB_USER = "mysql"
DB_PASS = "mysql"
DB_NAME = "mysql_db"
DB_HOST = "mysql"
DB_PORT = "3306"
DB_TABLE = ""

session = Session(aws_access_key_id='dummy',
    aws_secret_access_key='dummy',
    region_name='ap-northeast-1'
)
# endpoint_url は docker-compose.yml で指定したエイリアスを使っています。
s3 = session.resource(service_name='s3', endpoint_url='http://localstack:4566')

def lambda_handler(event, context):
    conn = None
    results = None
    logger.info(event)
    try:
        # MySQL の接続テスト
        conn = mysql.connector.connect(
            user=DB_USER,
            password=DB_PASS,
            database=DB_NAME,
            host=DB_HOST,
            port=DB_PORT
        )
        cur = conn.cursor()
        cur.execute(f"SELECT NOW();")
        results = cur.fetchall()
        for result in results:
            date_time = f"{result[0]}"
            logger.info(date_time)
        # S3 の接続テスト
        for bucket in s3.buckets.all():
            logger.info(bucket.name)
    except Exception as e:
        logger.info(f"Error Occurred: {e}")
    finally:
        if conn is not None and conn.is_connected():
            conn.close()
    return {
        "statusCode": 200,
        "body": json.dumps({
            "res": date_time,
            "event": event
            }),
    }
```
## poetry でパッケージを追加します。
```bash
# poetry で新しくプロジェクトを作成すると SSL 通信で利用する証明書の再設定が必要なので、プロジェクトを作成したら下記を実行します。(恐らく ZScaler 使用の場合のみ)
poetry source add --default pypiorg https://pypi.org/simple/
poetry source add files https://files.pythonhosted.org/
poetry config certificates.pypiorg.cert /usr/local/share/ca-certificates/zscaler.crt
poetry config certificates.files.cert /usr/local/share/ca-certificates/zscaler.crt
# パッケージを追加します。
poetry add mysql-connector-python boto3
```
## ビルドを行うbuild.sh を作成・実行します。
* 必要なパッケージと作成したlambda_test.pyをzipにまとめます。
```bash
nano build.sh
sudo apt install zip unzip
sh build.sh
```
```build.sh
poetry export -f requirements.txt --output requirements.txt;
pip install -r requirements.txt --target ./packages;
cd packages/;
zip -r ../package.zip .;
cd ..;
cp lambda_test/lambda_test.py lambda_test.py; zip -g package.zip lambda_test.py; rm -rf lambda_test.py;
```

## S3の作成・Lambda 関数をアップロードする aws_create.sh を作成・実行します。
```bash
nano aws_create.sh
# 実行前に LocalStack を実行しておく。
sh aws_create.sh
```
```aws_create.sh
awslocal s3 mb s3://my-bucket
awslocal lambda create-function \
  --function-name test_s3_mysql_lambda_function \
  --timeout 3 \
  --runtime python3.8 \
  --zip-file fileb://package.zip \
  --handle lambda_test.lambda_handler \
  --role arn:aws:iam::000000000000:role/lambda-role
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/0f045dd5-c057-bea6-6b02-8d5d79991575.png)

## Lambda 関数を起動する aws_invoke.sh を作成・実行します。
```bash
nano aws_invoke.sh
sh aws_invoke.sh
```
```aws_invoke.sh
awslocal lambda invoke --function-name test_s3_mysql_lambda_function result.log
```
LocalStack のログからMySQLへの接続、S3への接続が出来ている事が確認出来ました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/a0efc34a-e8b9-da3d-86a3-972ff5e04d1d.png)

## logs を確認すると CloudWatchLogs にログが書き込まれている事も確認出来ます。
```bash
awslocal logs describe-log-streams --log-group-name /aws/lambda/test_s3_mysql_lambda_function --query "logStreams[].[log
StreamName]" --output text
# --log-stream-name は上記で取得したもの
awslocal logs get-log-events --log-group-name /aws/lambda/test_s3_mysql_lambda_function --log-stream-name '2023/04/27/[$LATEST]cbc1850e7c010e24a4a6740c64fb654c' --query "events[].[message]" --output json | jq
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/5353ce90-9e0a-8901-5b17-fd07e5db7917.png)

# Lambda関数の作成_2
* S3, MySQL の接続に加えて CloudWatch のサブスクリプションフィルタで別の Lambda を起動する処理を追加したシステムを構築します。
* 今回は Lambda 関数が2つ必要なため、プロジェクトを2つ作成します。

## Python プロジェクトを作成します。（1つ目）
* まずは MySQL に接続する Lambda 関数を作成します。
```bash
mkdir lambda_get_timestamp
cd lambda_get_timestamp
poetry init
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/bdbda132-2323-6a25-be93-4fc0b4d81008.png)

## Lambda で実行する Python ファイルを作成します。
```bash
mkdir lambda_get_timestamp
nano lambda_get_timestamp/lambda_get_timestamp.py
```
```lambda_get_timestamp.py
import mysql.connector
import json
from datetime import datetime

import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)

DB_HOST = "mysql"
DB_PORT = "3306"
DB_USER = "mysql"
DB_NAME = "mysql_db"
DB_PASS = "mysql"
DB_TABLE = ""

def lambda_handler(event, context):
    conn = None
    results = None
    try:
        conn = mysql.connector.connect(
            user=DB_USER,
            password=DB_PASS,
            database=DB_NAME,
            host=DB_HOST,
            port=DB_PORT
        )
        if conn.is_connected:
            logger.info("Connected!")
        cur = conn.cursor()
        cur.execute(f"SELECT NOW();")
        results = cur.fetchall()
        for result in results:
            date_time = f"{result[0]}"
            logger.info("GETDATETIME:" + date_time)
    except Exception as e:
        logger.info(f"Error Occurred: {e}")
    finally:
        if conn is not None and conn.is_connected():
            conn.close()
    return {
        "statusCode": 200,
        "body": json.dumps({
            "res": date_time,
            "event": event
            }),
    }
```
## poetry でパッケージを追加します。
```bash
# poetry で新しくプロジェクトを作成すると SSL 通信で利用する証明書の再設定が必要なので、プロジェクトを作成したら下記を実行します。(恐らく ZScaler 使用の場合のみ)
poetry source add --default pypiorg https://pypi.org/simple/
poetry source add files https://files.pythonhosted.org/
poetry config certificates.pypiorg.cert /usr/local/share/ca-certificates/zscaler.crt
poetry config certificates.files.cert /usr/local/share/ca-certificates/zscaler.crt
# パッケージを追加します。
poetry add mysql-connector-python
```
## ビルドを行うbuild.sh を作成・実行します。
* 必要なパッケージと作成した lambda_get_timestamp.py をzipにまとめます。
```bash
nano build.sh
# 先に入れてあれば不要
sudo apt install zip unzip
sh build.sh
```
```build.sh
poetry export -f requirements.txt --output requirements.txt;
pip install -r requirements.txt --target ./packages;
cd packages/;
zip -r ../package.zip .;
cd ..;
cp lambda_get_timestamp/lambda_get_timestamp.py lambda_get_timestamp.py; zip -g package.zip lambda_get_timestamp.py; rm -rf lambda_get_timestamp.py;
```

## Lambda 関数をアップロードする aws_create.sh を作成します。
* アップロードは後ほど行うので、ここでは aws_create.sh の作成のみです。
```bash
nano aws_create.sh
```
```aws_create.sh
awslocal lambda create-function \
  --function-name get_timestamp_lambda_function \
  --timeout 3 \
  --runtime python3.8 \
  --zip-file fileb://package.zip \
  --handle lambda_get_timestamp.lambda_handler \
  --role arn:aws:iam::000000000000:role/lambda-role
awslocal logs create-log-group \
  --log-group-name /aws/lambda/get_timestamp_lambda_function
# destination-arn に書かれている Lambda 関数を呼び出します。
# --filter-pattern は PRO のみの機能のようで、フィルタ条件を書いても無視されてしまいました。
# 今回は /aws/lambda/get_timestamp_lambda_function にイベントが書かれた時点で put_s3_lambda_function が起動します。
awslocal logs put-subscription-filter \
  --log-group-name "/aws/lambda/get_timestamp_lambda_function" \
  --filter-name get_timestamp_lambda_filter \
  --filter-pattern "" \
  --destination-arn arn:aws:lambda:us-east-1:000000000000:function:put_s3_lambda_function
```
## Lambda 関数を起動する aws_invoke.sh を作成します。
```bash
nano aws_invoke.sh
```
```aws_invoke.sh
awslocal lambda invoke --function-name get_timestamp_lambda_function result.log
```
## Python プロジェクトを作成します。（2つ目）
* S3 にファイルを PUT する Lambda 関数を作成します。
* サブスクリプションフィルタで呼び出されます。
```bash
mkdir lambda_put_s3
cd lambda_put_s3
poetry init
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/1267c381-0e8b-efa5-e331-68aabe9a75b1.png)

## Lambda で実行する Python ファイルを作成します。
```bash
mkdir lambda_put_s3
nano lambda_put_s3/lambda_put_s3.py
```
```lambda_put_s3.py
import json
import base64
import gzip
import re
import boto3
from boto3.session import Session

import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)

session = Session(aws_access_key_id='dummy',
    aws_secret_access_key='dummy',
    region_name='ap-northeast-1'
)
s3 = session.resource(service_name='s3', endpoint_url='http://localstack:4566')

def lambda_handler(event, context):
    try:
        # base64 デコード
        decoded_data = base64.b64decode(event["awslogs"]["data"])
        # gzip 解凍
        decompressed_data = gzip.decompress(decoded_data)
        json_data = json.loads(decompressed_data)
        logger.info(json_data)
        # DATETIME: が書かれているログの一部を抜き出します。
        for logEvent in json_data["logEvents"]:
            log = re.search(r"GETDATETIME:.*", logEvent["message"])
            if log != None:
                body = log.group()
                logger.info(body)
                break
        bucket_name = "my-bucket"
        key = "timestamp.txt"
        s3.Bucket(bucket_name).put_object(Key=key, Body=body)
    except Exception as e:
        logger.info(f"Error Occurred: {e}")
    return {
        "statusCode": 200,
        "body": json.dumps({
            "res": "S3TEST",
            "event": event
            }),
    }
```
## poetry でパッケージを追加します。
```bash
# poetry で新しくプロジェクトを作成すると SSL 通信で利用する証明書の再設定が必要なので、プロジェクトを作成したら下記を実行します。(恐らく ZScaler 使用の場合のみ)
poetry source add --default pypiorg https://pypi.org/simple/
poetry source add files https://files.pythonhosted.org/
poetry config certificates.pypiorg.cert /usr/local/share/ca-certificates/zscaler.crt
poetry config certificates.files.cert /usr/local/share/ca-certificates/zscaler.crt
# パッケージを追加します。
poetry add boto3
```
## ビルドを行うbuild.sh を作成・実行します。
* 必要なパッケージと作成した lambda_put_s3.py をzipにまとめます。
```bash
nano build.sh
# 先に入れてあれば不要
sudo apt install zip unzip
sh build.sh
```
```build.sh
poetry export -f requirements.txt --output requirements.txt;
pip install -r requirements.txt --target ./packages;
cd packages/;
zip -r ../package.zip .;
cd ..;
cp lambda_put_s3/lambda_put_s3.py lambda_put_s3.py; zip -g package.zip lambda_put_s3.py; rm -rf lambda_put_s3.py;
```
## Lambda 関数をアップロードする aws_create.sh を作成します。
```bash
nano aws_create.sh
```
```aws_create.sh
awslocal s3 mb s3://my-bucket
awslocal lambda create-function \
  --function-name put_s3_lambda_function \
  --timeout 10 \
  --runtime python3.8 \
  --zip-file fileb://package.zip \
  --handle lambda_put_s3.lambda_handler \
  --role arn:aws:iam::000000000000:role/lambda-role
```

## Lambda 関数の作成と実行と確認
```bash
# 関数のアップロードと起動
cd ~/lambda_put_s3
sh aws_create.sh
cd ~/lambda_get_timestamp
sh aws_create.sh
sh aws_invoke.sh

# 関数の実行完了後
awslocal s3 ls s3://my-bucket
awslocal s3 cp s3://my-bucket/timestamp.txt ./
cat timestamp.txt
```
* get_timestamp_lambda_function を起動した後、put_s3_lambda_function がサブスクリプションフィルタによって起動して S3 にファイルが作成されている事を確認出来ました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/02da014e-c44e-8d33-2784-6396d6c88ce0.png)
---
* 本番環境ではサブスクリプションフィルタ作成時、Lambda 関数にパーミッション追加などの設定が必要らしいので、また試していきたいと思います。