AWS Lambdaのトリガーについて

## Lambdaについて

AWS Lambda（以下Lambda）はサーバレスコンピューティングサービスです。 オペレーティングシステム（以下OS）などのインフラストラクチャの管理が不要で、利用者はプログラムコードを準備し、Lambdaにアップロードするだけで実行できます。

## トリガーとは

トリガーはLambdaを実行するきっかけです、もちろん手動で実行できますが自動で実行も出来ます、それは色々の方法あります。

![Screen Shot 2022-06-16 at 12.26.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878226/9fac04c4-5e90-90b1-d1cd-5dfd0b8826aa.png)

これは普通の作成したlambdaです、画面で左側は「トリガーを追加」オプションがあります、それをクリックすると色々選べます。選べるオプションの中にちょっと説明します。

## s3

S3にファイルが置かれた時に自動でLambdaを実行できます。

設定画面はこちら：

![Screen Shot 2022-06-16 at 12.29.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878226/e65f6731-a8fe-6a16-da84-f3325abd6187.png)

まずはどこのバケットのトリガーを設定します、アカウントの全体アクションは出来ませんので各バケットに設定が必要です。

次はどこのアクションで動かす、S3はファイルストーレージのサービスなので何が出来ますということファイルが作成された時にトリガーが出来ますし、ファイルのコピー、ファイルのストレージクラス変更もトリガーが出来ますなどなど：

![Screen Shot 2022-06-16 at 12.31.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878226/9488487b-892a-7b22-9177-265b02b75864.png)

後はキーで設定できます、例えばフォルダーを指定して、ファイル名が「test-」から始まるファイルを対象できます。それでも不満しましたらサフィックスも付けれます！ファイルの「.jpg」しか欲しく場合なら「.jpg」を使うもできます。

例えば：「export」のバケットに「my_files/export-*.csv」から始まるファイルが作成された時にutf8に変更して、別のアカウントのs3に送りたいです！と言う設定はこちら。

![Screen Shot 2022-06-16 at 12.51.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878226/da2b104a-2133-8fd8-df4c-877fcd519199.png)


設定終わったらlambdaのコードで確認する。

S3のイベントはこんな感じです、普通のjsonです。


```json
{
  "Records": [
    {
      "eventVersion": "2.0",
      "eventSource": "aws:s3",
      "awsRegion": "us-east-1",
      "eventTime": "1970-01-01T00:00:00.000Z",
      "eventName": "ObjectCreated:Put",
      "userIdentity": {
        "principalId": "EXAMPLE"
      },
      "requestParameters": {
        "sourceIPAddress": "127.0.0.1"
      },
      "responseElements": {
        "x-amz-request-id": "EXAMPLE123456789",
        "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH"
      },
      "s3": {
        "s3SchemaVersion": "1.0",
        "configurationId": "testConfigRule",
        "bucket": {
          "name": "example-bucket",
          "ownerIdentity": {
            "principalId": "EXAMPLE"
          },
          "arn": "arn:aws:s3:::example-bucket"
        },
        "object": {
          "key": "test%2Fkey",
          "size": 1024,
          "eTag": "0123456789abcdef0123456789abcdef",
          "sequencer": "0A1B2C3D4E5F678901"
        }
      }
    }
  ]
}
```

jsonの形でファイルのバケットとパスが分かります。それで分かりやすいpythonコードにすると：

```python
import json
import boto3
import os

def lambda_handler(event, context):
    for record in event['Records']:
        bucket = record['s3']['bucket']['name']
        key = record['s3']['object']['key']
        s3 = boto3.resource('s3', aws_access_key_id=os.environ['ACCESS_KEY'], aws_secret_access_key=os.environ['SECRET_KEY'])
        obj = s3.Object(bucket, key)
        corpus = obj.get()['Body'].read().decode('utf-8')
        client = boto3.client('s3', aws_access_key_id=os.environ['ACCESS_KEY_PRIVATE_USE'], aws_secret_access_key=os.environ['SECRET_PRIVATE_USE'])
        client.put_object(Body=corpus, Bucket="private-csv", Key=key, ContentEncoding="utf-8")

    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }
```

先に`event['Records']`の中では腹痛のイベントがある可能性がありますのでループします、オブジェクトごとにゲットして、中身をutf-8してと別のアカウントに送ります。

os.environはlambdaの設定で登録したものです！

![Screen Shot 2022-06-16 at 13.00.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878226/449a3881-7352-80e3-4c4a-7b1e15855d75.png)

この設定でこのコードなら指定したファイルがS3のバケットに送られた瞬間にLambdaが実行して、そのファイルをutf-8変更して、別のs3アカウントに送る出来ます。

## Cloudwatch

Cloudwatchは基本ログ管理とメトリクス管理を使いますが面白い使い方もあります、CloudwatchのイベントからLambdaを実行できます！

イベントってなに？スケジュールが出来るトリガーです！定期実行、特別の時間とかは設定できます。例えば毎時間のサーバーレス実行でサーバーをチェックしたいとか、特別のアクションをしたい場合は出来ます。

![Screen Shot 2022-06-16 at 13.11.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878226/2145c00f-7161-789e-c17d-75ab3b18a927.png)

cronの設定して、次の１０回の実行確認が出来ますので書き間違いを確認しやすいです。ターゲットをlambdaにすると設定するjsonsテキストとして入れて、そのままにlambdaが受け取れます！

lambdaのコードはこちらです：

```python

def lambda_handler(event, context):
    log_path = event['log']
    print(log_path)
    s3 = boto3.resource('s3', aws_access_key_id=os.environ['ACCESS_KEY'], aws_secret_access_key=os.environ['SECRET_KEY'])
    obj = s3.Object(BUCKET, os_name+"/logs.txt")
    log_check(obj)

def log_check(obj):
    ##log check function
```

## SQS

SQSはキューのサービスです！それで色々ジョブとかを貯めて、ゆっくり実行してます。

例えば、エラーが発して、それをSQSのキューに入れて、キューに入ったらlambdaが実行されますのでlambdaからslackとかに送信したい・・！という事ができます。

これはSQSのトリガーイベントです：

```json
{
  "Records": [
    {
      "messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78",
      "receiptHandle": "MessageReceiptHandle",
      "body": "Hello from SQS!",
      "attributes": {
        "ApproximateReceiveCount": "1",
        "SentTimestamp": "1523232000000",
        "SenderId": "123456789012",
        "ApproximateFirstReceiveTimestamp": "1523232000001"
      },
      "messageAttributes": {},
      "md5OfBody": "{{{md5_of_body}}}",
      "eventSource": "aws:sqs",
      "eventSourceARN": "arn:aws:sqs:us-east-1:123456789012:MyQueue",
      "awsRegion": "us-east-1"
    }
  ]
}
```

そのイベントに対してのlambdaのコードはこちら：

```python
import urllib3
import json
http = urllib3.PoolManager()


def lambda_handler(event, context):
    for record in event['Records']:
        url = "https://hooks.slack.com/services/XXXX/XXXX/XXXX"
        msg = {
            "channel": "#general",
            "username": "",
            "text": "Hello From Lambda",
            "icon_emoji": ""
        }

        encoded_msg = json.dumps(msg).encode('utf-8')
        resp = http.request('POST', url, body=encoded_msg)
        print({
            "message": "Hello From Lambda", 
            "status_code": resp.status, 
            "response": resp.data
        })
```


## 他のlambda

もちろん、他のLambdaから呼べます、「なんでこれを使うの？」と言われたら色んな場合があります！

AWSのVPC内とVPC外は全然違います、同じlambdaをデータベースのデータを見たいと外部のサービスを使うは出来ません、それだと1 lambdaがVPC内でデータベース結果とって、VPC外のLambdaに送るが出来ます、設定しやすいし、セキュリティーとして安全です。

他の場合も、Lambdaの実行時間が決まってます、15分以上は出来ません、1ギガ以上のメモリが出来ませんので短いジョブに向いてます。多いユーザーリストに一件ずつを同じプロセスするより、同じプロセスが全部asyncに別のlambdaに送る方が効率良い、lambdaの同時実行がすごく良いのでそっちの方が安全です。

```python
import json
import boto3

def lambda_handler(event, context):
    log_path = event['logs']
    print(log_path)
    s3 = boto3.resource('s3', aws_access_key_id=os.environ['ACCESS_KEY'], aws_secret_access_key=os.environ['SECRET_KEY'])
    obj = s3.Object(BUCKET, log_path+'users.txt')
    corpus = obj.get()['Body'].read().decode('utf-8')
    content = json.loads(corpus)
    client = boto3.client('lambda', region_name='ap-northeast-1', aws_access_key_id=os.environ['ACCESS_KEY'], aws_secret_access_key=os.environ['SECRET_KEY'])
    for user in content:
        data = {"user": user, "log_path": log_path}
        client.invoke(
        FunctionName='arn:aws:lambda:ap-northeast-1:600552000000:function:check_users',
        InvocationType='Event',
        Payload=json.dumps(data)
        )
```

InvocationTypeは一番面白いオプションです、asyncか返事待ちを選べます：

- `RequestResponse` (デフォルト) - 関数を同期的に呼び出します。関数がレスポンスを返すかタイムアウトするまで、接続を開いたままにします。API レスポンスには、関数レスポンスと追加データが含まれます。

- `Event` - 関数を非同期的に呼び出します。複数回失敗したイベントを、関数のデッドレターキューに送信します (設定されている場合)。API レスポンスには、ステータスコードのみが含まれます。

- `DryRun` - パラメータ値を検証し、ユーザーまたはロールが、関数を呼び出すアクセス許可を持っていることを確認します。


Payloadは入力として Lambda 関数に提供される JSON。




以上。