踏み台サーバー用のVPCを作成する

# はじめに

ここでは踏み台サーバー用のVPCを作成してみます。

以下のように、他の VPC に接続するために踏み台サーバーを配置するために使用します。

VPC Peering や Transit Gateway を使用して他の VPCに接続する事を想定しています。(VPC Peering や Transit Gateway接続の手順はここでは扱いません)

![Basic VPC 構成 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99425/5680bed4-6a47-187d-0c4a-540ff4a6233b.png)

# VPC の作成

VPC の Network、その中に作り混む Public Subnet そして Private Subnet のレンジを、最低限決める必要があります。
ここでは以下のように決めました。

```
VPC の Network 10.8.0.0/16                 
VPC 内の Public Subnet 10.8.128.0/17        
VPC 内の Private Subnet 10.8.0.0/17          
```

この後は、コマンドラインで作成して行きます。
一つ一つコマンドを打つのは大変なのでシェル化しています。

(※ここでは Single Zone 用にネットワークを構成しています。Multi AZ にするには、サブネット等の必要なコンポーネントを AZ分作成する必要があります) 

```shell-session
#!/bin/bash

VPC_NETWORK=10.8.0.0/16                 # VPC の Network 
VPC_SUBNET_PUBLIC=10.8.128.0/17         # VPC 内の Public Subnet
VPC_SUBNET_PRIVATE=10.8.0.0/17          # VPC 内の Private Subnet

# Create a VPC
echo "[STEP1] Create VPC " $VPC_NETWORK 
VPC_ID=`aws ec2 create-vpc --cidr-block $VPC_NETWORK | jq -r .Vpc.VpcId`
aws ec2 modify-vpc-attribute --vpc-id $VPC_ID --enable-dns-hostnames | jq .
echo "VPCID = " $VPC_ID "has been created" 

# Create a public subnet in the VPC
echo "[STEP2] Create Public Subnet " $VPC_SUBNET_PUBLIC 
PUBLIC_SUBNET=`aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block $VPC_SUBNET_PUBLIC | jq -r .Subnet.SubnetId`
aws ec2 create-tags --resources $PUBLIC_SUBNET --tags Key=Name,Value=my-public | jq .
echo "Public subnet ID = " $PUBLIC_SUBNET "has been created" 

# Create a privatesubnet in the VPC
echo "[STEP3] Create Private Subnet " $VPC_SUBNET_PRIVATE
PRIVATE_SUBNET=`aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block $VPC_SUBNET_PRIVATE | jq -r .Subnet.SubnetId`
aws ec2 create-tags --resources $PRIVATE_SUBNET --tags Key=Name,Value=my-private | jq .
echo "Private subnet ID = " $PRIVATE_SUBNET "has been created" 

# Create Internet Gateway
echo "[STEP4] Create Internet Gateway in VPC " $VPC_ID 
I_GW=`aws ec2 create-internet-gateway | jq -r .InternetGateway.InternetGatewayId`
aws ec2 attach-internet-gateway --vpc-id $VPC_ID --internet-gateway-id $I_GW | jq .
echo "Internet Gateway ID = " $I_GW "has been created" 

# Create a route table for public subnet
echo "[STEP5] Create a Route table for the public subnet id" $PUBLIC_SUBNET
R_TABLE=`aws ec2 create-route-table --vpc-id $VPC_ID | jq -r .RouteTable.RouteTableId`
# -- sometimes it takes time to get Route Table id --
while ! aws ec2 describe-route-tables --route-table-id $R_TABLE  | jq .; do sleep 1; done
aws ec2 create-route --route-table-id $R_TABLE --destination-cidr-block 0.0.0.0/0 --gateway-id $I_GW | jq .
aws ec2 associate-route-table --subnet-id $PUBLIC_SUBNET --route-table-id $R_TABLE | jq .
echo "Route table ID " $R_TABLE " for the public subnet(" $PUBLIC_SUBNET ") has been created" 

# Create a NAT Gateway
echo "[STEP6] Create NAT Gateway in "  $PUBLIC_SUBNET
EIP=`aws ec2 allocate-address --domain vpc | jq -r .AllocationId`
NAT_GW=`aws ec2 create-nat-gateway --subnet-id $PUBLIC_SUBNET --allocation-id $EIP | jq -r .NatGateway.NatGatewayId`      
echo "NAT Gateway ID " $NAT_GW " has been created in the public subnet(" $PUBLIC_SUBNET ")"

# Wait for NAT Gateway created. Sometimes it took a few minutes
NAT_GW_STATUS=`aws ec2 describe-nat-gateways --nat-gateway-id $NAT_GW | jq -r .NatGateways[].State`
echo "- NAT Gateway status is " $NAT_GW_STATUS
while [ $NAT_GW_STATUS != "available" ]
do
  NAT_GW_STATUS=`aws ec2 describe-nat-gateways --nat-gateway-id $NAT_GW | jq -r .NatGateways[].State`
  if [ $NAT_GW_STATUS != "available" ] ; then
    echo "- NAT Gatewya id = " $NAT_GW " State = " $NAT_GW_STATUS
    echo "- Waiting for the NAT Gateway available"
    sleep 10;
  else
    echo "- NAT Gatewya id = " $NAT_GW " State = " $NAT_GW_STATUS
  fi
done

# Create a Route Tatble  for Private subnet
echo "[STEP7] Create a Route Table for the private subnet " $PRIVATE_SUBNET
R_TABLE_NAT=`aws ec2 create-route-table --vpc-id $VPC_ID | jq -r .RouteTable.RouteTableId`
# -- sometimes it takes time to get Route Table id --
while ! aws ec2 describe-route-tables --route-table-id $R_TABLE_NAT  | jq . ; do sleep 1; done
aws ec2 create-route --route-table-id $R_TABLE_NAT --destination-cidr-block 0.0.0.0/0 --gateway-id $NAT_GW | jq .
aws ec2 associate-route-table --subnet-id $PRIVATE_SUBNET --route-table-id $R_TABLE_NAT | jq .
echo "Route table ID " $R_TABLE_NAT " for the public subnet(" $PRIVATE_SUBNET ") has been created" 


```


# EC2インスタンスの作成

直接 Private Subnet には入れないので、Public Subnet と Private Subnet にそれぞれ EC2 を配置する必要があります。
この手順は簡単なので後ほど。

**VPC ID　の取得**

```
$ aws ec2 describe-vpcs | jq -r '.Vpcs[] | [.CidrBlock, .VpcId] | @csv'
"10.7.0.0/16","vpc-0e403f03014e706c3"
"10.8.0.0/16","vpc-08e6b9ecd4e90c949"  # Bastion 用に 今回作成したのはこれ
$ 
```

**セキュリティグループの作成**

```
$ export VPC_ID=vpc-08e6b9ecd4e90c949
$ aws ec2 create-security-group --group-name "bastion-scg" --description "bastion" --vpc-id $VPC_ID
{
    "GroupId": "sg-01edf951b3eee98e4"
}
$

#  返ってきた GroupId を変数に入力
$ export SG_ID=sg-01edf951b3eee98e4  

# SSH 用に Port 22 を開ける設定をする 
$ aws ec2 authorize-security-group-ingress --group-id $SG_ID --protocol tcp --port 22 --cidr 0.0.0.0/0
```


**EC2のキーペアの作成**

```
$ aws ec2 create-key-pair --key-name BastionKeyPair --query "KeyMaterial" --output text > BastionKeyPair.pem
$ chmod 400 BastionKeyPair.pem # 鍵のパーミッションが緩いと ssh ができないので、適切なパーミッションに変更
```

**Public Subnet 内へのEC2インスタンスの作成**

Public Subnet 内の EC2 インスタンスには、Elastic IPを与える必要があります。

EC2を作る Subnet ID が必要なので Subnet ID を調べます。

```
$  aws ec2 describe-subnets | jq -r '.Subnets[] | [ .CidrBlock, .SubnetId, .AvailabilityZone, .Tags[].Value ] | @csv'
"10.8.128.0/17","subnet-0d1224192eb265726","ap-northeast-1c","my-public"    # 最初は、ここに EC2を作る
"10.7.128.0/17","subnet-0a2360ce9dca34f9d","ap-northeast-1d","shared","my-public"
"10.7.0.0/17","subnet-03a000478bacb81f6","ap-northeast-1d","my-private","shared"
"10.8.0.0/17","subnet-0a4ecb7a715715c60","ap-northeast-1c","my-private"  # 次は、ここに EC2を作る
$ 
```



```
$ export PUB_SUBNET_ID=subnet-0d1224192eb265726 #  Public Subnet の ID 
$ export SG_ID=sg-01edf951b3eee98e4                 #  作成した Security Group の ID
$ export KEY_PAIR_NAME=BastionKeyPair               #  作成した Key Pair の名前

$ aws ec2 run-instances \
   --image-id ami-00f045aed21a55240 \
   --count 1 \
   --instance-type t2.micro \
   --key-name $KEY_PAIR_NAME \
   --security-group-ids $SG_ID \
   --subnet-id $PUB_SUBNET_ID \
   --associate-public-ip-address
$
```

**Private Subnet 内へのEC2インスタンスの作成**

```
$ export PRIVATE_SUBNET_ID=subnet-0a4ecb7a715715c60　　#  Private Subnet の ID 
$ export SG_ID=sg-01edf951b3eee98e4                #  変更無し 
$ export KEY_PAIR_NAME=BastionKeyPair              #  変更無し
 
$ aws ec2 run-instances \
   --image-id ami-00f045aed21a55240 \
   --count 1 \
   --instance-type t2.micro \
   --key-name $KEY_PAIR_NAME \
   --security-group-ids $SG_ID \
   --subnet-id $PRIVATE_SUBNET_ID \
   --no-associate-public-ip-address
$
```




# EC2インスタンスへのアクセス確認

作成した EC2 の IPアドレスを確認します。

```
$ aws ec2 describe-instances | jq -r '.Reservations[] | [.Instances[].InstanceType, .Instances[].InstanceId, .Instances[].State.Name, .Instances[].PrivateIpAddress, .Instances[].PublicIpAddress ] | @csv'
"t2.micro","i-007604a2516790360","running","10.8.129.192","13.231.221.123"
"t2.micro","i-0d513d50bc6e871a1","running","10.8.63.146",
$ 
```

Public Subnet に作った EC2は、インターネット向けのIPと Private IP の2つを持っている事に注目しましょう。


## Public SubnetにあるEC2インスタンスにログイン

変数に Pubilc Bastion のIPアドレスをセットします。

```
$export PUBLIC_BASTION=13.231.221.123                 # PUBLIC 側は Global IPを使う
```

Public Subnet にある EC2にログインします。

```
$ ssh -i BastionKeyPair.pem ec2-user@$PUBLIC_BASTION
The authenticity of host '13.231.221.123 (13.231.221.123)' can't be established.
ECDSA key fingerprint is SHA256:NgzW7WXI+DUccMWliMonHxmOkc2J7PFhturC3edMNfE.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '13.231.221.123' (ECDSA) to the list of known hosts.

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
47 package(s) needed for security, out of 94 available
Run "sudo yum update" to apply all updates.
[ec2-user@ip-10-8-129-192 ~]$ exit  # 一旦抜ける
```

ログインした EC2からは一旦ログアウトしておきます。

## Private SubnetにあるEC2インスタンスにログイン

ローカルに作成してある KeyPair を Public Subnet にあるEC2に `scp` でコピーします。

```
$ scp -i BastionKeyPair.pem ./BastionKeyPair.pem  ec2-user@$PUBLIC_BASTION:/home/ec2-user
```

Public Subnet にあるEC2に `ssh` ログインして鍵の権限を 400 に変更します。

```
$ ssh -i BastionKeyPair.pem ec2-user@$PUBLIC_BASTION
$ chmod 400 BastionKeyPair.pem 
```

変数に Private Bastion サーバーのIPをセットします。

```
$ export PRIVATE_BASTION=10.8.63.146
```

現在ログインしている EC2から、さらに Private Subnet にあるEC2にログインします。

```
[ec2-user@ip-10-8-129-192 ~]$ ssh -i BastionKeyPair.pem ec2-user@$PRIVATE_BASTION
The authenticity of host '10.8.63.146 (10.8.63.146)' can't be established.
ECDSA key fingerprint is SHA256:dwR3Blv3z3aJNcG3JdbRb1eiiZ5IFh6oN40143anS5c.
ECDSA key fingerprint is MD5:7d:b7:b6:e5:23:8a:8a:58:59:4d:cf:2f:a4:c7:40:42.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '10.8.63.146' (ECDSA) to the list of known hosts.

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
47 package(s) needed for security, out of 94 available
Run "sudo yum update" to apply all updates.
[ec2-user@ip-10-8-63-146 ~]$ 
```

ログインできる事が確認できれば、完了です。

## EC2 の CLI での起動/停止

コスト節約のために踏み台の EC2を止める場合、以下のようにします。

インスタンスIDのリストの取得

```
$ aws ec2 describe-instances | jq -r '.Reservations[] | [.Instances[].InstanceType, .Instances[].InstanceId, .Instances[].State.Name, .Instances[].PrivateIpAddress, .Instances[].PublicIpAddress ] | @csv'
"t2.micro","i-0a6d6f2ceeacfd599","running","10.8.132.237","54.238.201.65"
"t2.micro","i-03549090839a17cab","running","10.8.19.122",
$ 
```

インスタンスの停止

```
$ aws ec2 stop-instances --instance-ids i-03549090839a17cab i-0a6d6f2ceeacfd599
```

インスタンスの開始

```
aws ec2 start-instances --instance-ids i-03549090839a17cab i-0a6d6f2ceeacfd599
```