何言ってるかよくわからない?俺もなんでこんなことしてるのかようわからん。。
趣味程度に使うAWSなのでそんなにがっつり使ってるわけではないんだけど、windows(cygwin)環境下で使えるようにしたのでメモ ※python(pip)使ったほうがいいよ?→正解
環境
Windows10(64bit)
cygwin
※jsonの整形に jq と jo を使うので、cygwinから使えるようにパス通しておく
手順
AWS CLI for Windowsを入手
https://aws.amazon.com/jp/cli/
32bitか64bit好きなほう持ってくる
Windows上でてきとうなところへ展開しインストール。どこでもいい。
※cygwinから辿れるパスで
cygwinから使えるようにする
展開したディレクトリに aws.exe があるので、こいつをcygwin上で実行できるようにする。
自分は単純に展開したディレクトリを.bashrcに追記してパス通した。
ついでにaliasに書き足して aws-cli で実行できるように。
※あとで気付いたけどこのaliasいらなくてそのままパス通して aws で実行するだけでよかった
# for aws-cli
AWSCLI_HOME=[aws-cliを展開したディレクトリ、cygwinが辿れるように書く]
export PATH=$PATH:$AWSCLI_HOME
alias aws-cli='aws.exe'
これでcygwinからaws-cliを実行できるようになった。
configure忘れずに。
$ aws-cli --version
aws-cli/1.11.175 Python/2.7.9 Windows/8 botocore/1.7.33
やりたいこと
EC2インスタンスに設定しているセキュリティグループについて、自宅のIPが固定じゃないのでIPが変わったらスクリプト一発でセキュリティグループをアップデートしたい。以下の流れで実行する。
・新しいIPを取得する
インターネットに公開されてる自分のGIPを教えてくれるサービスにcurlで接続しレスポンスボディから抜き出す
取得したIPの末尾に /32 を補完してCIDR表記をゲット(具体的なやりかたは省略)
・セキュリティグループをアップデートするためのjsonを作る
aws-cli ec2 authorize-security-group-ingress
が使いたいコマンド。リファレンス → http://docs.aws.amazon.com/cli/latest/reference/ec2/authorize-security-group-ingress.html
使い方のイメージはリファレンスにある
$ aws ec2 authorize-security-group-ingress --group-id sg-123abc12 --ip-permissions '[{"IpProtocol": "tcp", "FromPort": 3389, "ToPort": 3389, "IpRanges": [{"CidrIp": "203.0.113.0/24", "Description": "RDP access from NY office"}]}]'
これなんだけど、ちょっとごちゃごちゃやってみたかぎり、投げるjsonをシェル変数に埋めてそのままPOSTするのムリっぽい(シングルクォートとかのエスケープを頑張ればいける・・?)ので、おとなしくjsonをファイルに生成して、それを --cli-input-json オプションに渡すのがよさそう。
※どういうjsonを投げたのか?をファイルとして残しておけるしね!
というわけで、シェルでjsonを生成する。
joコマンドを使う。 https://qiita.com/yangci/items/4a7602df4faae5773532 を参照。
生成する必要があるjsonフォーマットは以下の内容。
{
"GroupId": "xx-xxxxxxx",
"IpPermissions": [
{
"IpProtocol": "-1", #「全てのプロトコル」は -1 で指定
"IpRanges": [
{
"CidrIp": "203.0.113.0/24", #取得した自分のIP ※このIPはリファレンスに書いてあったもの
"Description": "aws-cli(test)" #説明文
}
]
}
]
}
以下のjoコマンドで生成する
TARGET_INFO=$(jo -p GroupId="${GROUP_ID}" IpPermissions[]=$(jo IpProtocol="${IP_PROTOCOL}" IpRanges[]=$(jo CidrIp="${MYIP}" Description="MyHomeIP($TODAY)")))
このjoだと、${IP_PROTOCOL}に"-1"をいれてもどうもjoに勝手に -1 に変えられてしまう(ダブルクォートが亡き者にされる)ので、sedを一個かまして無理やりダブルクォートを足す。
TARGET_INFO=$(jo -p GroupId="${GROUP_ID}" IpPermissions[]=$(jo IpProtocol="${IP_PROTOCOL}" IpRanges[]=$(jo CidrIp="${MYIP}" Description="MyHomeIP($TODAY)")))
echo ${TARGET_INFO} | jq '.' > ${TARGET_INFO_JSON}
TMP=$(cat ${TARGET_INFO_JSON} | sed -e "s/-1/\"-1\"/g")
echo -e "${TMP}" >${TARGET_INFO_JSON}
最初jqはいらない予定だったけど、ここでjsonの整形が必要になったので最初のjoの出力時点で再度jqで整形しておく。
joの -p オプションがこれやってくれる気がしたけど、うまく動かなかったのでそのままjqにパイプで渡してる
参考までに、--generate-cli-skeletonの出力は以下
$ aws ec2 authorize-security-group-ingress --generate-cli-skeleton
{
"CidrIp": "",
"FromPort": 0,
"GroupId": "",
"GroupName": "",
"IpPermissions": [
{
"FromPort": 0,
"IpProtocol": "",
"IpRanges": [
{
"CidrIp": "",
"Description": ""
}
],
"Ipv6Ranges": [
{
"CidrIpv6": "",
"Description": ""
}
],
"PrefixListIds": [
{
"Description": "",
"PrefixListId": ""
}
],
"ToPort": 0,
"UserIdGroupPairs": [
{
"Description": "",
"GroupId": "",
"GroupName": "",
"PeeringStatus": "",
"UserId": "",
"VpcId": "",
"VpcPeeringConnectionId": ""
}
]
}
],
"IpProtocol": "",
"SourceSecurityGroupName": "",
"SourceSecurityGroupOwnerId": "",
"ToPort": 0,
"DryRun": true
}
最終的にcygwinで実行するシェルスクリプト全体としてはこんな感じ。
# !/bin/bash
PATH=[いい感じにパス通す]
export PATH
### SCRIPT ENV ###
retval=0
SCRIPT_DIR=[スクリプトを設置してるディレクトリ]
LOG_DIR=${SCRIPT_DIR}/log
JSON_DIR=${SCRIPT_DIR}/json
TODAY=$(date '+%Y%m%d')
TIME=$(date '+%H%M%S')
MYIP="$(getMyIP.sh)/32"
TARGET_INFO_JSON=${TODAY}_${TIME}.json
cd ${SCRIPT_DIR}
### AWS ENV ###
retval=0
GROUP_ID="xx-xxxxxxxx"
IP_PROTOCOL="-1"
TARGET_INFO=$(jo -p GroupId="${GROUP_ID}" IpPermissions[]=$(jo IpProtocol="${IP_PROTOCOL}" IpRanges[]=$(jo CidrIp="${MYIP}" Description="MyHomeIP($TODAY)")))
echo ${TARGET_INFO} | jq '.' > ${TARGET_INFO_JSON}
TMP=$(cat ${TARGET_INFO_JSON} | sed -e "s/-1/\"-1\"/g")
echo -e "${TMP}" >${TARGET_INFO_JSON}
# cat ${TARGET_INFO_JSON}
### main ###
RESULT=$(aws ec2 authorize-security-group-ingress --cli-input-json file://${TARGET_INFO_JSON} 2>&1 )
retval=$?
# retvalが0以外だったら、ログディレクトリに出しておく
if [ ! $retval -eq 0 ] ; then
echo $0 > ${LOG_DIR}/${TODAY}_${TIME}.log
echo $RESULT >> ${LOG_DIR}/${TODAY}_${TIME}.log
echo "error , watch log -> ${LOG_DIR}/${TODAY}_${TIME}.log" 1>&2
fi
# 生成したjsonを退避
mv ${TARGET_INFO_JSON} ${JSON_DIR}/
exit $retval
実行
・成功
$ ./update_securitygroup_myip.sh
※特に出力なし。json/配下に、--input-cli-jsonで渡したjsonが残ってる。
・失敗(わざと2回目やると、重複するルールを登録することになるので失敗する)
$ ./update_securitygroup_myip.sh
error , watch log -> [ログディレクトリ]/20171022_192416.log
ログを確認する
$ cat [ログディレクトリ]/20171022_192416.log
./update_securitygroup_myip.sh
An error occurred (InvalidPermission.Duplicate) when calling the AuthorizeSecurityGroupIngress operation: the specified rule "peer: xxx.xxx.xxx.xxx/32, ALL, ALLOW" already exists
ちゃんと、「このIPのルール既にあるよ」のエラーを返している。ちなみに終了ステータスは255.
TO DO
・不要になったIPをセキュリティグループから削除する ※上のスクリプトの中でいっしょにやるべき