Amazon Elasticsearch Service とlambda(Python3) を一瞬で疎通するメモ 

# Amazon Elasticsearch Serviceと Lambda(Python3)を一瞬で疎通するメモ

## 要件

- AWS Lambdaと AWS Elasticsearch Serviceの連携ができること
- ローカルPCで下記が出来ること
 - curlで Elasticsearch Serviceに接続可能
 - kibanaを利用可能
 - python-lambda-localで Lambdaを実行し、Elasticsearch Serviceに接続可能

## Elasticsearch Serviceアクセスポリシー設定

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": "es:*",
      "Resource": "arn:aws:es:ap-northeast-1:[アカウントID]:domain/[ESドメイン]/*",
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": [
            "[アクセス元IPアドレス]"
          ]
        }
      }
    },
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::[アカウントID]:user/[ユーザ]"
      },
      "Action": "es:ESHttpGet",
      "Resource": "arn:aws:es:ap-northeast-1:[アカウントID]:domain/[ESドメイン]/*"
    }
  ]
}
```

- Elasticsearch Serviceのドメインを最小構成で起動しておく
- 下記ポリシーを持った IAMユーザを作成
 - **AWSLambdaFullAccess**
 - **AmazonESFullAccess**

## Elasticsearch Service動作確認

- ローカルPCから curlでの接続

```bash
$ curl -XGET https://xxxx.ap-northeast-1.es.amazonaws.com/_aliases
# {".kibana":{"aliases":{}}}
```
- kibanaはブラウザでいい感じに動くことを確認する

## Lambda実装サンプル

※botocoreを使用したサンプルは、EC2バージョンアップなどにより動かなくなる（なった）ため修正

```python
pip3 install requests -t./
```

requestパッケージを AmazonLinuxの EC2でダウンロード

```bash
pip3 install requests-aws4auth -t./
# 依存関係で python-request 他もダウンロードされる
```
※requests_aws4auth, requests, bin, certifi, chardet, idna, requests, urllib3 を scpなどで手元に持ってくるか、任意の S3バケットにアップロードする

```python3
import boto3
import json
import requests
from requests_aws4auth import AWS4Auth

def handler(event, context):
    url = "https://****-****.ap-northeast-1.es.amazonaws.com/_search"
    credentials = boto3.Session().get_credentials()
    awsauth = AWS4Auth(credentials.access_key, credentials.secret_key, 'ap-northeast-1', 'es', session_token=credentials.token)

    query = {"size": 1}
    response = requests.get(url, auth=awsauth, headers={"Content-Type": "application/json"}, data=json.dumps(query))

    return json.loads(response.text)
```
※初回は「アクション->関数のエクスポート->デプロイパッケージのダウンロード」で lambda_function.pyファイルをダウンロードし、先程ダウンロードした requestsパッケージにコピーしてzipファイルに圧縮、「コード エントリ タイプ->.zipファイルをアップロード」で展開すると楽


- 下記ポリシーを持った IAMロールを作成
 - **AmazonESFullAccess**
- Lambda環境変数に設定
 - **ES_ENDPOINT_URL**
 - **AWS_ACCESS_KEY_ID**
 - **AWS_SECRET_ACCESS_KEY**
 - **AWS_SESSION_TOKEN**
- Python以外だと署名バージョン4 の実装が若干面倒かも

## Lambda動作確認

- AWSデベロッパーコンソールに Lambdaソースを貼り付けてテスト

```json
{
  ".kibana": {
    "aliases": {}
  }
}
```
- ローカルPCから python-lambda-localで実行

```bash
$ export ES_ENDPOINT_URL=https://xxxx.ap-northeast-1.es.amazonaws.com
$ echo "{}" > event.json
$ python-lambda-local -f handler index.py event.json
# [root - INFO - 2017-11-xx xx:xx:xx,xxx] Event: {}
# [root - INFO - 2017-11-xx xx:xx:xx,xxx] START RequestId: xxxx
# [botocore.vendored.requests.packages.urllib3.connectionpool - INFO - 2017-11-xx xx:xx:xx,xxx] Starting new HTTPS connection (1): xxxx.ap-northeast-1.es.amazonaws.com
# [root - INFO - 2017-11-xx xx:xx:xx,xxx] END RequestId: xxxx
# [root - INFO - 2017-11-xx xx:xx:xx,xxx] RESULT:
# {'.kibana': {'aliases': {}}}
# [root - INFO - 2017-11-xx xx:xx:xx,xxx] REPORT RequestId: xxxx	Duration: 383.92 ms
```
- ここまでやったら Lambda関数をエクスポートして 以後は CloudFormationでデプロイするとあとあとよい
- ElasticSearchは忘れず止めておく😇

## 参考リンク

- [チュートリアル: Amazon Elasticsearch Service での検索アプリケーションの作成](https://docs.aws.amazon.com/ja_jp/elasticsearch-service/latest/developerguide/search-example.html#search-example-lambda)
- [kibanaに合ったAWS Elasticsearch Serviceのアクセスポリシーで迷います](https://qiita.com/bboobbaa/items/8ab5937e7ed5a2230a9d)
- [AWS Lambda Pythonをローカル環境で実行](https://dev.classmethod.jp/cloud/aws/invoke-aws-lambda-python-locally/)