AWS CLIでEC2インスタンス作成・削除・再作成

AWS CLIを使ってEC2インスタンスの作成・削除・再作成をやってみました。
事前に作成済みの踏み台サーバーから、プライベートネットワークに開発用サーバーを作成・削除・再作成します。

 <img src="https://qiita-image-store.s3.amazonaws.com/0/345798/94cabb89-518e-242f-5d55-527517dce729.png" width=70%>

# 目次
1. 踏み台サーバーに権限付与
2. インスタンスの作成
3. インスタンスにPython3関係をインストール
4. AMIの作成
5. インスタンスの停止・削除
6. 作成したAMIからインスタンスを作成

# 前提条件
- VPC, サブネット、インターネットゲートウェイ、セキュリテイグループ、キーペア、等々は作成済み
- 踏み台サーバーとNATインスタンスは作成済み
- 踏み台サーバーからインスタンスの作成・削除・再作成を行う

# 手順
## [1] 踏み台サーバーに権限付与
IAMで踏み台サーバーに権限を付与します。
EC2に関するアクセスを全て許可する「AmazonEC2FullAccess」を付与します。

- マネージメントコンソールのIAMの画面から[ロールの作成]を選択
- このロールを使用するサービスで[EC2]を選択
- アクセス権限ポリシーの画面で"EC2"で検索し[AmazonEC2FullAccess]を選択
- タグの追加は設定無し
- ロールの作成でロール名を入力
- 踏み台サーバーに作成したロールの割り当て

<img src="https://qiita-image-store.s3.amazonaws.com/0/345798/a5264404-6014-035d-de4b-8d3c6d6346ac.png" width=70%>
<img src="https://qiita-image-store.s3.amazonaws.com/0/345798/bb37ebaf-38c5-80e7-ccdc-680839ad286d.png" width=80%>
<img src="https://qiita-image-store.s3.amazonaws.com/0/345798/54dc1ad6-8485-be5c-4f3c-5372deee1898.png" width=80%>
<img src="https://qiita-image-store.s3.amazonaws.com/0/345798/31dcfde7-8a71-658c-1900-a4abe313831f.png" width=80%>
<img src="https://qiita-image-store.s3.amazonaws.com/0/345798/ca63de3f-ebe6-3bd7-eff4-c6b571651e35.png" width=80%>
<img src="https://qiita-image-store.s3.amazonaws.com/0/345798/9542c0ba-f0cc-d63d-a11a-2e7b12d38996.png" width=80%>

## [2] インスタンスの作成
踏み台サーバーはOSがAmazon Linuxなので、デフォルトでAWS CLIがインストールされています。

```bash
$ aws --version
aws-cli/1.15.80 Python/2.7.14 Linux/4.14.88-88.76.amzn2.x86_64 botocore/1.10.79
```

AWS CLIでの操作ではJSON形式でデータを扱うため、便利のために"jq"コマンドをインストールします。

```bash
$ sudo yum -y install jq
```
### [2-1] AWS CLIを試す
試しに作成済みインスタンスの情報をAWS CLIで取得してみます。

```bash
$ aws ec2 describe-instances | jq '.Reservations[].Instances[] | {InstanceId, Tags}'
You must specify a region. You can also configure your region by running \
"aws configure".
```

リージョンを指定しないとダメなようです。

```bash
$ aws ec2 describe-instances --region ap-northeast-1 \
  | jq '.Reservations[].Instances[] | {InstanceId}'
{
  "InstanceId": "i-xxxxxxxxx"
}
{
  "InstanceId": "i-xxxxxxxxxxxx",
}
```

今度は上手くいきました。
作成したIAMロールが踏み台サーバーに割り当てられていることが確認できました。

### [2-2] インスタンスの種類を決める
インスタンスを作成する前に、インスタンスタイプやAMI(OS)を決めます。
セキュリティグループ、サブネット、キーペアは事前に作成しました。

| 項目             | 内容                 |  AWSで設定するときの値
|:-----------------|---------------------|------------------|
| AMI              | Amazon Linux 2 AMI  | ami-0d7ed3ddb85b521a6 |
| インスタンスタイプ   | t3.nano             | t3.nano |
| 作成するインスタンスの数 | 1                | 1
| キーペア          | -                    | (キーペアの名称) |
| サブネット         | -                    | subnet-xxxxxx |
| プライベートIP      | 10.xx.xx.xx         | 10.xx.xx.xx |
| セキュリティグループ | -                    | sg-xxxxxx |
| タグ             | Name=private-inst    | Tags=[{Key=Name,Value=private-inst}]
| T2/T3 無制限     | 無効                 | standard

AMIのIDはマネジメントコンソールから確認するのが簡単です。
<img src="https://qiita-image-store.s3.amazonaws.com/0/345798/e91e0f04-3464-783f-be64-db3b269e9552.png" width=80%>


### [2-3] dry-runで試す
実際にインスタンスを作成する前に、dry-runオプションを付けてエラーがないか確認していきます。

```bash
$ aws ec2 run-instances --dry-run --region ap-northeast-1 \
  --image-id ami-0d7ed3ddb85b521a6 --count 1 \
  --instance-type t3.nano --key-name (キーペアの名称) \
  --subnet-id subnet-xxxxxx --private-ip-address 10.xx.xx.xx \
  --security-group-ids sg-xxxxxx \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=private-inst}]' \
  --credit-specification CpuCredits=standard

An error occurred (DryRunOperation) when calling the RunInstances operation: \
Request would have succeeded, but DryRun flag is set.
```

上手くいくだろう("Request would have succeeded")と返ってきました。

### [2-4] 本番コマンド実行
dry-runオプションを削除して実行します。

```bash
$ aws ec2 run-instances --region ap-northeast-1 \
  --image-id ami-0d7ed3ddb85b521a6 --count 1 \
  --instance-type t3.nano --key-name (キーペアの名称) \
  --subnet-id subnet-xxxxxx --private-ip-address 10.xx.xx.xx \
  --security-group-ids sg-xxxxxx \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=private-inst}]' \
  --credit-specification CpuCredits=standard
{
    "Instances": [
        {
...(以下、省略)...
```

作成されたインスタンスの情報が返ってきたので、作成に成功したようです。
念の為、マネジメントコンソールから確認すると、インスタンスが追加されていました。
ルートデバイスの容量はデフォルトで8GiBになるようです。


## [3] インスタンスにPython3関係をインストール
踏み台サーバーから、作成したインスタンスにログインします。

```bash
$ ssh -i ./xxxx.pem ec2-user@10.xx.xx.xx
```

Python3, pip3, virtualenvをインストールします。

```bash
$ sudo yum install python3 python3-pip
$ sudo pip3 install virtualenv
```

## [4] AMIの作成
踏み台サーバーからAMIの作成を行います。

```bash
$ aws ec2 create-image --region ap-northeast-1 --instance-id i-xxxxxx \
  --name "python3-base-ami" --reboot
{
    "ImageId": "ami-xxxxxx"
}
```

作成したAMIのIDが返ってきます。
describe-imagesで確認すると、AMIが作成されていることが分かります。

```bash
$ aws ec2 describe-images --region ap-northeast-1 --image-id ami-xxxxxx \
  | jq '.Images[]'
{
...(途中省略)...
  "Name": "python3-base-ami",
  "Hypervisor": "xen",
  "EnaSupport": true,
  "SriovNetSupport": "simple",
  "ImageId": "ami-xxxx",
  "State": "available",
  "BlockDeviceMappings": [
    {
      "DeviceName": "/dev/xvda",
      "Ebs": {
        "Encrypted": false,
        "DeleteOnTermination": true,
        "VolumeType": "gp2",
        "VolumeSize": 8,
        "SnapshotId": "snap-xxxxxx"
      }
    }
  ],
...(途中省略)...
}

```

## [5] インスタンスの停止・削除
インスタンスの停止と削除を行います。

```bash
$ aws ec2 stop-instances --region ap-northeast-1 --instance-ids i-xxxxxx
$ aws ec2 terminate-instances --region ap-northeast-1 --instance-ids i-xxxxxx
```

Stateを確認すると"terminated"になっています。

```bash
$ aws ec2 describe-instances --region ap-northeast-1 --instance-ids i-xxxxxx \
  | jq '.Reservations[].Instances[] | {InstanceId,Tags,State}'
{
  "InstanceId": "i-xxxxxx",
  "Tags": [
    {
      "Value": "private-inst",
      "Key": "Name"
    }
  ],
  "State": {
    "Code": 48,
    "Name": "terminated"
  }
}
```

## [6] 作成したAMIからインスタンスを作成

### [6-1] ルートデバイスの容量を拡張する
インスタンスを作成する際、ルートデバイスの容量はデフォルトだと8GiBですが、16GiBに拡張してみます。
拡張するための設定をJSONファイルに記述します。

- DeviceNameはデフォルトのルートデバイスと合わせる
- SnapshotIdに作成したAMIのSnapshotIdを設定する

```device-mapping.json
[
  {
    "DeviceName": "/dev/xvda",
    "Ebs": {
      "DeleteOnTermination": true,
      "VolumeType": "gp2",
      "VolumeSize": 16,
      "SnapshotId": "snap-xxxxxx"
    }
  }
]
```

### [6-2] インスタンス作成
"--image-id"に作成したAMIのID、"--block-device-mappings"にルートデバイス拡張のJSONファイルのパスを設定します。

```bash
$ aws ec2 run-instances --dry-run --region ap-northeast-1 --image-id ami-xxxxxx \
  --block-device-mappings file://device-mapping.json --count 1 \
  --instance-type t3.nano --key-name (キーペアの名称) \
  --subnet-id subnet-xxxxxx --private-ip-address 10.0.2.10 \
  --security-group-ids sg-xxxxxx \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=private-inst}]' \
  --credit-specification CpuCredits=standard
```

作成したインスタンスのボリュームを確認すると16GiBになっていることが分かります。

```bash
$ aws ec2 describe-volumes --region ap-northeast-1 | jq '.Volumes[] | \
  select(.Attachments[].InstanceId=="i-xxxxxx")'
{
  "AvailabilityZone": "ap-northeast-1d",
  "Attachments": [
    {
      "AttachTime": "2019-02-03T09:51:34.000Z",
      "InstanceId": "i-xxxxxx",
      "VolumeId": "vol-xxxxx",
      "State": "attached",
      "DeleteOnTermination": true,
      "Device": "/dev/xvda"
    }
  ],
  "Encrypted": false,
  "VolumeType": "gp2",
  "VolumeId": "vol-xxxxxx",
  "State": "in-use",
  "Iops": 100,
  "SnapshotId": "snap-xxxxxx",
  "CreateTime": "2019-02-03T09:51:34.102Z",
  "Size": 16
}
```
### [6-3] ライブラリ・ツールの確認
AMI作成前にインストールしたPython3, pip3, virtualenvがインストールされていることを確認します。

```bash
$ python3 --version
Python 3.7.1
$ pip3 --version
pip 9.0.3 from /usr/lib/python3.7/site-packages (python 3.7)
$ virtualenv --version
16.3.0
```

# 参考資料
[AWS Cli自分用Tips - Qiita](https://qiita.com/takachan/items/421928dc61c51af97fb1)
[AWS-CLI EC2でおなじものをつくるよ - Qiita](https://qiita.com/bohebohechan/items/891120175efc1b3cc7c4)
[AWS CLI Command Reference &mdash; AWS CLI 1.16.96 Command Reference](https://docs.aws.amazon.com/cli/latest/index.html)
[EC2 インスタンスを起動、リスト、および終了する - AWS Command Line Interface](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-services-ec2-instances.html)
[コマンドラインからの EBS ボリュームの変更 - Amazon Elastic Compute Cloud](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/cli-modify.html)