LambdaでAWSの料金を毎日Slackに通知する(Python3)

はじめに

個人アカウントは基本的に無料枠で運用しているので、少しでも請求がある場合はいち早く気づきたいです。
先日、とあるハンズオンイベントで使ったリソースを消し忘れて、最終的に$30ぐらい請求が来てしまいました。。。

CloudWatchで請求アラートは設定していますが、閾値超えが想定の場合、当然見逃すことになり、最終的な請求額に驚くハメになります。

これを防ぐためにLambdaで毎日SlackにAWS料金を通知することにします。

先日LambdaがPython3に対応したので、せっかくだし勉強がてらPython3で実装したい。
ネット上にはNode.jsでの実装例が多いようで、今回はこちらを参考にPython3で実装してみます。

必要なもの

  • Slack

    • incoming-webhooks URL

    • 適当なchannel
  • lambda-uploader
    • requestsモジュールをLambda上でimportするために利用

      • カレントディレクトリにモジュールをインストールして、モジュールごとZipに固めてアップロードでもいけるはずですが、私の環境だとうまくいかなかったので
  • aws cli
    • lambda-uploaderで必要
  • AWS
    • Lambda関数用IAM Role

      • CloudWatchReadOnlyAccessポリシーをアタッチ

事前準備

lambda-uploaderをインストール

$ pip install lambda-uploader 

こちらを参考にさせていただきました。

aws cliをインストール

$ pip install awscli

credential、リージョン設定

$ aws configure

確認
$ aws configure list

コード

ディレクトリ構成

ディレクトリ名は任意です。関数名とは無関係です。

ディレクトリ構成
awscost_to_slack/
|--lambda_function.py
|--requirements.txt
|--lambda.json

lambda_function.py

超過金額に応じて色をつけるようにしています。
\$0.0なら緑、超過したら黄色、\$10超えで赤になります。

lambda_function.py
#!/usr/bin/env python
# encoding: utf-8

import json
import datetime
import requests
import boto3
import os
import logging

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

# Slack の設定
SLACK_POST_URL = os.environ['slackPostURL']
SLACK_CHANNEL = os.environ['slackChannel']

response = boto3.client('cloudwatch', region_name='us-east-1')

get_metric_statistics = response.get_metric_statistics(
    Namespace='AWS/Billing',
    MetricName='EstimatedCharges',
    Dimensions=[
        {
            'Name': 'Currency',
            'Value': 'USD'
        }
    ],
    StartTime=datetime.datetime.today() - datetime.timedelta(days=1),
    EndTime=datetime.datetime.today(),
    Period=86400,
    Statistics=['Maximum'])

cost = get_metric_statistics['Datapoints'][0]['Maximum']
date = get_metric_statistics['Datapoints'][0]['Timestamp'].strftime('%Y年%m月%d日')

def build_message(cost):
    if float(cost) >= 10.0:
        color = "#ff0000" #red
    elif float(cost) > 0.0:
        color = "warning" #yellow
    else:
        color = "good"    #green

    text = "%sまでのAWSの料金は、$%sです。" % (date, cost)

    atachements = {"text":text,"color":color}
    return atachements

def lambda_handler(event, context):
    content = build_message(cost)

    # SlackにPOSTする内容をセット
    slack_message = {
        'channel': SLACK_CHANNEL,
        "attachments": [content],
    }

    # SlackにPOST
    try:
        req = requests.post(SLACK_POST_URL, data=json.dumps(slack_message))
        logger.info("Message posted to %s", slack_message['channel'])
    except requests.exceptions.RequestException as e:
        logger.error("Request failed: %s", e)

requirements.txt

pip installしたいモジュール名を書きます。

requirements.txt
requests

lambda.json

Lambda関数名、IAM RoleのARNなどは環境に合わせてください。
スクリプト本体のファイル名とハンドラの前半を一致させないと動きません。地味にハマるので注意!

lambda.json
{
  "name": "LAMBDA_FUNCTION_NAME",
  "description": "DESCRIPTION",
  "region": "ap-northeast-1",
  "handler": "lambda_function.lambda_handler",
  "role": "arn:aws:iam::XXXXXXX:role/ROLE_NAME_FOR_LUMBDA",
  "timeout": 300,
  "memory": 128
}

デプロイ

上記ファイルを配置したディレクトリに移動して、lambda-uploaderを実行します。

$ cd awscost_to_slack
$ lambda-uploader
λ Building Package
λ Uploading Package
λ Fin

Lambda環境変数設定

今回のLambda関数では、通知先SlackチャネルとWebhooks URLを環境変数で渡すようにしたので、設定します。

スクリーンショット 2017-06-23 14.51.30.png

lambda-uploaderのlambda.jsonに書けそうなのですが、書式が分からず、今回はマネコンで設定しました。
lambda-uploaderでLambda関数を更新すると消えてしまうので注意。

Lambda定期実行設定

CloudWatchのスケジュールイベントを定義して、lambda関数をターゲットに指定します。
時刻はUTCなので注意しましょう。
毎日UTC0:00に実行されるよう設定しました。

スクリーンショット 2017-06-23 15.01.27.png

実行イメージ

スクリーンショット 2017-06-23 14.15.54.png
こんな感じで毎朝9:00に通知がきます。
今日も無料!

まとめ

Lambdaはほぼ無料でプログラムが動かせるので楽しいですね!
Python初心者なのでコードが見苦しい点はご容赦ください。

続きを読む

IAMでクロスアカウントスイッチロール設定メモ

軽くテストしたので個人的な備忘録です。

★クロスアカウントスイッチロールすると嬉しいこと

複数のAWSアカウント間を認証画面介さず行き来できる
個人用IAMアカウント作るのは一か所でよくアカウント毎でなくなる
SDKつかってるような一部のツールではスイッチできないものもあるがawscliくらいならスイッチロールでいける
スイッチ先で権限を限定してスイッチ元でアカウントの増減を制御できるので
別会社間のメンバーの増減のアカウント管理のやり取りが生じなくてたぶんべんり

アカウント番号はサポート画面の右上に出てる。

★参考

Swith Roleで複数のAWSアカウント間を切替える – Qiita
超簡単!今すぐ使える「クロスアカウントアクセス」 | Developers.IO
AWS Black Belt Techシリーズ AWS IAM
【小ネタ】複数のSwitch Roleでのクロスアカウントアクセスをブラウザのブックマークで管理する | Developers.IO

一番したのやつ履歴が5こくらいまでできえることに憤慨している人は幸せになれそう。

★実際のクロスアカウントスイッチロール実装手順の簡易なメモ

0.テストするアカウントを2つようい

アカウント1
※スイッチ元
Account Number 1234zzzzzzzz

アカウント2
※スイッチ先
アカウント番号 5678xxxxxxxx

1.スイッチ先でロールを作成する

※お客様先にスイッチする場合お客様作業

IAMサービスを選択してロールを作る

新しいロールの作成
 >ロールの選択(クロスアカウントアクセスのロールで外部IDの使用を許可しないほうを選択)
  (※外部ID許可とはldapやadなどのIAMクレデンシャルでないID連携を許可するものと思われ)
  >このアカウントにアクセスできる IAM ユーザーの AWS アカウントの ID を入力(スイッチ元のIDを入力)
   (MFAが必要にチェックはデバイスやアプリの用意が可能な場合に入れる)
   >ポリシーのアタッチ(既存から選ぶのでカスタムにしたいならあらかじめ調べておく)
    (とりあえず試験用なので適当な権限にする(arn:aws:iam::aws:policy/AdministratorAccess ))
    >ロール名を入力:mygroup-admin

2.スイッチ元でロールを設定する

とりあえずユーザとグループを作る

グループ:mygroup
ユーザ:とりあえず二人くらいを作成

グループのインラインポリシーを作成しスイッチ先のアカウントとロールを設定する
ポリシー名:switch-to-otheraccount-name

{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Action": "sts:AssumeRole",
    "Resource": "arn:aws:iam::5678xxxxxxxx:role/mygroup-admin"
  }
}

3.スイッチ元で自分のユーザで入りなおしてから右上からスイッチロールを選択してスイッチする

アカウント:5678xxxxxxxx
ロール:mygroup-admin

お客様先にスイッチする場合、
・スイッチ元のアカウントIDをお伝えする
・作成したロール名とわたる先のアカウント名とアカウントIDを聞いて設定
・スイッチロールしてみる
・スイッチ履歴は5個くらいしか残らないので便利なリンクを作っておく

ということになります。

★証跡を追えるようにするためにスイッチ元でCloudTrailの設定

見た感じすでに設定済みな模様でござったのでリンク先をどうぞ。S3もみたところ数年前からログがあった。
Amazon Web Services ブログ: 【AWS発表】 AWS CloudTrail – AWS APIコールの記録を保存
AWSの操作履歴を記録するCloudTrailを試してみた « サーバーワークス エンジニアブログ

★アカウントのエイリアスの設定

あんまり関係ないがIDだと視認性が微妙なので設定したほうがよさそう(なくてもいい)

AWS アカウント ID とその別名 – AWS Identity and Access Management

変えたたらサブドメインがアカウント番号からエイリアス名になる(アカウント番号でもアクセスできるまま)
https://my-alias.signin.aws.amazon.com/console

★アカウント設定(パスワードポリシー)

ISMS的なアレ(または顧客要望)にのっとって適宜。
Account settingsから実施。

Minimum password length: x(x文字を要する)
Require at least one non-alphanumeric character(記号を要する)
Allow users to change their own password (自分で更新する)
Enable password expiration
Password expiration period (in days): xx(xx(日)で期限がきれる)

★ルートアカウントでアクセスしない

スイッチロールの設定時にrootアカウントにアクセスできるように設定しなければ
メニューにスイッチロールでないので物理的にルートアカウントにアクセスは不可能。
クラスメソッドのリンクが詳しい(rootでも設定するといけるけどやらないほうがいい))
単に運用上パスワード変えて限定共有する、クレデンシャル無効化する、MFAデバイス用意等。
あとCloudTrail的に個人IAMで操作したほうが証跡が追いやすい。

★クレデンシャルの書き方

たぶん以下のようになる。

[account2]
role_arn = arn:aws:iam::5678xxxxxxxx:role/mygroup-admin
source_profile = account1
region=us-xxxx-x

★スイッチロールのポリシーアタッチされてるグループにいるユーザをcliでだす

$ aws iam get-group --group-name mygroup --profile account1|jq -c -r '.Users[].UserName'

以上

続きを読む

AmazonECRとEC2を使って手元でビルドしたDockerイメージをAWS上でサクッと動かす

ECR(EC2 Container Registry)に登録したDockerイメージをEC2上でコンテナとして起動するまでの一通りの流れを書いてみた
ECSも一通り検証終わっていて、サービスではそちらを使う予定だが、基礎を振り返るという意味でのまとめ。

Docker

ここ一ヶ月ひたすらdockerを触っているが、やはり手元の開発環境で動いたものが、別の環境でそのまま動くというのは他にないメリット。
これまでだと、開発環境でOK→STでまた一から作る→本番でも同じくみたいなことしてたけど、ホスト側にDockerエンジン入れるだけで、実際のプロセスは開発環境のものをそのまま移植出来るというところはかなり熱い。といった印象。

やること

  • ECRを使う準備
  • ECRへのDockerイメージの登録
  • EC2作成
  • EC2上にECRからpullしたDockerコンテナを立てる

ECRとは

正式名称 EC2 Container Registry
Amazonが提供するフルマネージドのDockerコンテナレジストリ。
Dockerイメージを管理して、ECS(EC2 Container Service)やEB(Elastic Beanstalk)に簡単にデプロイすることが出来るソリューション

ECR使うと何がうれしい

  • EC2インスタンスにIAMroleを付与するだけ、EC2側で面倒な認証をせずにdockerイメージを使える
  • S3がバックエンドなので、可用性高い
  • 自動的に暗号化されたり、https通信されるのでセキュリティも安心

ECRを使う準備(ローカルマシンで実施)

AWS Command Line Interface のインストール を参考にAWS CLIを手元のマシンにインストールしておく。

基本的には、AWS CLIで操作する。

1.リポジトリの作成&確認

$aws ecr create-repository --repository-name tst-shnagai
{
    "repository": {
        "registryId": "xxxxx",
        "repositoryName": "tst-shnagai",
        "repositoryArn": "arn:aws:ecr:ap-northeast-1:xxxxx:repository/tst-shnagai",
        "createdAt": 1496229520.0,
        "repositoryUri": "xxxx.dkr.ecr.ap-northeast-1.amazonaws.com/tst-shnagai"
    }
}

リポジトリは、GUIから見ると、ECSサービスの中の[リポジトリ]に出来る

Amazon_EC2_Container_Service.png

2.ecrにログイン

セッションは12時間なので、感覚的に翌日には切れてる感じ。

## これで一発

$ $(aws ecr get-login --region ap-northeast-1)
Flag --email has been deprecated, will be removed in 17.06.
Login Succeeded

## $()の式展開を使わない場合

$ aws ecr get-login --region ap-northeast-1
docker login -u AWS -p eyJwYXlsb2FkIjoicURLTkxCTFhobUJuSTRxSDRNSUFBOEprc0txSnVuTVgrdzRzNkl4NU5rRDUxM0N...
### 標準出力の結果を貼り付けてログイン
$ docker login -u AWS -p eyJwYXlsb2FkIjoicURLTkxCTFhobUJuSTRxSDRNSUFBOEprc0txSnVuTVgrdzRzNkl4NU5rRDUxM0N...
Login Succeeded

ECRへのDockerイメージの登録(ローカルマシンで実施)

手元にある何かしらのDockerイメージをECRにpushする手順
手元で、Dockerイメージに対して、ECR用のタグづけを行ってから、ECRにpushする

1.docker tagコマンドでタグづけをする

今回は例として元々手元にある[apache_td]というdockerイメージに対して、ECRのルールに沿った名前でタグ付け(aliasつけるようなもの)する

## 元々のイメージ
$ docker image list |grep apache_td
apache_td                                                         latest              2c42dd3f5e5c        13 days ago         1.4GB

## タグ付けを実施
$ docker tag apache_td:latest  xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/tst-shnagai:latest

## imageIDは変わらないので、下記のような検索するとapache_tdがECRに対応したイメージとしてタグ付けされたことがわかる
$ docker image list |grep 2c42dd
xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/tst-shnagai     latest              2c42dd3f5e5c        13 days ago         1.4GB
apache_td                                                         latest              2c42dd3f5e5c        13 days ago         1.4GB

2. 1でタグづけしたDockerImageをECRにpushする

$ docker push xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/tst-shnagai:latest
The push refers to a repository [xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/tst-shnagai]
47d1cbb6b480: Layer already exists
...
latest: digest: sha256:14b7a5d491fa694c00f026bbc6c6cd09e0ddc63d0e586569a0de42a8ce7ec5d1 size: 2411

GUIで、タグ名とプッシュされた日時を確認して無事イメージがアップされていることを確認する

Amazon_EC2_Container_Service.png

ここまでで、ECRへのDockerイメージの登録は完了!!

EC2インスタンスの作成

1.通常通りEC2インスタンスを作成する(OSはデフォルトでawscliが入っているamazon linuxだと楽)

ポイントは、IAMRoleに[AmazonEC2ContainerRegistryReadOnly]ポリシを付与しておくことのみ

IAM_Management_Console.png

2. dockerのインストール

AWSの公式ドキュメントに沿ってやるだけなので、コマンドだけ羅列
Docker のインストール

ec2-userでdockerコマンドがsudoなしでうてるとこまでやっておく。

$ sudo yum update -y
$ sudo yum install -y docker
$ sudo service docker start
### ec2-userでsudoなしでdockerコマンドを打てるようにするため
$ sudo usermod -a -G docker ec2-user
###再ログイン
$ docker info
Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
Images: 0
Server Version: 17.03.1-ce

EC2上にECRからpullしたDockerコンテナを立てる(EC2上で実施)

1. ECRへのログイン

IAMRoleがついていない場合は、ログインで弾かれる

$ $(aws ecr get-login --region ap-northeast-1)
Login Succeeded

2. ECRからDockerイメージをpullする

## ECRにアップロードしたイメージをpull
$ docker pull xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/tst-shnagai:latest
latest: Pulling from tst-shnagai
996fe98f55d8: Pull complete
...
e6b377ddca6e: Pull complete
Digest: sha256:14b7a5d491fa694c00f026bbc6c6cd09e0ddc63d0e586569a0de42a8ce7ec5d1
Status: Downloaded newer image for xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/tst-shnagai:latest

## 手元のイメージとして登録されたことを確認
$ docker image ls
REPOSITORY                                                      TAG                 IMAGE ID            CREATED             SIZE
xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/tst-shnagai   latest              2c42dd3f5e5c        13 days ago         1.4 GB

3. dockerコンテナを起動する

pullしてきたイメージからコンテナを起動する

## ホストの8080ポートにマッピングするtestという名前のコンテナを起動する
$ docker run -d --name test -p 8080:80 xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/tst-shnagai:latest
dbbb74b6ebe95666d356250de8310c19403078f53e020069e9a6d10e479b2873

## -lオプションで最後に起動したコンテナを表示
$ docker ps -l
CONTAINER ID        IMAGE                                                                  COMMAND                  CREATED             STATUS              PORTS                  NAMES
dbbb74b6ebe9        xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/tst-shnagai:latest   "/bin/sh -c '/bin/..."   4 seconds ago       Up 4 seconds        0.0.0.0:8080->80/tcp   test

## 動作確認として、ホストの8080に対してcurlでリクエストしてみる
$ curl localhost:8080
version 1.2

まとめ

オーソドックスな、AWSでECRを使ってdockerコンテナを起動する一通りの流れをやってみた。dockerを手元で触ってる人だったら、特に躓くことなくやれる内容だと思う。
ECSは、基本オペレーション(この投稿でいうEC2以降の話)を抽象化して、クラスタというEC2集合体の上で、ELB,AutoScaling等を付加して使えるサービスなので、ココら辺をちゃんと理解してやるとやらないでは進みがだいぶ違うという印象を受ける。
裏で何が行われてるのかなという道理を理解することは大事。

続きを読む

[JAWS-UG CLI] CloudFormation:#6 スタックの削除

.. 後始末. CloudFormation スタックの削除

この手順の目的

CloudFormationスタック”BasicWebServerInVPC”を削除します。

作業対象

  • CloudFormationサービス

標準時間

8分

前提条件

作業権限

CloudFormationへの権限 ]-

CloudFormationに対してフル権限があること。

作業環境

AWS CLIのバージョン ]-

以下のバージョンで動作確認済

  • AWS CLI 1.11.14
コマンド
aws --version

結果(例):

  aws-cli/1.11.97 Python/2.7.12 Linux/4.4.11-23.53.amzn1.x86_64 botocore/1.5.60

バージョンが古い場合は最新版に更新しましょう。

コマンド
sudo -H pip install -U awscli

開始条件

作業に必要なモノ・情報

作業開始には、以下が全て揃っていることが必要です。

  • CloudFormationスタック名

    • 削除するCloudFormationスタックの名称です。
    • 今回は”BasicWebServerInVPC”とします。

0. 準備

まず変数の確認をします。

変数の確認
cat << ETX

        AWS_DEFAULT_PROFILE: (0.1) ${AWS_DEFAULT_PROFILE}
        AWS_DEFAULT_REGION:  (0.2) ${AWS_DEFAULT_REGION}
        CF_STACK_NAME:       (0.4) ${CF_STACK_NAME}

ETX

結果(例):

  AWS_DEFAULT_PROFILE: (0.1) <CloudFormationのフル権限が許可されているプロファイル>
  AWS_DEFAULT_REGION:  (0.2) ap-northeast-1
  CF_STACK_NAME:       (0.4) BasicWebServerInVPC

変数が入っていない、適切でない場合は、それぞれの手順番号について作業を行います。

0.1. プロファイルの指定

プロファイルの一覧を確認します。

コマンド
cat ~/.aws/credentials 
       | grep '[' 
       | sed 's/[//g' | sed 's/]//g'

結果(例):

  iamFull-prjz-mbpr13
  <CloudFormationのフル権限が許可されているプロファイル>
変数の設定
export AWS_DEFAULT_PROFILE='<CloudFormationのフル権限が許可されているプロファイル>'

0.2. リージョンの決定

変数の設定
export AWS_DEFAULT_REGION='ap-northeast-1'

0.3. スタック名の指定

変数の設定
CF_STACK_NAME='BasicWebServerInVPC'

再確認

設定されている変数の内容を再確認します。

変数の確認
cat << ETX

        AWS_DEFAULT_PROFILE: (0.1) ${AWS_DEFAULT_PROFILE}
        AWS_DEFAULT_REGION:  (0.2) ${AWS_DEFAULT_REGION}
        CF_STACK_NAME:       (0.4) ${CF_STACK_NAME}

ETX

結果(例):

  AWS_DEFAULT_PROFILE: (0.1) <CloudFormationのフル権限が許可されているプロファイル>
  AWS_DEFAULT_REGION:  (0.2) ap-northeast-1
  CF_STACK_NAME:       (0.4) BasicWebServerInVPC

1. 事前作業

1.1. 作業対象の状態確認

削除しようとしているCloudFormationスタック”BasicWebServerInVPC”は存在することを確認します。

コマンド
CF_STACK_STATUS=$( 
        aws cloudformation describe-stacks 
          --stack-name ${CF_STACK_NAME} 
          --query "Stacks[?StackName == `${CF_STACK_NAME}`].StackStatus" 
          --output text 
) 
        && echo ${CF_STACK_STATUS}

結果(例):

  CREATE_COMPLETE

2. 本作業

2.1. スタックの削除

変数の確認
cat << ETX

        CF_STACK_NAME: ${CF_STACK_NAME}

ETX
コマンド
aws cloudformation delete-stack 
        --stack-name ${CF_STACK_NAME}

結果:

  (戻り値なし)

2.2. ステータスの確認

コマンド
CF_STACK_STATUS=$( 
        aws cloudformation describe-stacks 
          --stack-name ${CF_STACK_NAME} 
          --query "Stacks[?StackName == `${CF_STACK_NAME}`].StackStatus" 
          --output text 
) 
        && echo ${CF_STACK_STATUS}

結果(例):

  DELETE_IN_PROGRESS

2.3. イベントの確認

ステータスがROLLBUCKになった場合は、イベントを確認して原因を特定します。

コマンド
aws cloudformation describe-stack-events 
        --stack-name ${CF_STACK_NAME}

結果(例):

  (省略)

3. 事後作業

3.1. 完了条件の確認

削除したCloudFormationスタック”BasicWebServerInVPC”が存在しないことを確認します。

コマンド
aws cloudformation describe-stacks 
        --stack-name ${CF_STACK_NAME}

結果(例):

  An error occurred (ValidationError) when calling the DescribeStacks operation: Stack with id BasicWebServerInVPC does not exist

完了

続きを読む

[JAWS-UG CLI] CloudFormation:#5 スタックの作成

この手順の目的

CloudFormationスタック”BasicWebServerInVPC”を作成します。

作業対象

  • CloudFormationサービス

標準時間

8分

前提条件

作業権限

CloudFormationへの権限

CloudFormationに対してフル権限があること。

作業環境

AWS CLIのバージョン

以下のバージョンで動作確認済

  • AWS CLI 1.11.14
コマンド
aws --version

結果(例):

  aws-cli/1.11.97 Python/2.7.12 Linux/4.4.11-23.53.amzn1.x86_64 botocore/1.5.60

バージョンが古い場合は最新版に更新しましょう。

コマンド
sudo -H pip install -U awscli

開始条件

作業に必要なモノ・情報

作業開始には、以下が全て揃っていることが必要です。

  • 設定ファイル用ディレクトリ

    • 今回は”${HOME}/tmp/conf-cloudformation”を設定ファイルディレクトリとします。
ls ${HOME}/tmp/conf-cloudformation
  • 存在しない場合は作成します。
mkdir -p ${HOME}/tmp/conf-cloudformation
  • CloudFormationスタック名

    • 作成するCloudFormationスタックの名称です。
    • 今回は”BasicWebServerInVPC”とします。
  • テンレートファイル

    • 検査の対象となるCloudFormationテンプレートファイルです。
    • 今回は”BasicWebServerInVPC.template”とします。
  • テンプレートのパラメータ

    • テンプレートに必要なパラメータです。
    • パラメータに含まれているリソースは存在するものとします。
変数の設定
EC2_KEYPAIR_NAME='<キーペア名>'

0. 準備

まず変数の確認をします。

変数の確認
cat << ETX

        AWS_DEFAULT_PROFILE: (0.1) ${AWS_DEFAULT_PROFILE}
        AWS_DEFAULT_REGION:  (0.2) ${AWS_DEFAULT_REGION}
        DIR_CONF:            (0.3) ${DIR_CONF}
        CF_STACK_NAME:       (0.4) ${CF_STACK_NAME}
        FILE_INPUT:          (0.5) ${FILE_INPUT}
        CF_STACK_PARAMETERS: (0.6) ${CF_STACK_PARAMETERS}

ETX

結果(例):

  AWS_DEFAULT_PROFILE: (0.1) <CloudFormationのフル権限が許可されているプロファイル>
  AWS_DEFAULT_REGION:  (0.2) ap-northeast-1
  DIR_CONF:            (0.3) ${HOME}/tmp/conf-cloudformation
  CF_STACK_NAME:       (0.4) BasicWebServerInVPC
  FILE_INPUT:          (0.5) ${HOME}/tmp/conf-cloudformation/BasicWebServerInVPC.template
  CF_STACK_PARAMETERS: (0.6) ParameterKey=KeyName,ParameterValue=${EC2_KEYPAIR_NAME}

変数が入っていない、適切でない場合は、それぞれの手順番号について作業を行います。

0.1. プロファイルの指定

プロファイルの一覧を確認します。

コマンド
cat ~/.aws/credentials \
       | grep '\[' \
       | sed 's/\[//g' | sed 's/\]//g'

結果(例):

  iamFull-prjz-mbpr13
  <CloudFormationのフル権限が許可されているプロファイル>
変数の設定
export AWS_DEFAULT_PROFILE='<CloudFormationのフル権限が許可されているプロファイル>'

0.2. リージョンの決定

変数の設定
export AWS_DEFAULT_REGION='ap-northeast-1'

0.3. 設定ファイルディレクトリの指定

変数の設定
DIR_CONF="${HOME}/tmp/conf-cloudformation"

0.4. スタック名の指定

変数の設定
CF_STACK_NAME='BasicWebServerInVPC'

0.5. テンプレートファイル名の指定

変数の設定
FILE_INPUT="${DIR_CONF}/${CF_STACK_NAME}.template" \
        && echo ${FILE_INPUT}

0.6. パラメータの指定

変数の設定
CF_STACK_PARAMETERS="ParameterKey=KeyName,ParameterValue=${EC2_KEYPAIR_NAME}" \
        && echo ${CF_STACK_PARAMETERS}

再確認

設定された変数の内容を再確認します。

変数の確認
cat << ETX

        AWS_DEFAULT_PROFILE: (0.1) ${AWS_DEFAULT_PROFILE}
        AWS_DEFAULT_REGION:  (0.2) ${AWS_DEFAULT_REGION}
        DIR_CONF:            (0.3) ${DIR_CONF}
        CF_STACK_NAME:       (0.4) ${CF_STACK_NAME}
        FILE_INPUT:          (0.5) ${FILE_INPUT}
        CF_STACK_PARAMETERS: (0.6) ${CF_STACK_PARAMETERS}

ETX

結果(例):

  AWS_DEFAULT_PROFILE: (0.1) <CloudFormationのフル権限が許可されているプロファイル>
  AWS_DEFAULT_REGION:  (0.2) ap-northeast-1
  DIR_CONF:            (0.3) ${HOME}/tmp/conf-cloudformation
  CF_STACK_NAME:       (0.4) BasicWebServerInVPC
  FILE_INPUT:          (0.5) ${HOME}/tmp/conf-cloudformation/BasicWebServerInVPC.template
  CF_STACK_PARAMETERS: (0.6) ParameterKey=KeyName,ParameterValue=${EC2_KEYPAIR_NAME}

1. 事前作業

1.1. 作業対象の状態確認

設定ファイル用ディレクトリが存在することを確認します。

コマンド
ls -d ${DIR_CONF}

結果(例):

  <ディレクトリのフルパス名>

テンプレートファイルが設定ファイル用ディレクトリに存在することを確認します。

コマンド
ls -1 ${FILE_INPUT}

結果:

  ${HOME}/tmp/conf-cloudformation/BasicWebServerInVPC.template

作成しようとしているCloudFormationスタック”BasicWebServerInVPC”はまだ存在しないことを確認します。

コマンド
aws cloudformation describe-stacks \
        --stack-name ${CF_STACK_NAME}

結果(例):

  An error occurred (ValidationError) when calling the DescribeStacks operation: Stack with id BasicWebServerInVPC does not exist

2. 本作業

2.1. スタックの作成

変数の確認
cat << ETX

        CF_STACK_NAME:       ${CF_STACK_NAME}
        CF_STACK_PARAMETERS: ${CF_STACK_PARAMETERS}
        FILE_INPUT:          ${FILE_INPUT}

ETX
コマンド
aws cloudformation create-stack \
        --stack-name "${CF_STACK_NAME}" \
        --template-body file://${FILE_INPUT} \
        --parameters ${CF_STACK_PARAMETERS}

結果(例):

  {
    "StackId": "arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXXXX:stack/BasicWebServerInVPC/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
  }

2.2. ステータスの確認

コマンド
CF_STACK_STATUS=$( \
        aws cloudformation describe-stacks \
          --stack-name ${CF_STACK_NAME} \
          --query "Stacks[?StackName == \`${CF_STACK_NAME}\`].StackStatus" \
          --output text \
) \
        && echo ${CF_STACK_STATUS}

結果(例):

  CREATE_IN_PROGRESS

2.3. イベントの確認

ステータスがROLLBUCKになった場合は、イベントを確認して原因を特定します

コマンド
aws cloudformation describe-stack-events \
        --stack-name ${CF_STACK_NAME}

結果(例):

  (省略)

3. 事後作業

3.1. 完了条件の確認

作成したCloudFormationスタック”BasicWebServerInVPC”が存在することを確認します。

コマンド
CF_STACK_STATUS=$( \
        aws cloudformation describe-stacks \
          --stack-name ${CF_STACK_NAME} \
          --query "Stacks[?StackName == \`${CF_STACK_NAME}\`].StackStatus" \
          --output text \
) \
        && echo ${CF_STACK_STATUS}

結果(例):

  CREATE_COMPLETE

完了

続きを読む

[JAWS-UG CLI] CloudFormation:#4 テンプレートの検査

この手順の目的

CloudFormationのテンプレートを検査(バリデート)します。

作業対象

  • CloudFormationテンプレート

標準時間

8分

前提条件

作業権限

CloudFormationへの権限

CloudFormationに対してフル権限があること。

作業環境

AWS CLIのバージョン

以下のバージョンで動作確認済

  • AWS CLI 1.11.102
コマンド
aws --version

結果(例):

  aws-cli/1.11.102 Python/2.7.12 Linux/4.4.11-23.53.amzn1.x86_64 botocore/1.5.60

バージョンが古い場合は最新版に更新しましょう。

コマンド
sudo -H pip install -U awscli

開始条件

作業に必要なモノ・情報

作業開始には、以下が全て揃っていることが必要です。

  • 設定ファイル用ディレクトリ

    • 今回は”${HOME}/tmp/conf-cloudformation”を設定ファイルディレクトリとします。
ls ${HOME}/tmp/conf-cloudformation
  • 存在しない場合は作成します。
mkdir -p ${HOME}/tmp/conf-cloudformation
  • テンレートファイル

    • 検査の対象となるCloudFormationテンプレートファイルです。
    • 今回は”BasicWebServerInVPC.template”とします。

0. 準備

まず変数の確認をします。

変数の確認
cat << ETX

        AWS_DEFAULT_PROFILE: (0.1) ${AWS_DEFAULT_PROFILE}
        AWS_DEFAULT_REGION:  (0.2) ${AWS_DEFAULT_REGION}
        DIR_CONF:            (0.3) ${DIR_CONF}
        FILE_INPUT:          (0.4) ${FILE_INPUT}

ETX

結果(例):

  AWS_DEFAULT_PROFILE: (0.1) <CloudFormationのフル権限が許可されているプロファイル>
  AWS_DEFAULT_REGION:  (0.2) ap-northeast-1
  DIR_CONF:            (0.3) ${HOME}/tmp/conf-cloudformation
  FILE_INPUT:          (0.4) ${HOME}/tmp/conf-cloudformation/BasicWebServerInVPC.template

変数が入っていない、適切でない場合は、それぞれの手順番号について作業を行います。

0.1. プロファイルの指定

プロファイルの一覧を確認します。

コマンド
cat ~/.aws/credentials 
       | grep '[' 
       | sed 's/[//g' | sed 's/]//g'

結果(例):

  iamFull-prjz-mbpr13
  <CloudFormationのフル権限が許可されているプロファイル>
変数の設定
export AWS_DEFAULT_PROFILE='<CloudFormationのフル権限が許可されているプロファイル>'

0.2. リージョンの決定

変数の設定
export AWS_DEFAULT_REGION='ap-northeast-1'

0.3. 設定ファイルディレクトリの指定

変数の設定
DIR_CONF="${HOME}/tmp/conf-cloudformation"

0.4. テンプレートファイル名の指定

変数の設定
FILE_INPUT="${DIR_CONF}/BasicWebServerInVPC.template"   && echo ${FILE_INPUT}

再確認

設定されている変数の内容を再確認します。

変数の確認
cat << ETX

        AWS_DEFAULT_PROFILE: (0.1) ${AWS_DEFAULT_PROFILE}
        AWS_DEFAULT_REGION:  (0.2) ${AWS_DEFAULT_REGION}
        DIR_CONF:            (0.3) ${DIR_CONF}
        FILE_INPUT:          (0.4) ${FILE_INPUT}

ETX

結果(例):

  AWS_DEFAULT_PROFILE: (0.1) <CloudFormationのフル権限が許可されているプロファイル>
  AWS_DEFAULT_REGION:  (0.2) ap-northeast-1
  DIR_CONF:            (0.3) ${HOME}/tmp/conf-cloudformation
  FILE_INPUT:          (0.4) ${HOME}/tmp/conf-cloudformation/BasicWebServerInVPC.template

1. 事前作業

1.1. 作業対象の状態確認

設定ファイル用ディレクトリが存在することを確認します。

コマンド
ls -d ${DIR_CONF}

結果(例):

  <ディレクトリのフルパス名>

検査しようとしているcloudformationテンプレートファイル
“BasicWebServerInVPC.template”が存在することを確認します。

コマンド
ls -1 ${FILE_INPUT}

結果:

  ${HOME}/tmp/conf-cloudformation/BasicWebServerInVPC.template

2. 本作業

2.1. テンプレートのバリデーション

コマンド
aws cloudformation validate-template 
        --template-body file://${FILE_INPUT}

結果:

  (テンプレートの出力設定に応じた出力)

2.2. 結果の取得

変数の設定
RESULT_VALIDATION=$?

3. 事後作業

cloudformationテンプレート”BasicWebServerInVPC”が有効であることを確認します。

コマンド
echo ${RESULT_VALIDATION}

完了

続きを読む

AWS Lambda&cloudwatchのルールでインスタンスを自動停止させる

最近AWSを勉強するために、個人でアカウントを作り色々いじって遊んでいます。
インスタンスを停止し忘れることがしょっちゅうあるので、自動で停止するようにしようと思い、調べました。

インスタンスのcronにawscliのコマンドを登録してもよかったのですが、せっかくなのでAWSの機能を色々使うことにしました。

以下の記事と公式のドキュメントを参考にして作業を進めました。

https://geeknavi.net/aws/ec2インスタンスをスケジュールで自動起動・自動
https://aimless.jp/blog/archives/2681/

前提

EC2インスタンスが既に存在しており、起動した状態であること。

Lambdaの登録

AWSにログインをしたらLambdaの画面を開き、新規関数を作成します。

設計図の選択

設計図は Blank Function をクリックします。

スクリーンショット 2017-06-11 14.05.12.png

トリガーの設定

何もせずに次へをクリックします。

スクリーンショット 2017-06-11 14.06.51.png

関数の設定

画面上部

スクリーンショット 2017-06-11 14.08.14.png

名前、説明はお好きなように。
とりあえず

  • 名前
    stop_ec2_instance

  • 説明
    インスタンスを停止する関数

としておきます。

  • ランタイム

Node.jsのままにします。

  • コード

参考記事に記載されている以下の内容にします。
SDKについてはドキュメントがありますので、詳細を知りたい場合は目を通しておくとよいと思います。
以下はJavascript用です。
http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/index.html

YOUR_INSTANCE_ID , YOUR_REGION は自身の環境に合わせて書き換えてください。
idはArrayで渡すので、複数指定が可能なようです。
(ドキュメントを見ると Array<String> となっています)

const INSTANCE_ID = 'YOUR_INSTANCE_ID';

var AWS = require('aws-sdk'); 
AWS.config.region = 'YOUR_REGION';

function ec2Stop(cb){
    var ec2 = new AWS.EC2();
    var params = {
        InstanceIds: [
            INSTANCE_ID
        ]
    };

    ec2.stopInstances(params, function(err, data) {
        if (!!err) {
            console.log(err, err.stack);
        } else {
            console.log(data);
            cb();
        }
    });
}
exports.handler = function(event, context) {
    console.log('start');
    ec2Stop(function() {
        context.done(null, 'Stoped Instance');
    });
};

画面下部

スクリーンショット 2017-06-11 14.20.35.png

  • 環境変数

特に設定しないので空欄のままにします。

  • ハンドラ

そのままにします。

  • ロール

カスタムロールの作成を選びます。
そうすると別タブにIAM ロール作成画面が開くので作成します。

IAM ロール

スクリーンショット 2017-06-11 14.25.59.png

  • IAM ロール

そのままにします。

  • ロール名

お好きなように。
とりあえず stop_instance_lambda とします。

  • ポリシードキュメント

クリックするとテキストエリアが開くので、以下の内容に書き換えます。
ポリシードキュメントについてはこちらの公式ドキュメントを読むとよいと思います。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents",
                "ec2:StopInstances"
            ],
            "Resource": [
                "arn:aws:logs:*:*:*",
                "arn:aws:ec2:*"
            ]
        }
    ]
}

入力し終わったら右下に 許可 ボタンがあるのでクリックします。
作成が完了するとlambdaの画面に戻ります。

既存のロールで先ほど作成したロールが選べるようになっているので選択します。

  • タグ
  • 詳細設定

こちらはデフォルトのままにするので何もせず。
画面右下の次へをクリックします。

確認画面に遷移するので、タイポなどがないか確認し、問題なければ 関数の作成 ボタンをクリックします。

関数確認

スクリーンショット 2017-06-11 14.35.19.png

作成が完了するとこちらの画面に遷移します。
作った関数が動くか確認するためテストをクリックします。

  • テスト

スクリーンショット 2017-06-11 14.37.17.png

このような画面が開くので、そのまま何もせず 保存してテスト をクリックします。
そうすると関数が実行されます。

実行した結果が画面の下部に表示されるので、ログにエラーがないことを確認してください。
そして指定したインスタンスが動作を停止しているかどうか確認し、停止していればOKです。

Lambdaのスケジュール実行

毎日0時にlambdaを実行するために、cloudwatchの設定をします。
cloudwatchの画面を開き、左メニューの イベント をクリックして画面を開き、ルールの作成をクリックしてください。

イベントソース

スクリーンショット 2017-06-11 14.48.26.png

  • イベントパターン or スケジュール

スケジュール のラジオボタンを選択してください。

  • CloudWatch イベントスケジュール

好みの問題になりそうなのですが、 cron式 を選択して以下の内容に書き換えてください。
イベントスケジュールの詳細はこちらに目を通しておくとよいです。

0 15 * * ? *

注意事項としては、時間は GMT グリニッジ標準時 で実行されるそうなので、9時間マイナスして考える必要があります。
(上記の場合、日本時間のAM0:00に実行させます)

ターゲット

ターゲットの追加をクリックし、先ほど作成したLambdaを選びましょう。

  • バージョン/エイリアスの設定
  • 入力の設定

はデフォルトのままにします。
最終的に以下のようになると思います。

スクリーンショット 2017-06-11 15.00.52.png

右下の設定の詳細をクリックします。

ルールの詳細の設定

スクリーンショット 2017-06-11 15.01.52.png

  • 名前
  • 説明

こちらはお好きなように。
入力が完了したら右下のルールの作成をクリックして完了します。

さいごに

インスタンスを起動させたままにして0時を迎えましょう。
lambdaが起動するはずなので、

  • cloudwatchのログ
  • インスタンスの状態

を確認し、lambdaが実行されていることと、インスタンスが停止していればOKです。

続きを読む

aws周りのメモ3

インスタンスの起動と停止など

【Tips】AWS CLIを使ってAmazon EC2を起動・停止するワンライナーまとめ | Developers.IO
http://dev.classmethod.jp/cloud/aws/awscli-tips-ec2-start-stop/

起動

INSTANCE_ID=<instance_id>
# 起動
aws ec2 start-instances --instance-ids ${INSTANCE_ID}
# 起動確認
aws ec2 describe-instance-status --instance-ids ${INSTANCE_ID} | jq '.InstanceStatuses[] | {InstanceId, InstanceState: .InstanceState.Name, SystemStatus: .SystemStatus.Status, InstanceStatus: .InstanceStatus.Status}'
# 起動と待機
aws ec2 start-instances --instance-ids ${INSTANCE_ID} && aws ec2 wait instance-running --instance-ids ${INSTANCE_ID}
# 全起動
aws ec2 start-instances --instance-ids $(aws ec2 describe-instances | jq -r '[.Reservations[].Instances[] | select(.State.Name == "stopped") | .InstanceId] | join(" ")')

停止

# 停止
aws ec2 stop-instances --instance-ids ${INSTANCE_ID}
# 停止確認
aws ec2 describe-instances --instance-ids ${INSTANCE_ID} | jq '.Reservations[].Instances[] | {InstanceId, InstanceState: .State.Name}'
# 停止と待機
aws ec2 stop-instances --instance-ids ${INSTANCE_ID} && aws ec2 wait instance-stopped --instance-ids ${INSTANCE_ID}
# 全停止
aws ec2 stop-instances --instance-ids $(aws ec2 describe-instances | jq -r '[.Reservations[].Instances[] | select(.State.Name == "running") | .InstanceId] | join(" ")')
# 自分以外を全停止
aws ec2 stop-instances --instance-ids $(aws ec2 describe-instances | jq -r --arg myid $(curl http://169.254.169.254/latest/meta-data/instance-id 2> /dev/null) '[.Reservations[].Instances[] | select(.InstanceId != $myid) | select(.State.Name == "running") | .InstanceId] | join(" ")')

AWS CLIにalias機能が追加されました | Developers.IO
http://dev.classmethod.jp/cloud/aws/aws-cli-alias/

~/.aws/cli/aliasに特定の書式でファイルを作成することで利用できるようです。GitHub上のサンプルファイル内には7つの例が記載されています。

https://github.com/awslabs/awscli-aliases

公式

brew upgrade aws-cli

RDSの削除と復帰

AWS CLIを利用したRDSの起動停止スクリプト(検証環境用2017年1月版) | Developers.IO
http://dev.classmethod.jp/cloud/aws/rds-oracle-aurora-start-stop-by-aws-cli/

追記:削除じゃなくて停止ができるようになった(なる?)らしい。

請求まわりがまだよくわからない

【AWS超初心者】 AWSの請求をいろいろと調べたメモ。 – /var/www/yatta47.log
http://yatta47.hateblo.jp/entry/2016/09/14/201121

続きを読む