AWS CLIでVPC環境を構築する手順を作ったので公開。変数等は設定ファイルにまとめても良いと思ったけど、命名ルールが揺らいでいる最中に作ったので、敢えて毎度設定する手順とし、また、変数の流し込み方はbash前提。ウィザードから作成すると何が裏で作成されているのかが把握できないので、個別に1つ1つの項目を作成していった方が理解が深まると思います。

構築する環境

構築する環境は以下の通り。詳細は命名ルールから察してください。

項目 Nameタグ CIDRブロック Regin/Availability Zone/他
VPC名 VPC-MySystem 10.100.0.0/22 ap-northeast-1
Subnet名 SN-MySystem-Public-1 10.100.0.0/24 ap-northeast-1a
Subnet名 SN-MySystem-Private-1 10.100.1.0/24 ap-northeast-1a
Subnet名 SN-MySystem-Public-2 10.100.2.0/24 ap-northeast-1c
Subnet名 SN-MySystem-Private-2 10.100.3.0/24 ap-northeast-1c
オンプレNW オンプレNW 10.0.0.0/8 ※VPN接続想定

前提

権限のある作業用IAMアカウントとそのアクセスキー作成は割愛。Amazon Linux環境の場合は不要となるが、RHEL系Linux前提のAWS CLI環境のセットアップ手順も記載しておく。OSX環境はこちらも参考にしてください。

# pythonは2.6以降?
# python, python-setuptoolsを導入
sudo yum install python python-setuptools -y

# pip install
easy_install pip

# awscli install
pip install awscli

# version確認(ここのところupdateが続いていて本日時点は1.6.4が最新)
aws --version

# アクセスキー情報登録
aws configure
AWS Access Key ID [****************WZCA]:(アクセスキーID)
AWS Secret Access Key [****************u0qi]:(シークレットアクセスキー)
Default region name [ap-northeast-1]:(Tokyoリージョンを想定)
Default output format [json]:(以降の手順はjson出力がデフォルトの想定)

元々、credentialsはAWS SDK用、configはAWS CLI用だったと思うのだけど、AWS CLIの最近のバージョンはアクセスキー、シークレットアクセスキーをcredentialsに、リージョンとアウトプットをconfigに保存するようになった模様。

$ cat ~/.aws/config
[default]
output = json
region = ap-northeast-1

$ cat ~/.aws/credentials
[default]
aws_access_key_id = ****************WZCA
aws_secret_access_key = ****************u0qi

ちなみに認証情報は下記順序で参照されることに注意。実は環境変数が最強なので、aws configureした情報をOverrideすることもできる。

  1. Environment Variables(環境変数)
  2. ~/.aws/credentials,C:\Users\USERNAME\.aws\credentials
  3. ~/.aws/config,C:\Users\USERNAME\.aws\config
  4. Instance profile credentials(インスタンスに割り当てたIAM Role)

利用できる環境変数や指定可能な共通オプションはこちらを参考にすると良い。

Variable Option Config Entry Environment Variable Description
profile –profile profile AWS_DEFAULT_PROFILE Default profile name
region –region region AWS_DEFAULT_REGION Default AWS Region
config_file AWS_CONFIG_FILE Alternate location of config
output –output output AWS_DEFAULT_OUTPUT Default output style
access_key aws_access_key_id AWS_ACCESS_KEY_ID AWS Access Key
secret_key aws_secret_access_key AWS_SECRET_ACCESS_KEY AWS Secret Key
token aws_security_token AWS_SECURITY_TOKEN AWS Token (temp credentials)

方針

jqは使用せず、AWS CLIで利用できるオプションをできる限り利用した。作成したものには全てNameタグを付与する方針としたので、悩んだ挙句awkコマンドを利用することにした。

  • jqは使わない(できる限りAWS CLIで)
  • createしたらすぐにNameタグをすぐにつける
  • awkコマンドを使うことは許容

各オブジェクトの作成フローは以下の通り。

  1. 名前等を変数で指定(exportは念の為。bash環境想定。)
  2. createと同時にID取得
  3. 作成したオブジェクトに応じて必要な初期設定
  4. describe系コマンドで情報出力
  5. 再作成を考慮して削除手順を「###」付きで記載

1. VPC作成

# 変数定義
export CIDR=10.100.0.0/22
export VPCNAME=VPC-MySystem

# VPC作成
export VPCID=$(aws ec2 create-vpc --cidr-block $CIDR | awk '/VpcId/ {gsub(/\"/, "");gsub(/,/,""); print $2}')

# Nameタグ設定
aws ec2 create-tags --resources $VPCID --tags Key=Name,Value=$VPCNAME

# VPC Attribute設定 ※DNS Hostnames設定を忘れがちなので注意 
aws ec2 modify-vpc-attribute --vpc-id $VPCID --enable-dns-support
aws ec2 modify-vpc-attribute --vpc-id $VPCID --enable-dns-hostnames

# describeで確認
aws ec2 describe-vpcs --vpc-ids $VPCID
aws ec2 describe-vpc-attribute --vpc-id $VPCID --attribute enableDnsSupport
aws ec2 describe-vpc-attribute --vpc-id $VPCID --attribute enableDnsHostnames

# 削除手順
###export VPCID=$(aws ec2 describe-vpcs --query Vpcs[].VpcId[] --filters "Name=tag-key,Values=Name" "Name=tag-value,Values=$VPCNAME" --output text)
###aws ec2 delete-vpc --vpc-id $VPCID

2. Internet Gateway作成

# 変数定義
export IGWNAME=IGW-MySystem

# Internet Gateway作成
export IGWID=$(aws ec2 create-internet-gateway | awk '/InternetGatewayId/ {gsub(/\"/, "");gsub(/,/,""); print $2}')

# Nameタグ設定
aws ec2 create-tags --resources $IGWID --tags Key=Name,Value=$IGWNAME

# VPCに割り当て
aws ec2 attach-internet-gateway --internet-gateway-id $IGWID --vpc-id $VPCID

# describeで確認
aws ec2 describe-internet-gateways --filters "Name=internet-gateway-id,Values=$IGWID"

# 削除手順
###export IGWID=$(aws ec2 describe-internet-gateways --query InternetGateways[].InternetGatewayId[] --filters "Name=tag-key,Values=Name" "Name=tag-value,Values=$IGWNAME" --output text)
###aws ec2 detach-internet-gateway --internet-gateway-id $IGWID --vpc-id $VPCID
###aws ec2 delete-internet-gateway --internet-gateway-id $IGWID

3. Virtual Private Gateway作成

# 変数定義
export VPGWNAME=VPGW-MySystem

# Virtual Private Gateway作成
export VPGWID=$(aws ec2 create-vpn-gateway --type ipsec.1 | awk '/VpnGatewayId/ {gsub(/\"/, "");gsub(/,/,""); print $2}')

# Nameタグ設定
aws ec2 create-tags --resources $VPGWID --tags Key=Name,Value=$VPGWNAME

# VPCに割り当て
aws ec2 attach-vpn-gateway --vpn-gateway-id $VPGWID --vpc-id $VPCID

# describeで確認
aws ec2 describe-vpn-gateways --filters "Name=vpn-gateway-id,Values=$VPGWID"

# 削除手順
###export VPGWID=$(aws ec2 describe-vpn-gateways --query VpnGateways[].VpnGatewayId[] --filters "Name=tag-key,Values=Name" "Name=tag-value,Values=$VPGWNAME" --output text)
###aws ec2 detach-vpn-gateway --vpn-gateway-id $VPGWID --vpc-id $VPCID
###aws ec2 delete-vpn-gateway --vpn-gateway-id $VPGWID

4. DHCPオプションにNameタグだけ設定(VPCデフォルトのまま利用)

# 変数定義
export DHCPOPSNAME=DHCP-MySystem
export DHCPOPSID=$(aws ec2 describe-vpcs --vpc-ids $VPCID --query Vpcs[].DhcpOptionsId[] --output text)

# Nameタグだけ設定
aws ec2 create-tags --resources $DHCPOPSID --tags Key=Name,Value=$DHCPOPSNAME

# describeで確認
aws ec2 describe-dhcp-options --dhcp-options-ids $DHCPOPSID

5. Subnet作成

# 変数定義
export SUB1CIDR=10.100.0.0/24
export SUB1AZ=ap-northeast-1a
export SUB1NAME=SN-MySystem-Public-1

# Subnet作成
export SUB1ID=$(aws ec2 create-subnet --vpc-id $VPCID --cidr-block $SUB1CIDR --availability-zone $SUB1AZ | awk '/SubnetId/ {gsub(/\"/, "");gsub(/,/,""); print $2}')

# Nameタグ設定
aws ec2 create-tags --resources $SUB1ID --tags Key=Name,Value=$SUB1NAME

# PublicなのでAuto Assign Public IPする
aws ec2 modify-subnet-attribute --subnet-id $SUB1ID --map-public-ip-on-launch

# describeで確認
aws ec2 describe-subnets --subnet-ids $SUB1ID

# 削除手順
###export SUB1ID=$(aws ec2 describe-subnets --query Subnets[].SubnetId[] --filters "Name=tag-key,Values=Name" "Name=tag-value,Values=$SUB1NAME" --output text)
###aws ec2 delete-subnet --subnet-id $SUB1ID
# 変数定義
export SUB2CIDR=10.100.1.0/24
export SUB2AZ=ap-northeast-1a
export SUB2NAME=SN-MySystem-Private-1

# Subnet作成
export SUB2ID=$(aws ec2 create-subnet --vpc-id $VPCID --cidr-block $SUB2CIDR --availability-zone $SUB2AZ | awk '/SubnetId/ {gsub(/\"/, "");gsub(/,/,""); print $2}')

# Nameタグ設定
aws ec2 create-tags --resources $SUB2ID --tags Key=Name,Value=$SUB2NAME

# PrivateなのでAuto Assign Public IPしない
aws ec2 modify-subnet-attribute --subnet-id $SUB2ID --no-map-public-ip-on-launch

# describeで確認
aws ec2 describe-subnets --subnet-ids $SUB2ID

# 削除手順
###export SUB2ID=$(aws ec2 describe-subnets --query Subnets[].SubnetId[] --filters "Name=tag-key,Values=Name" "Name=tag-value,Values=$SUB2NAME" --output text)
###aws ec2 delete-subnet --subnet-id $SUB2ID
# 変数定義
export SUB3CIDR=10.100.2.0/24
export SUB3AZ=ap-northeast-1c
export SUB3NAME=SN-MySystem-Public-2

# Subnet作成
export SUB3ID=$(aws ec2 create-subnet --vpc-id $VPCID --cidr-block $SUB3CIDR --availability-zone $SUB3AZ | awk '/SubnetId/ {gsub(/\"/, "");gsub(/,/,""); print $2}')

# Nameタグ設定
aws ec2 create-tags --resources $SUB3ID --tags Key=Name,Value=$SUB3NAME

# PublicなのでAuto Assign Public IPする
aws ec2 modify-subnet-attribute --subnet-id $SUB3ID --map-public-ip-on-launch

# describeで確認
aws ec2 describe-subnets --subnet-ids $SUB3ID

# 削除手順
###export SUB3ID=$(aws ec2 describe-subnets --query Subnets[].SubnetId[] --filters "Name=tag-key,Values=Name" "Name=tag-value,Values=$SUB3NAME" --output text)
###aws ec2 delete-subnet --subnet-id $SUB3ID
# 変数定義
export SUB4CIDR=10.100.3.0/24
export SUB4AZ=ap-northeast-1c
export SUB4NAME=SN-MySystem-Private-2

# Subnet作成
export SUB4ID=$(aws ec2 create-subnet --vpc-id $VPCID --cidr-block $SUB4CIDR --availability-zone $SUB4AZ | awk '/SubnetId/ {gsub(/\"/, "");gsub(/,/,""); print $2}')

# Nameタグ設定
aws ec2 create-tags --resources $SUB4ID --tags Key=Name,Value=$SUB4NAME

# PrivateなのでAuto Assign Public IPしない
aws ec2 modify-subnet-attribute --subnet-id $SUB4ID --no-map-public-ip-on-launch

# describeで確認
aws ec2 describe-subnets --subnet-ids $SUB4ID

# 削除手順
###export SUB4ID=$(aws ec2 describe-subnets --query Subnets[].SubnetId[] --filters "Name=tag-key,Values=Name" "Name=tag-value,Values=$SUB4NAME" --output text)
###aws ec2 delete-subnet --subnet-id $SUB4ID

6. Route Table作成

NATインスタンスがまだ無いので、PrivateサブネットはVPGWへのルートのみ追加する手順とした。あと、Public用のRoute Tableは共通だけど、NATインスタンスはAZごとに追加する想定なので、それぞれ別のRoute Tableとした。

# 変数定義
# サブネットはNameタグで拾ってくる
export RT1NAME=RT-MySystem-Public
export PUBSUBLIST=$(aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPCID" "Name=tag-key,Values=Name" "Name=tag-value,Values=*Public*" --query 'Subnets[].SubnetId[]' --output text)

# Route Table作成
export RT1ID=$(aws ec2 create-route-table --vpc-id $VPCID | awk '/RouteTableId/ {gsub(/\"/, "");gsub(/,/,""); print $2}')

# Nameタグ設定
aws ec2 create-tags --resources $RT1ID --tags Key=Name,Value=$RT1NAME

# ルート追加
# Nameタグから拾ってきたサブネットに割り当て
aws ec2 create-route --route-table-id $RT1ID --destination-cidr-block 0.0.0.0/0 --gateway-id $IGWID
for PUBSUB in $PUBSUBLIST
do
  aws ec2 associate-route-table --route-table-id $RT1ID --subnet-id $PUBSUB
done

# describeで確認
aws ec2 describe-route-tables --route-table-ids $RT1ID

# 削除手順
###export RT1ID=$(aws ec2 describe-route-tables --query RouteTables[].RouteTableId[] --filters "Name=tag-key,Values=Name" "Name=tag-value,Values=$RT1NAME" --output text)
###ASSOCIDS=$(aws ec2 describe-route-tables --filters "Name=vpc-id,Values=$VPCID" "Name=route-table-id,Values=$RT1ID" --query 'RouteTables[].Associations[].RouteTableAssociationId[]' --output text)
###for ASSOCID in $ASSOCIDS
###do
###  aws ec2 disassociate-route-table --association-id $ASSOCID
###done
###aws ec2 delete-route-table --route-table-id $RT1ID
# 変数定義
# サブネットはNameタグで拾ってくる
export RT2NAME=RT-MySystem-Private1
export PRIVSUBLIST1=$(aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPCID" "Name=tag-key,Values=Name" "Name=tag-value,Values=*Private-1*" --query 'Subnets[].SubnetId[]' --output text)

# Route Table作成
export RT2ID=$(aws ec2 create-route-table --vpc-id $VPCID | awk '/RouteTableId/ {gsub(/\"/, "");gsub(/,/,""); print $2}')

# Nameタグ設定
aws ec2 create-tags --resources $RT2ID --tags Key=Name,Value=$RT2NAME

# ルート追加(NATインスタンスを追加した後にさらにルート追加される想定)
# Nameタグから拾ってきたサブネットに割り当て
aws ec2 create-route --route-table-id $RT2ID --destination-cidr-block 10.0.0.0/8 --gateway-id $VPGWID
for PRIVSUB1 in $PRIVSUBLIST1
do
  aws ec2 associate-route-table --route-table-id $RT2ID --subnet-id $PRIVSUB1
done

# describeで確認
aws ec2 describe-route-tables --route-table-ids $RT2ID

# 削除手順
###export RT2ID=$(aws ec2 describe-route-tables --query RouteTables[].RouteTableId[] --filters "Name=tag-key,Values=Name" "Name=tag-value,Values=$RT2NAME" --output text)
###ASSOCIDS=$(aws ec2 describe-route-tables --filters "Name=vpc-id,Values=$VPCID" "Name=route-table-id,Values=$RT2ID" --query 'RouteTables[].Associations[].RouteTableAssociationId[]' --output text)
###for ASSOCID in $ASSOCIDS
###do
###  aws ec2 disassociate-route-table --association-id $ASSOCID
###done
###aws ec2 delete-route-table --route-table-id $RT2ID
# 変数定義
# サブネットはNameタグで拾ってくる
export RT3NAME=RT-MySystem-Private2
export PRIVSUBLIST2=$(aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPCID" "Name=tag-key,Values=Name" "Name=tag-value,Values=*Private-2*" --query 'Subnets[].SubnetId[]' --output text)

# Route Table作成
export RT3ID=$(aws ec2 create-route-table --vpc-id $VPCID | awk '/RouteTableId/ {gsub(/\"/, "");gsub(/,/,""); print $2}')

# ルート追加(NATインスタンスを追加した後にさらにルート追加される想定)
# Nameタグから拾ってきたサブネットに割り当て
aws ec2 create-tags --resources $RT3ID --tags Key=Name,Value=$RT3NAME
aws ec2 create-route --route-table-id $RT3ID --destination-cidr-block 10.0.0.0/8 --gateway-id $VPGWID
for PRIVSUB2 in $PRIVSUBLIST2
do 
  aws ec2 associate-route-table --route-table-id $RT3ID --subnet-id $PRIVSUB2 done

# describeで確認
aws ec2 describe-route-tables --route-table-ids $RT3ID

# 削除手順
###export RT3ID=$(aws ec2 describe-route-tables --query RouteTables[].RouteTableId[] --filters "Name=tag-key,Values=Name" "Name=tag-value,Values=$RT3NAME" --output text)
###ASSOCIDS=$(aws ec2 describe-route-tables --filters "Name=vpc-id,Values=$VPCID" "Name=route-table-id,Values=$RT3ID" --query 'RouteTables[].Associations[].RouteTableAssociationId[]' --output text)
###for ASSOCID in $ASSOCIDS
###do
###  aws ec2 disassociate-route-table --association-id $ASSOCID
###done
###aws ec2 delete-route-table --route-table-id $RT3ID

ここまで作るならCloudFormationかもと思ったけど、変更管理しないものをCloudFormationで作成するのは適切ではないとの判断で、AWS CLIでの手順としてみた(本当はCloudFormationがまだ使いこなせるほどわかっていないという噂も)。これ以外にも構築手順は全てCLIで作成中。これにNATも追加すれば良いけど、可用性の要件が環境により異なると思うので。

需要があるかわからないけど、参考にしてくれる人が一人でもいたらとても嬉しい。

TOP