[aws-cli tips] aws-cliを使ってEC2インスタンスのEBSのスナップショットを作成する

# はじめに
大量のEC2インスタンスのEBSのスナップショットを作成しようとすると、AWSコンソール上からポチポチやるのでは辛いところがあります。そこで、aws cliを使いコマンドライン上で、EC2インスタンス名からEBSのスナップショットを作成する手順を備忘録として残します。

# aws cli
まず、今回使用するaws cliについて軽く解説をします。[本家の解説ページ][awscli]より引用。
> AWS Command Line Interface (AWS CLI) は、コマンドラインシェルでコマンドを使用して AWS サービスとやり取りするためのオープンソースツールです。

aws cliには大量のサブコマンドがあり、それらを用いてaws上の各種リソースにアクセスします。今回使用するコマンドは以下の二つです。

- `aws ec2 describe-instances`
- `aws ec2 create-snapshot`

`describe-instances`はインスタンスの情報を取得するためのコマンド、`create-snapshot`はEBSのスナップショットを作成するコマンドです。
`aws ec2 create-snapshot`のオプションには以下の物があります。この中で必須のオプションが`--volume-id`です。ここにはスナップショットを作成するEBSのボリュームIDを指定します。
また、タグを設定する場合、ResourceTypeには`ResourceType="snapshot"`を指定します。

```
$ aws ec2 create-snapshot help
...
OPTIONS
       --description (string)
          A description for the snapshot.

       --volume-id (string)
          The ID of the EBS volume.

       --tag-specifications (list)
          The tags to apply to the snapshot during creation.

       Shorthand Syntax:

          ResourceType=string,Tags=[{Key=string,Value=string},{Key=string,Value=string}] ...
...
```

`aws ec2 describe-instances`のオプションには以下の物があります。オプション無しで実行すると、アカウントに紐ずく全てのEC2インスタンスの情報を取得しますが、`--filters`オプションを使用することで情報を取得するEC2を絞り込むことができます。

```
$ aws ec2 describe-instances help
...
OPTIONS
       --filters (list)
          The filters.
...
       Shorthand Syntax:

          Name=string,Values=string,string ...
...
```

# ボリュームIDの取得
まず、EBSのボリュームIDを取得します。`aws ec2 describe-instances`の`--filters`オプションでは様々な条件でインスタンスの絞り込みを行えます。今回はタグを用いて絞り込みを行います。タグでの絞り込みの書式は以下通りです。

```
 aws ec2 describe-instances --filters "Name=tag:{TAG_NAME},Values={TAG_VALUE1}[,TAG_VALUE2]..."
```
適切なタグ名と値の組み合わせでコマンドを叩くと次の様なデータが返ってきます。

```
$ aws ec2 describe-instances --filters "Name=tag:Name,Values=aws-cli-snapshot"

{
    "Reservations": [
        {
            "Groups": [],
            "Instances": [
                {
                    "AmiLaunchIndex": 0,
                    "ImageId": "ami-xxxxxxxxxxxxxxxxx",
                    "InstanceId": "i-xxxxxxxxxxxxxxxxx",
                    "InstanceType": "t2.micro",
                    "KeyName": "xxxxx",
                    "LaunchTime": "2020-06-03T03:15:48+00:00",
                    "Monitoring": {
                        "State": "disabled"
                    },
                    "Placement": {
                        "AvailabilityZone": "ap-northeast-1a",
                        "GroupName": "",
                        "Tenancy": "default"
                    },
                    "PrivateDnsName": "ip-xxx-xx-xx-xxx.ap-northeast-1.compute.internal",
                    "PrivateIpAddress": "xxx.xx.xx.xx",
                    "ProductCodes": [],
                    "PublicDnsName": "ec2-xxx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com",
                    "PublicIpAddress": "xx.xxx.xxx.xxx",
                    "State": {
                        "Code": 16,
                        "Name": "running"
                    },
                    "StateTransitionReason": "",
                    "SubnetId": "subnet-xxxxxxxx",
                    "VpcId": "vpc-xxxxxxxx",
                    "Architecture": "x86_64",
                    "BlockDeviceMappings": [
                        {
                            "DeviceName": "/dev/xvda",
                            "Ebs": {
                                "AttachTime": "2020-05-13T02:20:17+00:00",
                                "DeleteOnTermination": true,
                                "Status": "attached",
                                "VolumeId": "vol-123456789xxxxxxxx"
                            }
                        }
                    ],
                    "ClientToken": "",
                    "EbsOptimized": false,
                    "EnaSupport": true,
                    "Hypervisor": "xen",
                    "IamInstanceProfile": {
                        "Arn": "arn:aws:iam::1234567890:instance-profile/xxxxxxxx",
                        "Id": "123456789XXXXXXXXXX"
                    },
                    "NetworkInterfaces": [
                        {
                            "Association": {
                                "IpOwnerId": "amazon",
                                "PublicDnsName": "ec2-xxx-xx-xx-xx.ap-northeast-1.compute.amazonaws.com",
                                "PublicIp": "xxx.xx.xx.xx"
                            },
                            "Attachment": {
                                "AttachTime": "2020-05-13T02:20:16+00:00",
                                "AttachmentId": "eni-attach-xxxxxxxxxxxxxxxxx",
                                "DeleteOnTermination": true,
                                "DeviceIndex": 0,
                                "Status": "attached"
                            },
                            "Description": "",
                            "Groups": [
                                {
                                    "GroupName": "xxxxx",
                                    "GroupId": "sg-xxxxxxxxxxxxxxxxx"
                                }
                            ],
                            "Ipv6Addresses": [],
                            "MacAddress": "xx:xx:xx:xx:xx:xx",
                            "NetworkInterfaceId": "eni-xxxxxxxxxxxxxxxxx",
                            "OwnerId": "414867676510",
                            "PrivateDnsName": "ip-xxx-xx-xx-xx.ap-northeast-1.compute.internal",
                            "PrivateIpAddress": "xxx.xx.xx.xx",
                            "PrivateIpAddresses": [
                                {
                                    "Association": {
                                        "IpOwnerId": "amazon",
                                        "PublicDnsName": "ec2-xxx-xx-xx-xx.ap-northeast-1.compute.amazonaws.com",
                                        "PublicIp": "xxx.xx.xx.xx"
                                    },
                                    "Primary": true,
                                    "PrivateDnsName": "ip-xxx-xx-xx-xxx.ap-northeast-1.compute.internal",
                                    "PrivateIpAddress": "xxx.xx.xx.xx"
                                }
                            ],
                            "SourceDestCheck": true,
                            "Status": "in-use",
                            "SubnetId": "subnet-xxxxxxxx",
                            "VpcId": "vpc-xxxxxxxx",
                            "InterfaceType": "interface"
                        }
                    ],
                    "RootDeviceName": "/dev/xvda",
                    "RootDeviceType": "ebs",
                    "SecurityGroups": [
                        {
                            "GroupName": "xxxxxxxxxx",
                            "GroupId": "sg-xxxxxxxxxxxxxxxxx"
                        }
                    ],
                    "SourceDestCheck": true,
                    "Tags": [
                        {
                            "Key": "Name",
                            "Value": "aws-cli-snapshot"
                        }
                    ],
                    "VirtualizationType": "hvm",
                    "CpuOptions": {
                        "CoreCount": 1,
                        "ThreadsPerCore": 1
                    },
                    "CapacityReservationSpecification": {
                        "CapacityReservationPreference": "open"
                    },
                    "HibernationOptions": {
                        "Configured": false
                    },
                    "MetadataOptions": {
                        "State": "applied",
                        "HttpTokens": "optional",
                        "HttpPutResponseHopLimit": 1,
                        "HttpEndpoint": "enabled"
                    }
                }
            ],
            "OwnerId": "xxxxxxxxxxxxxxxxx",
            "ReservationId": "r-xxxxxxxxxxxxxxxxx"
        }
    ]
}

```

EBSのボリュームIDは、Reservations -> Instances -> BlockDeviceMappings -> Ebs -> VolumeIdにあります。この値を`jq`で取り出します。ボリュームIDは後ほど使用するので変数へ格納しておきます。

```
$ export VOLUME_ID=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=aws-cli-snapshot" \
| jq '.Reservations[].Instances[].BlockDeviceMappings[].Ebs.VolumeId' -r)

$ echo $VOLUME_ID
vol-123456789xxxxxxxx
```

# スナップショットの作成
続いて、取得したボリュームIDを用いてスナップショットを作成します。スナップショットの作成方法は、`aws ec2 create-snapshot`の`--volume-id`オプションに先ほどのボリュームIDを渡すだけです。今回はタグとdescriptionも同時につけておきます。

```
$ aws ec2 create-snapshot --volume-id $VOLUME_ID \
--tag-specification 'ResourceType="snapshot",Tags=[{Key="Name",Value="aws-cli-snapshot"}]' \
--description "Created by aws cli"
```

以上でEBSのスナップショットが作成されます！



# まとめ
今回はaws-cliを使ってコマンドラインからEC2のEBSのスナップショットを作成する方法をまとめました。EC2のfilter方法だけあらかじめ決めておけば、シェルスクリプトなどを用いることで、数が多くなってもスナップショットの作成が容易に行えると思います。
aws-cli含め、awsはまだまだ初心者ですが、小さなハックから知識を深めていきたいです。




[awscli]:https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-chap-welcome.html