EC2の自動停止スクリプト(Lambda+CloudWatch Events)
うはww また消し忘れて寝てるww 請求書((((;゚Д゚))))
となるのを避けるのを防ぐためのスクリプトのお話。 今回は、下記で実施
- Lambda
- bash
- awscli
- CloudWatch Events
前提
- 開発環境でaws cliが設定済み
- Lambdaで利用するロールは事前に作成済みとする
今回の場合だと、AWSLambdaBasicExecutionRole以外にも、
EC2を操作する権限や、パラメータストアから値を取得する権限が必要
シェルの用意
除外する方法もいろいろ。今回はAWS Systems Manager のパラメータストアを利用
組織とかで使うなら、tagやファイルの読み込みのほうが使いやすそう
#!/bin/bash #起動中インスタンスの一覧を取得 RUNNING_INSTANCE=$(aws ec2 describe-instances | jq -r '[.Reservations[].Instances[] | select(.State.Name == "running") | .InstanceId] | join(" ")') #除外対象とするインスタンスの一覧を取得 EXCLUDE_LIST=$(aws ssm get-parameter --name <設定するパラメータキー名> | jq -r '.Parameter.Value' | sed -e 's/,/ /g' ) #除外対象以外のインスタンスを停止 for e in ${RUNNING_INSTANCE[@]}; do if ! `echo ${EXCLUDE_LIST[@]} | grep -q "${e[@]}"` ; then aws ec2 stop-instances --instance-ids ${e[@]} else : fi done
パラメータストアの設定
aws ssm put-parameter \ --name "パラメータキー名" \ --value "除外対象のインスタンスID_1,除外対象のインスタンスID_2" \ --type StringList
Lamda関数の作成
bashの場合カスタムランタイムで起動する。すぐに終わるかと思ったが、 awsのチュートリアルで進めると、awscliやjqコマンドをたたく際に、そんなコマンドねーぞと怒られるので少し準備が必要。 Bashを動かす環境としてbash-lambda-layerなる便利なものが公開されていたのでこちらをlayerとして使わせていただきます<(_ _)>
アップロードするスクリプトファイルの作成
bash-lambda-layer example-basic.shを参考に用意したシェルを修正し、index.shとして保存
function handler () { set -e #起動中インスタンスの一覧を取得 RUNNING_INSTANCE=$(aws ec2 describe-instances | jq -r '[.Reservations[].Instances[] | select(.State.Name == "running") | .InstanceId] | join(" ")') #除外対象とするインスタンスの一覧を取得 EXCLUDE_LIST=$(aws ssm get-parameter --name exclude.ec2instance | jq -r '.Parameter.Value' | sed -e 's/,/ /g' ) #除外対象以外のインスタンスを停止 for e in ${RUNNING_INSTANCE[@]}; do if ! `echo ${EXCLUDE_LIST[@]} | grep -q "${e[@]}"` ; then aws ec2 stop-instances --instance-ids ${e[@]} else : fi done echo "{\"success\": true}" >&2 }
アップロードのためindex.shをzip化
zip function.zip index.sh
awscliでlambda関数の作成
$ aws lambda create-function \ --function-name terminate-ec2 \ --role <事前準備済みのroleを指定> \ --handler index.handler \ --runtime provided \ --timeout --> defaultの3秒は厳しいので適当な値を設定 \ --memory-size --> memoryもデフォルト128 MBだと結構時間がかかるのでry \ --layers arn:aws:lambda:ap-northeast-1:744348701589:layer:bash:8 \ --zip-file fileb://function.zip
起動確認
$ aws lambda invoke --function-name terminate-ec2 response.txt { "StatusCode": 200, "ExecutedVersion": "$LATEST" } $ cat response.txt {"success": true}
Cloudwatch Eventsの作成
時刻はUTCなので日本時間では+9 (夜中の3時に実行)
$ aws events put-rule --name "Ec2TerminateAtMidnight" --schedule-expression "cron(0 18 * * ? *)" --state ENABLED
CloudWatch Eventsから作成したLamba実行できるための権限付与
$ aws lambda add-permission \ --function-name "terminate-ec2" \ --statement-id "cwevents" \ --action 'lambda:InvokeFunction' \ --principal events.amazonaws.com \ --source-arn arn:aws:events:ap-northeast-1:<accountId>:rule/Ec2TerminateAtMidnight
targetの設定 (eventとlambda関数の紐づけ)
aws events put-targets --rule "Ec2TerminateAtMidnight" --targets Arn=arn:aws:lambda:ap-northeast-1:<accountId>:function:terminate-ec2,Id=1
こんな感じでできれば完成
参考
チュートリアル – カスタムランタイムの公開
gkrizek/bash-lambda-layer
AWS CLIを使ってEC2インスタンスの情報を取得する