AWS Lambda(Python3 boto3)をローカル環境で構築してからアップしよう

#はじめに
AWS Lambda(Python3)を構築する際、直接Lambda関数の画面で作業してもいいのですが、デバッグしづらいし、エディタは使いづらい。
その為、ローカル環境で一度作成してから、AWS Lambdaへアップする方法を記載します。

→2017/12/02追記
AWSアップデートされCould9なんてものが追加されたし、AWS Lambdaエディタ機能も追加されたので、今後はブラウザ上だけで充分かもね(´･ω･｀)

***
##環境
各インストール方法はぐぐってね。
OSはWindows10 Proにて確認。

+ Python3(https://www.python.org/downloads/)
+ AWS CLI(https://aws.amazon.com/jp/cli/)
+ Boto3(https://aws.amazon.com/jp/sdk-for-python/)
+ ATOMエディタ（https://atom.io/）

事前にAWS AMIアカウントから「アクセスキー」、「シークレットアクセスキー」を発行後
コマンドプロンプト「aws configure」で登録してね。

```
$ aws configure
aws_access_key_id = アクセスキー
aws_secret_access_key = シークレットアクセスキー
region = ap-northeast-1
output = json
```

###ATOMアドオン 
+ atom-beautify スクリプト整形用※
+ atom-runner　Python実行用★必須
+ autocomplete-python　Python補完用※
+ file-icons　アイコンが変わるよ
+ highlight-selected　選択文字列ハイライト表示※
+ japanese-menu　ATOM日本語化※
+ minimap 　スクロールバー※
+ python-autopep8　Python補完用
+ show-ideographic-space　全角スペース表示用※
+ symbols-tree-view　関数、変数一覧表示
+ web-search　ATOMからWeb検索用

atom-runnerはATOM上でPythonを実行するのに必須。※印はお勧めアドオンです。
***

##動作確認（Python3コード）
以下コードをコピーし、適当なファイル名+拡張子(.py)で作成後、「ALT+R」でPythonを実行。
稼働中のEC2インスタンスIDとTag名（サーバー名）を取得します。

```py3
import boto3
import json

ec2c = boto3.client('ec2')

##############################################
#起動しているインスタンスID取得
##############################################
def func_GetInstanceId():

    instanceid_List = []
    retec2c = ec2c.describe_instances(
        Filters=[
            {'Name':'instance-state-name','Values': ['running']}
        ]
    )
    for reservation in retec2c['Reservations']:
      for instanceid in reservation['Instances']:
        instanceid_List.append(instanceid['InstanceId'])

    return instanceid_List
##############################################
#インスタンスIDからタグ名取得
#引数：インスタンスID(i-1234567890ABCDEFG) 
#戻り値：タグ名　Win2016_SV、未設定の場合は「サーバー名未設定」
##############################################
def func_RET_ServerTagName(SET_InstanceId):

    TagName = ec2c.describe_tags(
        Filters=[
        {'Name': 'resource-id', 'Values': [SET_InstanceId]}
        ,{'Name': 'tag-key', 'Values': ['Name']}
        ])
    try:
        RET_ServerTagName=TagName['Tags'][0]['Value']
    except Exception as e:
        RET_ServerTagName="サーバー名未設定"
    return RET_ServerTagName

################################
# MAIN
################################
#def lambda_handler(event, context):
if __name__ == "__main__":

    #起動中のEC2インスタンスIDを取得
    insid_list=func_GetInstanceId()

    for insid in insid_list:
        #インスタンスIDからTag名（サーバー名）を取得    
        ins_name=func_RET_ServerTagName(insid)
        print(ins_name + "(" + insid + ")")
```
##出力例 
以下インスタンスIDは適当に書き換えています

```
Win2016SV1(i-123456789ABCDEFG)
サーバー名未設定(i-123456789)
Windows2012SV2(i-0123456789abcd)
```
##AWS Lambdaにアップする際に修正する箇所

```py3
if __name__ == "__main__":
```
をコメントアウトし

```py3
def lambda_handler(event, context):
```
を有効にしましょう。

```py3
#def lambda_handler(event, context):
if __name__ == "__main__":
```
↓

```py3
def lambda_handler(event, context):
#if __name__ == "__main__":
```

あとはAWS Lambdaにコピペして動作を確認。

#時刻取得その1
時刻を取得する際、ローカル環境ではOSの時刻を取得する為、JSTで取得できますが
Lambda上はGMTで取得する為、+9時間する必要がありますよ。

```py3
import datetime

#Lambda上で実行する場合
#def lambda_handler(event, context):

#ローカルで実行する場合
if __name__ == "__main__":

    #ローカルで実行する場合
    d = datetime.datetime.now()

    #Lambda上で実行する場合
    #d = datetime.datetime.now() + datetime.timedelta(hours=9)

    print("d時間：" + str(d))
```

#時刻取得その2
もしくは環境変数キー名「TZ」値「Asia/Tokyo」を追加すればJSTで取得する為、上記の様に+9時間しなくても大丈夫。

##グローバル変数で時刻を取得すると・・・だれかが死ぬ
日付時刻を取得する際、関数内で取得せず、グローバル変数として取得した場合、
AWS Lambda上2回目の動作以降に同じ時間を取得するはめになりますので注意。

※障害例
時刻取得処理をトリガーにて9:00開始、10分毎に実行した際、
9:00に実行した場合は、初回に実行する為「9:00」と正確に取得しますが、次回9:10に実行した際にも「9:00」と取得される。9:20以降もずーっと「9:00」で取得されてしまう。

またこれがLambdaテスト実行時には再利用されないため障害は発生せず、トリガーで定期的に実行すると発生するやっかいなヤツ。

・2回目以降のLambdaを再利用している為発生する模様。
http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-introduction.html

#時刻取得（正）

```py3
import datetime

def lambda_handler(event, context):
    d = datetime.datetime.now() + datetime.timedelta(hours=9)　#　◎関数内の為毎回取得します。
    print("d時間：" + str(d))
```
#時刻取得（誤）

```py3
import datetime

d = datetime.datetime.now() + datetime.timedelta(hours=9)　#　×グローバル変数で取得しちゃダメ

def lambda_handler(event, context):
    print("d時間：" + str(d))
```
以上