AWS Console, CLI, Terraform と色々な方法で EC2 インスタンスを作る

今 Terraform の勉強をしているのだが、AWS の事をよく知らないとチュートリアルすら進められない事が分かった。そこで AWS Console, AWS CLI, Terraform のそれぞれで EC2 インスタンスを作ってみる。

## 前提条件

* AWS アカウントと AWS CLI (`AWS_PROFILE`) 設定済み。
* リージョンは Oregon us-west-2 を使う (`AWS_REGION=us-west-2`)。
* 外部から SSH アクセス可能な Amazon Linux が動く t2.micro インスタンスを作る。

## AWS Console で EC2 インスタンスを作る

### キーペアの作成

* https://console.aws.amazon.com/ec2 > [Key Pairs]
* Create Key Pair
  * Key pair name: hogehoge-keypair
* 自動的に hogehoge-keypair.pem がダウンロードされる。

### VPC の作成

* https://console.aws.amazon.com/vpc/ > Launch VPC Wizard
* Step 1: Select a VPC Configuration > VPC with a Single Public Subnet
* Step 2: VPC with a Single Public Subnet
  * VPC name: hogehoge-vpc
  * Availability Zone: us-west-2a
    * 注: チュートリアルには無いが指定すること! 例えば t2.micro は us-west-2d に対応していないので、たまたま us-west-2d に割り当てられると動かない。
  * Subnet name: hogehoge-subnet
  * Create VPC

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

注) https://console.aws.amazon.com/vpc にも Create Security Group があるが、これだと Rule を同時に作れない。

* https://console.aws.amazon.com/ec2/ > Security Groups
* Create Security Groups
  * Security group name: hogehoge-security-group
  * Description: hogehoge-security-group
  * VPC: hogehoge-vpc
  * Inbound
    * Add Rule: Type: SSH, Source: My IP
  * Create

### EC2 インスタンスの起動

* https://console.aws.amazon.com/ec2/ > Launch Instance
* Step 1: Choose AMI >  Amazon Linux 2 AMI (HVM) ami-061392db613a6357b (64-bit x86) を選択
* Step 2: Choose an Instance Type > t2.micro を選択して Next
* Step 3: Configure Instance Details
  * Network: hogehoge-vpc
  * Auto-assign Public IP: Enable
    * 注: チュートリアルには無いが指定すること! これがないとパブリック IP が出来ない。
  * Review and Launch
* Step 7: Review Instance Launch
  * Security Groups > Edit security groups
* Step 6: Configure Security Group
  * Assign a security group: Select an existing security group
  * hogehoge-security-group を選択
  * Review and Launch
  * Launch
    * Select a key paier: hogehoge-keypair
    * Launch Instances
    * View instances

フィンガープリント確認

    aws ec2 get-console-output --query 'Output' --output text --instance-id i-02baaaef6f9ec52ce

ログイン

```shell
chmod 400 hogehoge-keypair.pem
ssh -i hogehoge-keypair.pem ec2-user@ec2-34-212-218-142.us-west-2.compute.amazonaws.com
```

### 削除

* EC2 インスタンスの削除は、インスタンスを選択して Actions > Instance State > Terminate
* VPC を削除すると、subnet とセキュリティグループも削除される。

## AWS CLI で EC2 インスタンスを作る

では、ほぼ同じ物を AWS CLI で作ってみます。かなり AWS Console が細かい部分を自動設定してくれていた事に気が付きます。また、CLI だと「〇〇をメモして後で使う」という作業が多く、かなり面倒臭いです。

### キーペアの作成

参考: [EC2 キーペアの作成、表示、および削除](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-services-ec2-keypairs.html)

```shell
aws ec2 create-key-pair --key-name hogehoge-keypair --query 'KeyMaterial' --output text > hogehoge-keypair.pem
chmod 400 hogehoge-keypair.pem
```

### VPC の作成

参考: [例: AWS CLI を使用して IPv4 VPC とサブネットを作成](https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/vpc-subnets-commands-example.html)

VPC を作成して VpcId をメモ。

```shell
$ aws ec2 create-vpc --cidr-block 10.0.0.0/16
{
    "Vpc": {
        "VpcId": "vpc-0f44c5100359a35a2", 
...
```

作った VPC に名前を付ける。AWS Console には Name タグの値が名前として表示される。

    aws ec2 create-tags --resources vpc-0f44c5100359a35a2 --tags Key=Name,Value=hogehoge-vcp

作った VPC の enable-dns-hostnames を有効にして、Public DNS でアクセス出来るようにする。

    aws ec2 modify-vpc-attribute --vpc-id vpc-0f44c5100359a35a2 --enable-dns-hostnames

作った VPC にサブネットを作成して SubnetId をメモ。

```shell
$ aws ec2 create-subnet --vpc-id vpc-0f44c5100359a35a2 --cidr-block 10.0.0.0/24 --availability-zone us-west-2a
{
    "Subnet": {
        "AvailabilityZone": "us-west-2a", 
        "SubnetId": "subnet-09c5ef1906c8f9a64", 
...
```

インターネットゲートウェイを作成して、InternetGatewayId をメモ。

```shell
$ aws ec2 create-internet-gateway
{
    "InternetGateway": {
        "InternetGatewayId": "igw-0496792827b64e61b"
...
```

作ったインターネットゲートウェイに作った VPC をアタッチ。

    aws ec2 attach-internet-gateway --vpc-id vpc-0f44c5100359a35a2 --internet-gateway-id igw-0496792827b64e61b

作った VPC にルートテーブルを作成して RouteTableId をメモ。
    
```shell
$ aws ec2 create-route-table --vpc-id vpc-0f44c5100359a35a2
{
    "RouteTable": {
        "RouteTableId": "rtb-0b34e4d46da3e7cd9", 
...
```

作ったルートテーブルに設定を追加して、すべてのトラフィック 0.0.0.0/0 を作ったインターネットゲートウェイに向ける

    aws ec2 create-route --route-table-id rtb-0b34e4d46da3e7cd9 --destination-cidr-block 0.0.0.0/0 --gateway-id igw-0496792827b64e61b

設定したルートテーブル確認

    aws ec2 describe-route-tables --route-table-id rtb-0b34e4d46da3e7cd9

作ったサブネットに作ったルートテーブルを割り当てる

    aws ec2 associate-route-table --subnet-id subnet-09c5ef1906c8f9a64 --route-table-id rtb-0b34e4d46da3e7cd9

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

作った VPC にセキュリティグループを作成。GroupId をメモ。

```shell
$ aws ec2 create-security-group --group-name hogehoge-security-group --description hogehoge-security-group --vpc-id vpc-0f44c5100359a35a2
{
    "GroupId": "sg-06fb1ac5a64f171ec"
}
```

作ったセキュリティグループに Port 22 へのアクセスを許可。InstanceId をメモ。

    aws ec2 authorize-security-group-ingress --group-id sg-06fb1ac5a64f171ec --protocol tcp --port 22 --cidr 0.0.0.0/0

いよいよEC2 インスタンスを起動。

```shell
$ aws ec2 run-instances \
    --image-id ami-061392db613a6357b \
    --instance-type t2.micro \
    --key-name hogehoge-keypair \
    --security-group-ids sg-06fb1ac5a64f171ec \
    --subnet-id subnet-09c5ef1906c8f9a64 \
    --associate-public-ip-address \
    --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=hogehoge-instance}]'
{
    "Instances": [
        {
            "InstanceId": "i-0ffa41b43b361ae0c", 
...
```

### 状態の確認

```
aws ec2 describe-instances --instance-id i-0ffa41b43b361ae0c
aws ec2 get-console-output --query 'Output' --output text --instance-id i-0ffa41b43b361ae0c
ssh -i hogehoge-keypair.pem ec2-user@ec2-34-215-201-252.us-west-2.compute.amazonaws.com
```

### 全部削除

```shell
aws ec2 terminate-instances --instance-ids i-0ffa41b43b361ae0c
aws ec2 delete-security-group --group-id sg-06fb1ac5a64f171ec
aws ec2 delete-subnet --subnet-id subnet-09c5ef1906c8f9a64
aws ec2 delete-route-table --route-table-id rtb-0b34e4d46da3e7cd9
aws ec2 detach-internet-gateway --internet-gateway-id igw-0496792827b64e61b --vpc-id vpc-0f44c5100359a35a2
aws ec2 delete-internet-gateway --internet-gateway-id igw-0496792827b64e61b
aws ec2 delete-vpc --vpc-id vpc-0f44c5100359a35a2
```

## Terraform で EC2 インスタンスを作る

では、ほぼ同じ物を Terraform で作る。CLI だと「〇〇をメモして後で使う」部分が変数参照になるので非常にスッキリする。

### キーペアの作成

これだけは省略します。

### Terraform で VPC の作成から EC2 の起動までを記述する。

とあるディレクトリに `example.tf` のようなファイル名で以下の内容を書けば、これだけの操作を一発で実現出来る。

```tf
// VPC を作成する。VpcId は ${aws_vpc.hoge.id} で参照する。enable-dns-hostnames を有効。
resource "aws_vpc" "hoge" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = "true"
  tags = {  Name = "hogehoge-vpc" }
}

// サブネットを作成 SubnetId は ${aws_subnet.hoge.id} で参照する。
resource "aws_subnet" "hoge" {
  vpc_id            = "${aws_vpc.hoge.id}"
  cidr_block        = "10.0.0.0/24"
  availability_zone = "us-west-2a"
  tags = {  Name = "hogehoge-subnet" }

}

// インターネットゲートウェイを作成。InternetGatewayId は ${aws_internet_gateway.hoge.id} で参照する。
resource "aws_internet_gateway" "hoge" {
  vpc_id = "${aws_vpc.hoge.id}"
  tags = {  Name = "hogehoge-gateway" }

}

// ルートテーブルを作成。 RouteTableId は ${aws_route_table.hoge.id} で参照する。ゲートウェイも追加。

resource "aws_route_table" "hoge" {
  vpc_id = "${aws_vpc.hoge.id}"

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = "${aws_internet_gateway.hoge.id}"
  }
  tags = {  Name = "hogehoge-route-table" }
}

// 作ったサブネットに作ったルートテーブルを割り当てる。
resource "aws_route_table_association" "hoge" {
  subnet_id      = "${aws_subnet.hoge.id}"
  route_table_id = "${aws_route_table.hoge.id}"
}

// セキュリティグループの作成。GroupId は ${aws_security_group.hoge.id} で参照。
resource "aws_security_group" "hoge" {
  name        = "hogehoge-security-group"
  description = "hogehoge-security-group"
  vpc_id      = "${aws_vpc.hoge.id}"

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

// いよいよEC2 インスタンスを起動。
resource "aws_instance" "hoge" {
  ami                         = "ami-061392db613a6357b"
  instance_type               = "t2.micro"
  key_name                    = "hogehoge-keypair"
  vpc_security_group_ids      = ["${aws_security_group.hoge.id}"]
  subnet_id                   = "${aws_subnet.hoge.id}"
  associate_public_ip_address = "true"
  tags = {  Name = "hogehoge-instance" }
}
```


### Terraform の実行


Terraform のインストール

    brew install terraform

とあるディレクトリで最初だけ初期化する。

    terraform init

設定を反映させます。

    terraform apply

次回からは、.tf ファイルを編集して再度 `terraform apply` すれば変更点だけが更新されます。

### 全部削除

    terraform destroy

滅茶苦茶簡単。

### 参考

* [Getting Started](https://learn.hashicorp.com/terraform/getting-started/install.html)
* [aws_vpc](https://www.terraform.io/docs/providers/aws/r/vpc.html)
* [aws_subnet](https://www.terraform.io/docs/providers/aws/r/subnet.html)
* [EC2とVPCを自動化してみたら素晴らしかった](https://blog.adachin.me/archives/4693)

## おまけ: Supported Attributes について

[EC2-Classic](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-classic-platform.html) によれば、AWS EC2 アカウントは二種類あるそうです。

* 2013 年 12 月 4 日より古いアカウント (EC2-Classic)
  * Supported Platforms に EC2 と VPC と表示される
  * 最近のインスタンスタイプを使うためには VPC を自分で作る必要がある。
* それ以外
  * Supported Platforms に VPC、Default VPC にデフォルト VPC が表示される
  * 最近のインスタンスタイプを使う時に、デフォルト VPC を使える。

EC2-Classic の人は標準の Terraform チュートリアルを実行出来ません。