AWS Advent Calender 2012 の15日目担当の @satotech です。
最近は、 AWS CloudFormation と Amazon Virtual Private Cloud(Amazon VPC) の話題が多いですね。
CloudFormationは、ホントに便利ですよね!
今回は、シェルスクリプトでVPCの構築をしてみたいと思います。
次のようなイメージのVPCを構築します。
複数のAZにそれぞれ複数のサブネットを持つVPCを作成します。
1つ目はPublicサブネットで、Elastic IP を取得、セキュリティグループを作成してEC2 インスタンスを起動します。
2つ目はPrivateサブネットで、バックエンドのEC2 インスタンスやRDS用になります。
手順
- 10.0.0.0/16 で VPC を作成
- Zone A に PublicサブネットA 10.0.10.0/24 を作成
- Zone B に PublicサブネットB 10.0.11.0/24 を作成
- インターネットゲートウェイを作成
- Publicサブネットの Route Table を作成
- Publicサブネットの Route Table にデフォルトゲートウェイとしてインターネットゲートウェイを設定
- PublicサブネットAに Route Table を関連づけ
- PublicサブネットBに Route Table を関連づけ
- Publicサブネットに NetworkACL(NACL) を定義
- Publicサブネットに対してNACLを関連づけ
- Zone A にPrivateサブネットA 10.0.20.0/24 を作成
- Zone B にPrivateサブネットB 10.0.21.0/24 を作成
- Privateサブネットに NACL を定義
- Publicサブネット用にセキュリティグループを定義
- Inbound用にHTTPポートを許可
- Inbound用にSSHポートを許可
- インスタンスを起動
- タグを設定
- EIPを取得
- インスタンスにEIPを関連づけ
シェルスクリプト
main.sh
#!/bin/bash source ~/aws/export.cfg source ~/aws/functions.sh REGION="ap-northeast-1" ZONE_A="${REGION}a" ZONE_B="${REGION}b" OPTS="--region ${REGION} -H --show-empty-fields" SSH_PORT="22" GROUP_NAME="PublicSubnetGroup" DESCRIPTION="Enable SSH access via port ${SSH_PORT}" AMI_ID="ami-dcfa4edd" SIZE="10" DELETE_VOLUME="true" KEYPAIR="vpcsample" TYPE="t1.micro" AKI_ID="aki-ec5df7ed" RUN_OPTS="${AMI_ID} -b /dev/sda1=:${SIZE}:${DELETE_VOLUME} -b /dev/sdc=ephemeral0 -k ${KEYPAIR} -t ${TYPE} --instance-initiated-shutdown-behavior stop --kernel ${AKI_ID}" VPC_ID=`create-vpc 10.0.0.0/16 "${OPTS}"` PUBLIC_SUBNET_ID_A=`create-subnet ${VPC_ID} 10.0.10.0/24 ${ZONE_A} "${OPTS}"` PUBLIC_SUBNET_ID_B=`create-subnet ${VPC_ID} 10.0.11.0/24 ${ZONE_B} "${OPTS}"` IGW_ID=`create-internet-gateway ${VPC_ID} "${OPTS}"` PUBLIC_ROUTE_TABLE_ID=`create-route-table ${VPC_ID} "${OPTS}"` ec2-create-route ${PUBLIC_ROUTE_TABLE_ID} -r 0.0.0.0/0 -g ${IGW_ID} ${OPTS} ec2-associate-route-table ${PUBLIC_ROUTE_TABLE_ID} -s ${PUBLIC_SUBNET_ID_A} ${OPTS} ec2-associate-route-table ${PUBLIC_ROUTE_TABLE_ID} -s ${PUBLIC_SUBNET_ID_B} ${OPTS} PUBLIC_ACL_ID=`create-network-acl ${VPC_ID} "${OPTS}"` ec2-create-network-acl-entry ${PUBLIC_ACL_ID} -n 100 -P 6 -r 0.0.0.0/0 -p 80 --allow ${OPTS} ec2-create-network-acl-entry ${PUBLIC_ACL_ID} -n 101 -P 6 -r 0.0.0.0/0 -p ${SSH_PORT} --allow ${OPTS} ec2-create-network-acl-entry ${PUBLIC_ACL_ID} -n 102 -P 6 -r 0.0.0.0/0 -p 1024-65535 --allow ${OPTS} ec2-create-network-acl-entry ${PUBLIC_ACL_ID} -n 100 --egress -P 6 -r 0.0.0.0/0 -p 80 --allow ${OPTS} ec2-create-network-acl-entry ${PUBLIC_ACL_ID} -n 101 --egress -P 6 -r 0.0.0.0/0 -p 443 --allow ${OPTS} ec2-create-network-acl-entry ${PUBLIC_ACL_ID} -n 102 --egress -P 6 -r 0.0.0.0/0 -p 1024-65535 --allow ${OPTS} replace-network-acl-association ${PUBLIC_SUBNET_ID_A} ${PUBLIC_ACL_ID} "${OPTS}" replace-network-acl-association ${PUBLIC_SUBNET_ID_B} ${PUBLIC_ACL_ID} "${OPTS}" PRIVATE_SUBNET_ID_A=`create-subnet ${VPC_ID} 10.0.20.0/24 ${ZONE_A} "${OPTS}"` PRIVATE_SUBNET_ID_B=`create-subnet ${VPC_ID} 10.0.21.0/24 ${ZONE_B} "${OPTS}"` PRIVATE_ROUTE_TABLE_ID=`create-route-table ${VPC_ID} "${OPTS}"` ec2-associate-route-table ${PRIVATE_ROUTE_TABLE_ID} -s ${PRIVATE_SUBNET_ID_A} ${OPTS} ec2-associate-route-table ${PRIVATE_ROUTE_TABLE_ID} -s ${PRIVATE_SUBNET_ID_B} ${OPTS} PRIVATE_ACL_ID=`create-network-acl ${VPC_ID} "${OPTS}"` ec2-create-network-acl-entry ${PRIVATE_ACL_ID} -n 100 -P all -r 10.0.0.0/16 --allow ${OPTS} ec2-create-network-acl-entry ${PRIVATE_ACL_ID} -n 100 --egress -P all -r 10.0.0.0/16 --allow ${OPTS} replace-network-acl-association ${PRIVATE_SUBNET_ID_A} ${PRIVATE_ACL_ID} "${OPTS}" replace-network-acl-association ${PRIVATE_SUBNET_ID_B} ${PRIVATE_ACL_ID} "${OPTS}" PUBLIC_GROUP_ID=`create-group ${GROUP_NAME} "${DESCRIPTION}" ${VPC_ID} "${OPTS}"` ec2-authorize ${PUBLIC_GROUP_ID} -P 6 -p 80 -s 0.0.0.0/0 ${OPTS} ec2-authorize ${PUBLIC_GROUP_ID} -P 6 -p ${SSH_PORT} -s 0.0.0.0/0 ${OPTS} INSTANCE_ID_A=`run-instances "${RUN_OPTS}" ${PUBLIC_GROUP_ID} ${ZONE_A} ${PUBLIC_SUBNET_ID_A} 10.0.10.5 host00 "${OPTS}"` INSTANCE_ID_B=`run-instances "${RUN_OPTS}" ${PUBLIC_GROUP_ID} ${ZONE_B} ${PUBLIC_SUBNET_ID_B} 10.0.11.5 host01 "${OPTS}"` ALLOCATION_ID_A=`associate-address ${INSTANCE_ID_A} "${OPTS}"` ALLOCATION_ID_B=`associate-address ${INSTANCE_ID_B} "${OPTS}"`
functions.sh
#!/bin/bash function create-vpc() { local VPC_CIDR=$1 local OPTS=$2 local VPC_ID="" while [[ -z ${VPC_ID} ]]; do VPC_ID=$(ec2-create-vpc ${VPC_CIDR} ${OPTS} | grep VPC | awk '{print $2}') done while [[ -z "$(ec2-describe-vpcs ${VPC_ID} --filter \"state=available\" ${OPTS})" ]]; do sleep 10 done echo "${VPC_ID}" } function create-subnet() { local VPC_ID=$1 local CIDR=$2 local ZONE=$3 local OPTS=$4 local SUBNET_ID="" while [[ -z ${SUBNET_ID} ]]; do SUBNET_ID=$(ec2-create-subnet -c ${VPC_ID} -i ${CIDR} -z ${ZONE} ${OPTS} | grep SUBNET | awk '{print $2}') done while [[ -z "$(ec2-describe-subnets ${SUBNET_ID} --filter \"state=available\" ${OPTS})" ]]; do sleep 10 done echo "${SUBNET_ID}" } function create-internet-gateway() { local VPC_ID=$1 local OPTS=$2 local IGW_ID="" while [[ -z ${IGW_ID} ]]; do IGW_ID=$(ec2-create-internet-gateway ${OPTS} | grep ^INTERNETGATEWAY | awk '{print $2}') done ec2-attach-internet-gateway ${IGW_ID} -c ${VPC_ID} ${OPTS} > /dev/null 2>&1 while [[ -z "$(ec2-describe-internet-gateways ${IGW_ID} --filter \"attachment.vpc-id=${VPC_ID}\" --filter \"attachment.state=available\" ${OPTS})" ]]; do sleep 10 done echo "${IGW_ID}" } function create-route-table() { local VPC_ID=$1 local OPTS=$2 local ROUTE_TABLE_ID="" while [[ -z ${ROUTE_TABLE_ID} ]]; do ROUTE_TABLE_ID=$(ec2-create-route-table ${VPC_ID} ${OPTS} | grep ROUTETABLE | awk '{print $2}') done echo "${ROUTE_TABLE_ID}" } function create-network-acl() { local VPC_ID=$1 local OPTS=$2 local ACL_ID="" while [[ -z ${ACL_ID} ]]; do ACL_ID=$(ec2-create-network-acl ${VPC_ID} ${OPTS} | grep NETWORKACL | awk '{print $2}') done echo "${ACL_ID}" } function replace-network-acl-association() { local SUBNET_ID=$1 local ACL_ID=$2 local OPTS=$3 local ASSOCIATION="" while [[ -z ${ASSOCIATION} ]]; do ASSOCIATION=$(ec2-describe-network-acls --filter \"association.subnet-id=${SUBNET_ID}\" ${OPTS} | grep ASSOCIATION | grep ${SUBNET_ID} | awk '{print $2}') done ec2-replace-network-acl-association ${ASSOCIATION} -a ${ACL_ID} ${OPTS} > /dev/null 2>&1 echo "${ASSOCIATION}" } function create-group() { local GROUP_NAME=$1 local DESCRIPTION=$2 local VPC_ID=$3 local OPTS=$4 local GROUP_ID="" while [[ -z ${GROUP_ID} ]]; do GROUP_ID=$(ec2-create-group ${GROUP_NAME} -d "${DESCRIPTION}" -c ${VPC_ID} ${OPTS} | grep GROUP | awk '{print $2}') done echo "${GROUP_ID}" } function run-instances() { local RUN_OPTS=$1 local GROUP_ID=$2 local ZONE=$3 local SUBNET_ID=$4 local PRIVATE_IP=$5 local SERVER_NAME=$6 local OPTS=$7 local INSTANCE_ID="" while [[ -z ${INSTANCE_ID} ]]; do INSTANCE_ID=$(ec2-run-instances ${RUN_OPTS} -g ${GROUP_ID} -z ${ZONE} -s ${SUBNET_ID} --private-ip-address ${PRIVATE_IP} ${OPTS} | grep INSTANCE | awk '{print $2}') done ec2-create-tags ${INSTANCE_ID} --tag Name=${SERVER_NAME} ${OPTS} > /dev/null 2>&1 while [[ -z "$(ec2-describe-instance-status ${INSTANCE_ID} ${OPTS} | grep INSTANCESTATUS | grep passed)" ]]; do sleep 10 done echo "${INSTANCE_ID}" } function associate-address() { local INSTANCE_ID=$1 local OPTS=$2 local ALLOCATION_ID="" ALLOCATION_ID=$(ec2-allocate-address -d vpc ${OPTS} | grep ADDRESS | awk '{print $5}') ec2-associate-address -a ${ALLOCATION_ID} -i ${INSTANCE_ID} ${OPTS} > /dev/null 2>&1 echo "${ALLOCATION_ID}" }
main部分は、手順とほぼ同じステップで記述できました。
では、スクリプトを実行していきます。
Macbook Air のファンの音が大きくなってきましたw
Management Console で各項目を見ながら進行状況を確認します...
...
3分40秒で終了しました。
確認してみましょう。
無事、EIPとの関連づけまでできていることを確認できました。
確認も終わったので削除します。
感想
今回、 ELB や RDS の配置まで書こうと思っていたのですが、間に合いませんでした。。
やっぱり CloudFormation は便利ですね!!
あとがき
先日ラスベガスで行なわれた AWS re:Invent ツアーに参加させていただきました。
Keynote や 各Session( CDP 盛り上がりましたね!)はもちろん、 Technical Bootcamp や Code Challenge 、シアトルのAWS本社訪問など刺激を受ける場面ばかりでした。
ご一緒させていただいた皆様、ありがとうございました。
来年もラスベガスでの開催が決まっているそうです。
気になる方はぜひご参加下さい!
ありがとうございました!