AWS CLI で json 使って EC2 をいじる

ちょうど今AWS環境におけるサーバ構築の効率化を進めているので、
使っている AWS CLI をまとめたいと思います。

# 前提
aws cli のインストール、認証情報の登録は済んでいることとする。
使用するAMIは準備済み。

# EC2インスタンス作成
最初に AWS CLI でインスタンスを作成してみる。
AWS CLI は json形式で設定値を流し込むことが出来ます。
--generate-cli-skeleton オプションで jsonファイルのひな型を出力することが出来るので以下を実行。

```bash
aws ec2 run-instances --generate-cli-skeleton
```

jsonファイルが出力されるので、それを基に以下のような感じで指定したい項目を埋めていく。
※適宜必要な項目を埋めて下さい。

```json:ec2.json
{
    "BlockDeviceMappings": [
        {
            "DeviceName": "/dev/sda1",
            "VirtualName": "${InstanceName}",
            "Ebs": {
                "DeleteOnTermination": true,
                "VolumeSize": 40,
                "VolumeType": "gp2"
            },
            "NoDevice": ""
        }
    ],
    "ImageId": "${AMIID}",
    "InstanceType": "t2.micro",
    "MaxCount": 1,
    "MinCount": 1,
    "Monitoring": {
        "Enabled": false
    },
    "Placement": {
        "AvailabilityZone": "ap-northeast-1d"
    },
    "SubnetId": "${SubnetID}",
    "UserData": "",
    "AdditionalInfo": "",
    "DisableApiTermination": true,
    "DryRun": false,
    "IamInstanceProfile": {
        "Name": "${RoleName}"
    },
    "InstanceInitiatedShutdownBehavior": "stop",
    "NetworkInterfaces": [
        {
            "AssociatePublicIpAddress": true,
            "DeleteOnTermination": true,
            "Description": "",
            "DeviceIndex": 0,
            "Groups": [
                "${SecurityGroupID}"
            ],
            "PrivateIpAddresses": [
                {
                    "Primary": true,
                    "PrivateIpAddress": "173.16.0.5"
                }
            ],
            "SubnetId": "${SubnetID}"
        }
    ],
    "TagSpecifications": [
        {
            "ResourceType": "instance",
            "Tags": [
                {
                    "Value": "${任意の値}",
                    "Key": "${任意のキー}"
                },
                {
                    "Value": "${任意の値}",
                    "Key": "${任意のキー}"
                }
            ]
        },
        {
            "ResourceType": "volume",
            "Tags": [
                {
                    "Value": "${任意の値}",
                    "Key": "${任意のキー}"
                },
                {
                    "Value": "${任意の値}",
                    "Key": "${任意のキー}"
                }
            ]
        }
    ]
}
```
- VirtualName ... インスタンス名
- VolumeSize ... EBSボリュームサイズ
- VolumeType ... EBSボリュームタイプ
- DeleteOnTermination ... インスタンス削除時にEBSボリュームも一緒に削除するかどうか
- ImageId ... ベースとなるAMI ID を指定
- DisableApiTermination ... 削除保護の有無
- IamInstanceProfile ... IAMロール名
- Groups ... セキュリティグループ名
- PrivateIpAddress ... プライベートIPアドレス
- SubnetId ... サブネットID
- TagSpecifications ... タグの指定(上記の例ではインスタンスとボリュームの両方に2つずつタグをつけている)


で、それを --cli-input-json で指定すればOK

```bash

aws ec2 run-instances --cli-input-json file:///path/to/dir/ec2.json
```

※jsonファイルは file:// で指定しないと以下のようなエラーになるので注意。

```
[エラー文言]
Error parsing parameter 'cli-input-json': Invalid JSON: No JSON object could be decoded
```

これでインスタンスが作成されます。


# EIP取得
次は、EIPを取得してさっき作成したインスタンスに紐づけてみる。
まずは、EIPを取得。
※ --output text は特定の値を取りたいときに便利です。以下の場合は AllocationID が戻り値で取れます。 

```bash
aws ec2 allocate-address --domain vpc --query AllocationId --output text
```

そして、紐付けたい AllocationID と InstanceID を指定すればOK

```bash
aws ec2 associate-address --allocation-id ${AllocationID} --instance ${InstanceID}
```

これで作成したインスタンスにEIPが紐づきました。

# SecurityGroup作成
次は、セキュリティグループの作成から、ルールの追加、インスタンスへの紐づけをやってみる。
これも json ファイルを出力する。

```bash
aws ec2 create-security-group --generate-cli-skeleton
```

少ないからオプションで指定してもいいかもしれないが、せっかくだから --cli-input-json でやりたい。

```json:securitygroup.json
{
    "Description": "${Description}", 
    "GroupName": "${SecurityGroupName}", 
    "VpcId": "${VPCID}"
}
```
- Description ... 説明
- GroupName ... セキュリティグループ名
- VpcId ... VPC ID


こんな感じで値を入れて、以下の通り実行。
※ --output text を入れているので戻り値は SecurityGroupID

```bash
aws ec2 create-security-group --cli-input-json file:///path/to/dir/securitygroup.json --output text
```

そして、作ったセキュリティグループにルールを追加。
これもまずは json 出力して

```bash
aws ec2 authorize-security-group-ingress --generate-cli-skeleton
```

必要な値を入れて

```json:rule.json
{
    "GroupId": "${SecurityGroupID}", 
    "IpPermissions": [
        {
            "FromPort": 0, 
            "IpProtocol": "tcp", 
            "IpRanges": [
                {
                    "CidrIp": "173.16.0.5/32", 
                    "Description": ""
                }
            ], 
            "FromPort": 22,
            "ToPort": 22
        }
    ],
    "DryRun": false
}
```
- GroupId ... セキュリティグループID
- CidrIp ... ソースIP
- FromPort,ToPort ... ポート範囲


以下の通り実行。

```bash
aws ec2 authorize-security-group-ingress --cli-input-json file:///path/to/dir/rule.json
```

これでセキュリティグループが作成できたので、インスタンスに適用してみよう。

```bash
aws ec2 modify-instance-attribute --group ${SecurityGroupID} --instance-id ${InstanceID}
```

既にセキュリティグループが設定されているインスタンスの場合は置き換わってしまうので注意！
--group で指定したセキュリティグループのみになってしまうので、追加したい場合は下記のようにスペース区切りで複数指定して下さい。

```bash
aws ec2 modify-instance-attribute --group ${SecurityGroupID} ${SecurityGroupID} --instance-id ${InstanceID}
```

#running になるまで待つ
EIPの紐づけにしても、セキュリティグループの紐づけにしても、
インスタンスの状態が running にならないと以下のようなエラーになってしまう。

```
An error occurred (InvalidInstanceID) when calling the AssociateAddress operation: The pending instance '${InstanceID}' is not in a valid state for this operation.
```

なので、一連の処理をシェルスクリプト等でまとめてやろうとすると、
インスタンス状態の判定が必要になる。

僕は以下のようにインスタンスの状態を判定していたのだが、、

```bash
STATE=`aws ec2 describe-instances --instance-ids ${InstanceID} --query 'Reservations[*].Instances[*].State[].Name' --output text`
while [ $STATE != "running" ]
do
        sleep 5
        STATE=`aws ec2 describe-instances --instance-ids ${InstanceID} --query 'Reservations[*].Instances[*].State[].Name' --output text`
done
```

こんなことせずとも、以下でステータスの状態が running になるまで待ってくれます。。

```bash
aws ec2 wait instance-running --instance-ids ${InstanceID}
```

ちなみに、wait は他にも色んなオプションがあるので何かと便利。
例えば、システムチェックが終わるのを待ちたい場合はこれ。

```bash
aws ec2 wait instance-status-ok --instance-ids ${InstanceID}
```

---
[:christmas_tree: FORK Advent Calendar 2018](/advent-calendar/2018/fork)
:arrow_left: 18日目 [ちょっとかわいいBrainF*ck](/sunnyplace/items/e7baff4d70e7d06c3f66) @sunnyplace
:arrow_right: 19日目 [git clone できない時の調べ方と対策](/username/items/xxxxxxxxx) @izanari