1.1. Lambda関数用ソースコードファイルの作成 (budgets-monitor)¶
作業の目的 [why]¶
Lambda関数"budgets-monitor"用のソースコードファイルを作成します。
完了条件/事前条件 [設計者用情報]
完了条件 [after]
主処理は、以下を満たしたときに成功したものとします。
- 完了条件1
- Lambda関数"budgets-monitor"のソースコードファイル"${HOME}/tmp/conf-handson-cli/budgets-monitor.py"が存在する。
事前条件 [before]
主処理の実施は、以下の状態であることを前提とします。
- 事前条件1
- 関数コードファイル用ディレクトリ"${HOME}/tmp/conf-handson-cli"が存在する。
- 事前条件2
- Lambda関数"budgets-monitor"のソースコードファイル"${HOME}/tmp/conf-handson-cli/budgets-monitor.py"が存在しない。
- 事前条件3
- Budgets予算"cost-all"が存在する。
作業対象 [what]¶
- Lambdaサービス
標準時間(合計)¶
8分
パラメータ設定¶
パラメータ設定の標準時間: | 2分 |
---|
作業に必要なモノ・情報 [resource]¶
作業開始には、以下が全て揃っていることが必要です。
リソース1: 関数コードファイル用ディレクトリ
- 今回は"${HOME}/tmp/conf-handson-cli"をLambda関数コード用ディレクトリとします。
ディレクトリが存在することを確認します。
コマンド:
ls -d ${HOME}/tmp/conf-handson-cli
結果(例:存在する場合):
${HOME}/tmp/conf-handson-cli
存在しない場合は作成します。
コマンド:
mkdir -p ${HOME}/tmp/conf-handson-cli
リソース2: Lambda関数名
- 作成するLambda関数の名称です。
- 今回は"budgets-monitor"とします。
リソース3: Slack Incoming Hook URL
- 通知先となるSlack Incoming HookのURLです。
- 今回は"<SlackのIncomingHookURL>"とします。
リソース4: Slackチャンネル
- 通知先となるSlackのチャンネルです。 (#で始まる文字列)
- 今回は"#handson-cli"とします。
リソース5: AWSアカウント名称
- Slackに表示される通知元AWSアカウント名です。
- 今回は"<AWSアカウント名>"とします。
リソース6: 予算名
- 通知の対象となる予算名です。
- 今回は"cost-all"とします。
パラメータの指定¶
作業に必要なパラメータを変数に格納をします。
0.3. Slack Incoming Hook URLの指定¶
Slack Incoming Hook URLを指定します。
変数の設定:
SLACK_URL_INCOMING_HOOK='<SlackのIncomingHookURL>'
パラメータの保存¶
設定されている変数の保存先となるファイル名を指定します。
変数の設定:
DIR_PARAMETER="${HOME}/tmp/parameter-handson-cli" FILE_PARAMETER="${DIR_PARAMETER}/$(date +%Y-%m-%d)-lambda-function_code_python-create-expand-slack-incoming.env" \ && echo ${FILE_PARAMETER}
結果(例):
${HOME}/tmp/parameter-handson-cli/2019-04-30-lambda-function_code_python-create-expand-slack-incoming.env
各変数に正しいパラメータ値が格納されていることを確認しながら保存します。
変数の確認:
cat << EOF > ${FILE_PARAMETER} # 0.1. DIR_LAMBDA_CODE:"${HOME}/tmp/conf-handson-cli" DIR_LAMBDA_CODE="${DIR_LAMBDA_CODE}" # 0.2. LAMBDA_FUNCTION_NAME:"budgets-monitor" LAMBDA_FUNCTION_NAME="${LAMBDA_FUNCTION_NAME}" # 0.3. SLACK_URL_INCOMING_HOOK:"<SlackのIncomingHookURL>" SLACK_URL_INCOMING_HOOK="${SLACK_URL_INCOMING_HOOK}" # 0.4. SLACK_CHANNEL:"#handson-cli" SLACK_CHANNEL="${SLACK_CHANNEL}" # 0.5. AWS_ACCOUNT_NAME:"<AWSアカウント名>" AWS_ACCOUNT_NAME="${AWS_ACCOUNT_NAME}" # 0.6. BUDGETS_BUDGET_NAME:"cost-all" BUDGETS_BUDGET_NAME="${BUDGETS_BUDGET_NAME}" EOF cat ${FILE_PARAMETER}
下段の変数が入っていない、もしくは上段と同等の値が入っていない場合は、それぞれの手順番号に戻って変数の設定を行います。
タスクの実施¶
タスク標準時間: | 6分 |
---|
1. 前処理¶
1.1. 状態確認に必要な情報の取得¶
関数コードファイルの指定
変数の設定:
FILE_LAMBDA_CODE="${DIR_LAMBDA_CODE}/${LAMBDA_FUNCTION_NAME}.py" \ && echo ${FILE_LAMBDA_CODE}
結果(例):
${HOME}/tmp/conf-handson-cli/budgets-monitor.py
AWS IDの取得
コマンド:
AWS_ID=$( \ aws sts get-caller-identity \ --query 'Account' \ --output text \ ) \ && echo ${AWS_ID}
結果(例):
XXXXXXXXXXXX
1.2. 処理対象の状態確認¶
主処理の実施は、以下の状態であることを前提とします。
前提と異なることが判明した場合、直ちに処理を中止します。
事前条件1: 関数コードファイル用ディレクトリ"${HOME}/tmp/conf-handson-cli"が存在する。
「関数コードファイル用ディレクトリ"${HOME}/tmp/conf-handson-cli"が存在する。」ことを確認します。
コマンド:
ls -d ${DIR_LAMBDA_CODE}
結果(例):
${HOME}/tmp/conf-handson-cli
事前条件2: Lambda関数"budgets-monitor"のソースコードファイル"${HOME}/tmp/conf-handson-cli/budgets-monitor.py"が存在しない。
「Lambda関数"budgets-monitor"のソースコードファイル"${HOME}/tmp/conf-handson-cli/budgets-monitor.py"が存在しない。」ことを確認します。
コマンド:
! ls ${FILE_LAMBDA_CODE}
結果(例):
ls: ${HOME}/tmp/conf-handson-cli/budgets-monitor.py: No such file or directory
事前条件3: Budgets予算"cost-all"が存在する。
「Budgets予算"cost-all"が存在する。」ことを確認します。
コマンド:
aws budgets describe-budgets \ --account-id ${AWS_ID} \ --query "Budgets[?BudgetName == \`${BUDGETS_BUDGET_NAME}\`].BudgetName" \ --output text
結果(例):
cost-all
sample
2. 主処理¶
関数コードファイルの作成¶
変数の確認:
cat << ETX # AWS_ID:"XXXXXXXXXXXX" AWS_ID="${AWS_ID}" # AWS_ACCOUNT_NAME:"<AWSアカウント名>" AWS_ACCOUNT_NAME="${AWS_ACCOUNT_NAME}" # BUDGETS_BUDGET_NAME:"cost-all" BUDGETS_BUDGET_NAME="${BUDGETS_BUDGET_NAME}" # FILE_LAMBDA_CODE:"${HOME}/tmp/conf-handson-cli/budgets-monitor.py" FILE_LAMBDA_CODE="${FILE_LAMBDA_CODE}" # SLACK_CHANNEL:"#handson-cli" SLACK_CHANNEL="${SLACK_CHANNEL}" # SLACK_URL_INCOMING_HOOK:"<SlackのIncomingHookURL>" SLACK_URL_INCOMING_HOOK="${SLACK_URL_INCOMING_HOOK}" ETX
コマンド:
cat << EOF > ${FILE_LAMBDA_CODE} # -*- coding: utf-8 -*- from __future__ import print_function import logging import json import boto3 from urllib.request import Request, urlopen from urllib.error import URLError, HTTPError hook_url = "${SLACK_URL_INCOMING_HOOK}" channel = "${SLACK_CHANNEL}" budget_name = "${BUDGETS_BUDGET_NAME}" aws_id = "${AWS_ID}" aws_account_name = "${AWS_ACCOUNT_NAME}" logger = logging.getLogger() logger.setLevel(logging.INFO) client = boto3.client('budgets') def lambda_handler(event, context): logger.info("Event: " + str(event)) res = client.describe_budget(AccountId=aws_id, BudgetName=budget_name) limit = str(res['Budget']['BudgetLimit']['Amount'])[:7] act = str(res['Budget']['CalculatedSpend']['ActualSpend']['Amount'])[:7] forecast = str(res['Budget']['CalculatedSpend']['ForecastedSpend']['Amount'])[:7] post_text = u"%s の予算状況を報告します。 (予算: $%s / 現行: $%s / 予測: $%s)" % (aws_account_name, limit, act, forecast) slack_message = { 'channel': channel, 'text': post_text } req = Request(hook_url, json.dumps(slack_message).encode('utf-8')) try: response = urlopen(req) response.read() logger.info("Message posted to %s", slack_message['channel']) except HTTPError as e: logger.error("Request failed: %d %s", e.code, e.reason) except URLError as e: logger.error("Server connection failed: %s", e.reason) EOF cat ${FILE_LAMBDA_CODE}
結果(例):
# -- coding: utf-8 -- from __future__ import print_function import logging import json import boto3 from urllib.request import Request, urlopen from urllib.error import URLError, HTTPError hook_url = "<SlackのIncomingHookURL>" channel = "#handson-cli" budget_name = "cost-all" aws_id = "XXXXXXXXXXXX" aws_account_name = "<AWSアカウント名>" logger = logging.getLogger() logger.setLevel(logging.INFO) client = boto3.client('budgets') def lambda_handler(event, context): logger.info("Event: " + str(event)) res = client.describe_budget(AccountId=aws_id, BudgetName=budget_name) limit = str(res['Budget']['BudgetLimit']['Amount'])[:7] act = str(res['Budget']['CalculatedSpend']['ActualSpend']['Amount'])[:7] forecast = str(res['Budget']['CalculatedSpend']['ForecastedSpend']['Amount'])[:7] post_text = u"%s の予算状況を報告します。 (予算: $%s / 現行: $%s / 予測: $%s)" % (aws_account_name, limit, act, forecast) slack_message = { 'channel': channel, 'text': post_text } req = Request(hook_url, json.dumps(slack_message).encode('utf-8')) try: response = urlopen(req) response.read() logger.info("Message posted to %s", slack_message['channel']) except HTTPError as e: logger.error("Request failed: %d %s", e.code, e.reason) except URLError as e: logger.error("Server connection failed: %s", e.reason)
課題
実績値が0だと、予測が空なのでエラーになる。
3. 後処理¶
完了条件の確認¶
主処理は、以下を満たしたときに成功したものとします。
完了条件1: Lambda関数"budgets-monitor"のソースコードファイル"${HOME}/tmp/conf-handson-cli/budgets-monitor.py"が存在する。
「Lambda関数"budgets-monitor"のソースコードファイル"${HOME}/tmp/conf-handson-cli/budgets-monitor.py"が存在する。」ことを確認します。
コマンド:
ls ${FILE_LAMBDA_CODE}
結果(例):
${HOME}/tmp/conf-handson-cli/budgets-monitor.py