RDS(Aurora)のメンテナンス情報を、AWS CLIのdescribe-pending-maintenance-actionsで情報を取得して、
slack apiにメッセージを送るシェルスクリプトです。
PHDやAWS Health APIでもRDSのメンテナンス情報の取得は可能です。
他のリソースについても通知が可能なので、メンテナンス情報の通知を受け取るだけであれば、そちらを利用した方が良いと思います。
ただ、実行時点のメンテナンス適用状況の情報が必要な場合はdescribe-pending-maintenance-actionsを使用します。
※PHD や Health API で取得する情報は、その時点のメンテナンス適用状況が取得されるのではなく、通知した時点での適用対象から変化しない為
環境情報
Slackワークスペースプラン:スタンダード(有料)
$ cat /etc/system-release
Amazon Linux release 2 (Karoo)
$ uname -a
Linux hogehuga.ap-northeast-1.compute.internal 4.14.77-81.59.amzn2.x86_64 #1 SMP Mon Nov 12 21:32:48 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
$ aws --v
aws-cli/1.16.102 Python/2.7.14 Linux/4.14.77-81.59.amzn2.x86_64 botocore/1.12.92
SlackAPIToken準備
別稿としておりますのでこちらをご参照ください。
今回Botを使用するので、AppのPermission Scopeは「bot(Add a bot user with the username@hogehuga)」があれば大丈夫です。
シェルスクリプト本体
元々Slack通知にはIncoming Webhooksを使用し、メンテナンス内容の緊急度によってicon_emojiを変更するつもりだったのですが、jq等で解析せずに見やすく通知出来なかったのでfiles.uploadを使用しています。
見やすく出来なかった実装(クリックで展開)
#!/bin/bash
# ================================
# 設定
# ================================
SHELL_FILE_NAME=`basename $0`
SHELL_DIR=$(cd "$(dirname "$0")" && pwd)
WORK_DIR=${SHELL_DIR}/work
LOG_DIR=${SHELL_DIR}/log
OUTPUT_AWSCLI_JSON=${WORK_DIR}/output_awscli.json
BEFORE_OUTPUT_AWSCLI_JSON=${WORK_DIR}/before_output_awscli.json
RDS_MENTENANCE_NOTICE_LOG=${LOG_DIR}/rds_maintenance_notice.log
# 前回と差分が無い場合(true:通知しない,false:通知する)
NO_DIFF_NOT_NOTIFICATION=true
# 結果をSlackへ(true:送信する,false:送信しない)
SEND_SLACK=true
# Slack設定
SLACK_WEBHOOK_URL="https://hooks.slack.com/services/ABCDEFGHI/GKLMNOPQR/stuvwxyz1234567890"
SLACK_INFO_USER_NAME="RDS maintenance info"
SLACK_NOTIFIER_USER_NAME="RDS maintenance notifier"
SLACK_EMERGENCY_USER_NAME="RDS maintenance Alert"
SLACK_INFO_ICON=":information_source:"
SLACK_NOTIFIER_ICON=":warning:"
SLACK_EMERGENCY_ICON=":sos:"
SLACK_CHANNEL_NAME="#AWS通知"
# ================================
# 関数
# ================================
# 使用方法
function usage {
cat <<EOF
Usage:
$(basename ${0}) [options]
Description:
$(basename ${0}) is a tool for Get pending RDS maintenance actions and send Slack notification
Options:
-h show this help, then exit
EOF
return 0
}
# オプション解析
while getopts h OPT ; do
case $OPT in
h)
usage
exit 0
;;
esac
done
# ログ出力
function log() {
time=`date "+%Y/%m/%d %H:%M:%S.%3N"`
shellName=`basename ${0%.*}`
level=$1; shift
color=$1; shift
echo -e "${time} ${level:0:1} [${shellName}] \e[${color}m$*\e[m"
}
# ログ出力:各レベル
function log_error() { log "ERROR" "1;31" "$*";}
function log_warn() { log "WARN" "1;33" "$*";}
function log_info() { log "INFO" "1;34" "$*";}
function log_debug() { log "DEBUG" "1;37" "$*";}
# Slack:メッセージ送信
function slack_post() {
# パラメータ取得
hookUrl="$1"; shift
text="$1"; shift
# 必須パラメータチェック
if [ "${hookUrl}" = "" ]; then
log_error "hookUrl is empty."
return 1
fi
if [ "${text}" = "" ]; then
log_error "text is empty."
return 1
fi
# text内に引用符が存在するとinvalid payloadとなるので除去
text=$(echo ${text} | sed 's/"//g')
# オプション指定を反映
local OPTIND
while getopts u:i:c: OPT
do
case $OPT in
"u" ) username="$OPTARG";;
"i" ) iconEmoji="$OPTARG";;
"c" ) channel="$OPTARG";;
esac
done
shift $((OPTIND - 1))
payload=`cat << EOF
payload={
"text": "${text}",
"username": "${username}",
"icon_emoji": "${iconEmoji}",
"channel": "${channel}"
}
EOF
`
# Slackに送信
curl -X POST -d "${payload}" "${hookUrl}"
return $!
}
# ================================
# 処理
# ================================
log_info "処理開始"
# 前回の出力をbeforeに変更
if [ -f "${OUTPUT_AWSCLI_JSON}" ]; then
mv -f ${OUTPUT_AWSCLI_JSON} ${BEFORE_OUTPUT_AWSCLI_JSON}
fi
# RDSメンテナンス情報取得
output=`aws rds describe-pending-maintenance-actions | tee ${OUTPUT_AWSCLI_JSON}`
# メンテナンス情報が無ければ通知しない
if [ -f "${OUTPUT_AWSCLI_JSON}" ]; then
maintenanceInfo=`cat ${output} | jq '.PendingMaintenanceActions[]'`
if [ -z "${maintenanceInfo}" ]; then
log_info "RDS maintenance info is not."
log_info "処理終了"
exit 0
fi
fi
# 前回の出力と内容が変わっていなければ通知しない
if [ -f "${BEFORE_OUTPUT_AWSCLI_JSON}" ]; then
diff ${BEFORE_OUTPUT_AWSCLI_JSON} ${OUTPUT_AWSCLI_JSON}
if [ $? -eq 0 ] && "${NO_DIFF_NOT_NOTIFICATION}"; then
log_info "RDS maintenance info is not changed."
log_info "処理終了"
exit 0
fi
fi
# Slackに送信
if "${SEND_SLACK}"; then
# text="<!channel> 保留中のメンテナンスアクションがあります。 ${output}"
username="${SLACK_INFO_USER_NAME}"
icon="${SLACK_INFO_ICON}"
# 適用日が決まっていれば通知内容を変更
if grep "ForcedApplyDate" "${OUTPUT_AWSCLI_JSON}"; then
# text="<!channel> 強制的に適用予定のメンテナンスアクションがあります。 ${output}"
username="${SLACK_EMERGENCY_USER_NAME}"
icon="${SLACK_EMERGENCY_ICON}"
elif grep -e "AutoAppliedAfterDate" -e "CurrentApplyDate" "${OUTPUT_AWSCLI_JSON}"; then
# text="<!channel> 次回メンテナンスウィンドウ時に自動で適用予定のメンテナンスアクションがあります。 ${output}"
username="${SLACK_NOTIFIER_USER_NAME}"
icon="${SLACK_NOTIFIER_ICON}"
fi
# Slackに送信
slack_post "${SLACK_WEBHOOK_URL}" "${output}" -u "${username}" -i "${icon}" -c "${SLACK_CHANNEL_NAME}"
fi
log_info "処理終了"
exit 0
slack apiでは投稿するBotかユーザーを用意する必要がありますが、API内でIncoming Webhooksのようにアイコンが変更出来なかったので異なるアイコンのBotを用意し、それぞれのTokenを設定しています。
通常はBot1体分のTokenを設定すれば良いと思います。
もしより良い実現方法あればコメントいただけると嬉しいです!
#!/bin/bash
# ================================
# 設定
# ================================
SHELL_FILE_NAME=`basename $0`
SHELL_DIR=$(cd "$(dirname "$0")" && pwd)
WORK_DIR=${SHELL_DIR}/work
LOG_DIR=${SHELL_DIR}/log
OUTPUT_AWSCLI_JSON=${WORK_DIR}/output_awscli.json
BEFORE_OUTPUT_AWSCLI_JSON=${WORK_DIR}/before_output_awscli.json
RDS_MENTENANCE_NOTICE_LOG=${LOG_DIR}/rds_maintenance_notice.log
# 前回と差分が無い場合(true:通知しない,false:通知する)
NO_DIFF_NOT_NOTIFICATION=true
# 結果をSlackへ(true:送信する,false:送信しない)
SEND_SLACK=true
# Slack設定
SLACK_FILEUPLOAD_URL="https://slack.com/api/files.upload"
SLACK_INFO_TOKEN="xoxb-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx"
SLACK_NOTIFIER_TOKEN="xoxb-yyyyyyyyyyyy-yyyyyyyyyyyy-yyyyyyyyyyyyyyyyyyyyyyyy"
SLACK_EMERGENCY_TOKEN="xoxb-zzzzzzzzzzzz-zzzzzzzzzzzz-zzzzzzzzzzzzzzzzzzzzzzzz"
SLACK_CHANNEL_NAME="#AWS通知"
# ================================
# 関数
# ================================
# 使用方法
function usage {
cat <<EOF
Usage:
$(basename ${0}) [options]
Description:
$(basename ${0}) is a tool for Get pending RDS maintenance actions and send Slack notification
Options:
-h show this help, then exit
EOF
return 0
}
# オプション解析
while getopts h OPT ; do
case $OPT in
h)
usage
exit 0
;;
esac
done
# ログ出力
function log() {
time=`date "+%Y/%m/%d %H:%M:%S.%3N"`
shellName=`basename ${0%.*}`
level=$1; shift
color=$1; shift
echo -e "${time} ${level:0:1} [${shellName}] \e[${color}m$*\e[m"
}
# ログ出力:各レベル
function log_error() { log "ERROR" "1;31" "$*";}
function log_warn() { log "WARN" "1;33" "$*";}
function log_info() { log "INFO" "1;34" "$*";}
function log_debug() { log "DEBUG" "1;37" "$*";}
# Slack:ファイルアップロード
function slack_upload() {
# パラメータ取得
token="$1"; shift
file="$1"; shift
# 必須パラメータチェック
if [ "${token}" = "" ]; then
log_error "channels is empty."
return 1
fi
if [ "${file}" = "" -a ! -f "${file}" ]; then
log_error "file is empty or not exists: ${file}"
return 1
fi
# パラメータをマップに格納
declare -A parameters
parameters=(
["token"]="${token}"
)
# オプションを格納
# filename:ダウンロード時のファイル名、title未設定の場合はメッセージ上で見えるファイルタイトルにもなる
# title:メッセージ上で見えるファイルタイトル、filename未設定の場合はダウンロード時のファイル名にもなる
local OPTIND
while getopts c:f:t:i:h:n: OPT
do
case "$OPT" in
"c" ) parameters["channels"]="$OPTARG";;
"f" ) parameters["filename"]="$OPTARG";;
"t" ) parameters["filetype"]="$OPTARG";;
"i" ) parameters["initial_comment"]="$OPTARG";;
"h" ) parameters["thread_ts"]="$OPTARG";;
"n" ) parameters["title"]="$OPTARG";;
esac
done
shift $((OPTIND - 1))
# cURLの引数にパラメータを格納
curlArgs=()
for paramName in ${!parameters[@]}; do
paramValue="${parameters[$paramName]}"
curlArgs=("${curlArgs[@]}" "--form-string" "${paramName}=${paramValue}")
done
# ファイルをパラメータに追加
curlArgs=("${curlArgs[@]}" "-F" "file=@${file}")
# Slackに送信
log_debug "curl args: ${curlArgs[@]}"
curl "${curlArgs[@]}" "${SLACK_FILEUPLOAD_URL}"
return $!
}
# ================================
# 処理
# ================================
log_info "処理開始"
# 前回の出力をbeforeに変更
if [ -f "${OUTPUT_AWSCLI_JSON}" ]; then
mv -f ${OUTPUT_AWSCLI_JSON} ${BEFORE_OUTPUT_AWSCLI_JSON}
fi
# RDSメンテナンス情報取得
aws rds describe-pending-maintenance-actions > ${OUTPUT_AWSCLI_JSON}
# メンテナンス情報が無ければ通知しない
if [ -f "${OUTPUT_AWSCLI_JSON}" ]; then
maintenanceInfo=`cat ${OUTPUT_AWSCLI_JSON} | jq '.PendingMaintenanceActions[]'`
if [ -z "${maintenanceInfo}" ]; then
log_info "RDS maintenance info is not."
log_info "処理終了"
exit 0
fi
fi
# 前回の出力と内容が変わっていなければ通知しない
if [ -f "${BEFORE_OUTPUT_AWSCLI_JSON}" ]; then
diff ${BEFORE_OUTPUT_AWSCLI_JSON} ${OUTPUT_AWSCLI_JSON}
if [ $? -eq 0 ] && "${NO_DIFF_NOT_NOTIFICATION}"; then
log_info "RDS maintenance info is not changed."
log_info "処理終了"
exit 0
fi
fi
# Slackに送信
if "${SEND_SLACK}"; then
token="${SLACK_INFO_TOKEN}"
now=`date "+%Y%m%d%H%M"`
filename="rds_maintenance_info-${now}.log"
filetype="text"
message="<!channel> 保留中のメンテナンスアクションがあります。"
title=`basename ${OUTPUT_AWSCLI_JSON}`
# 適用日が決まっていれば通知内容を変更
if grep "ForcedApplyDate" "${OUTPUT_AWSCLI_JSON}"; then
token="${SLACK_EMERGENCY_TOKEN}"
message="<!channel> 強制的に適用予定のメンテナンスアクションがあります。"
elif grep -e "AutoAppliedAfterDate" -e "CurrentApplyDate" "${OUTPUT_AWSCLI_JSON}"; then
token="${SLACK_NOTIFIER_TOKEN}"
message="<!channel> 次回メンテナンスウィンドウ時に自動で適用予定のメンテナンスアクションがあります。"
fi
slack_upload "${token}" "${OUTPUT_AWSCLI_JSON}" -c "${SLACK_CHANNEL_NAME}" -f "${filename}" -t "${filetype}" -i "${message}" -n "${title}"
fi
log_info "処理終了"
exit 0
cron設定
「AutoAppliedAfterDate」や「ForcedApplyDate」が設定されてから実際に適用されるまでの猶予についてAWSサポートへ問い合わせたところ、以下のような回答をいただきました。
「AutoAppliedAfterDate」や「ForcedApplyDate」は設定されてから実際に適用されるまでの期間については、特に一定猶予期間は定められておりません。メンテナンスをスケジュールする際に、メンテナンスの種類によって都度判断して設定する形にさせていただいております。
なお、RDS のメンテナンスは、パッチの影響範囲、深刻度に応じて、フォーラムまたはメールにて通知させて頂いております。大規模なメンテナンスが計画される場合の連絡時期につきましては、通常は二週間前を目安に実施しておりますが、パッチの緊急度によっては直前のご連絡になることもございます。
1日1回以上確認してもあまり意味が無さそうなので、毎朝9時に情報を取得するようにcronにシェルを仕込みました。
# RDS maintenance notification
0 9 * * * /usr/local/maintenance_notification/RDS_maintenance_notification.sh >> /usr/local/maintenance_notification/log/rds_maintenance_notice.log 2>&1
結果
丁度良いタイミングでメンテナンスが計画されたので、テストしてみたところ、以下画像のように通知できました!
※画像内のAlertとnotifierは通知テスト用にjsonを直接編集していますが、実際にはこのようなjsonが返却されます。
{
"PendingMaintenanceActions": [
{
"PendingMaintenanceActionDetails": [
{
"Action": "system-update",
"ForcedApplyDate": "2019-07-02T16:00:00Z",
"CurrentApplyDate": "2019-06-18T14:00:00Z",
"AutoAppliedAfterDate": "2019-06-18T14:00:00Z",
"Description": "Upgrade to Aurora PostgreSQL 1.3.2"
}
],
"ResourceIdentifier": "arn:aws:rds:ap-northeast-1:123456789123:cluster:hoge-cluster"
}
]
}
参考
- AWSCLI
describe-pending-maintenance-actions — AWS CLI 1.16.171 Command Reference
Amazon RDSのメンテナンスについて調べてみた - 本日も乙
【速報】Amazon RDS:メンテナンスへの柔軟な対応が可能に(pending-maintenance) | DevelopersIO
- Slack file upload
files.upload method | Slack
Slack APIでファイルをアップロードする方法 - UTALI