JAWS-UG CLI専門支部 #155R EC2入門参加まとめ #jawsug_cli

#JAWS-UG CLI専門支部 #155R EC2入門に参加してみて

155回目のハンズオン！もうすぐ6年！
オンラインハンズオンは4/27からスタートして1ヵ月が経ちました。
CLI覚えて転職したメンバーが多いのが支部の特徴！
CLIなれるとマネコン操作がめんどくさくなるらしい→そう言えるようになりたい！
オンラインになって今のところ毎回参加できているので、オンラインっていいなーとしみじみおもう。
シェルは冪等性を担保し易い。マイクロサービスとシェルは考え方が近い。
※冪等性とは何回やっても同じ結果が得られること。

EC2は現在402コマンドあるとのこと！
マネジメントコンソールだと
VPCメニュー、EC2メニュー（AutoScaling、ELBが含まれている）、、
APIだとそれぞれわかれている。
EC2、AutoScaling、ELB(CLB)、ELBv2(ALB)、ebs、ec2-instance-connect
生い立ちが違うのかなぁ

# できるようになったこと
[★教材ページ](http://prototype-handson-cli.s3-website-ap-northeast-1.amazonaws.com/handson_light-aws_service/handson_light-aws_service-ec2-getting_started/index.html)

1.デフォルトVPCの作成（すでにあった）
2.キーペアの作成
3.ユーザデータの設定
4.セキュリティグループの設定（インバウンドルール追加）
5.インスタンス作成
6.インスタンスメタデータの取得
7.インスタンスの破棄
8.セキュリティグループの設定（インバウンドルール削除）
9.キーペアの削除
10.デフォルトVPCの削除

## 1.デフォルトVPCの作成
作るときは1コマンドで済むが削除するときは1個1個削除する必要がある。
デフォルトVPCは決めることが少ないので検証環境を作る場合に最適。
本番環境には適さない。
VPCは1アカウント5個の上限がある。上限緩和はできる。
デフォルトVPCだと--group-nameが使える。カスタムVPCは--group-idが必要。


``` VPCの作成.
aws ec2 create-default-vpc
```

```確認.
aws ec2 describe-vpcs \
  --filters "Name=isDefault,Values=true" \
  --query "Vpcs[].CidrBlock" \
  --output text
```

## 2.キーペアの作成
OSにログオンするときのsshキー。
事前にキーファイルを置くディレクトリを作成して権限修正。
```mkdir -p ${HOME}/.ssh && chmod 700 ${HOME}/.ssh```

```キーペアの作成.
aws ec2 create-key-pair \
--key-name ${EC2_KEY_PAIR_NAME} \
--query 'KeyMaterial' --output text \
> ${FILE_SSH_KEY_SECRET} \
&& cat ${FILE_SSH_KEY_SECRET} \
&& chmod 400 ${FILE_SSH_KEY_SECRET}
```

--key-name \${EC2_KEY_PAIR_NAME}　→ キーの名前を指定（任意）。
--query 'Key Material' --output text　→ queryとoutputを併用することで秘密鍵を出力。
\> ${FILE_SSH_KEY_SECRET} → リダイレクトで秘密鍵をファイルに出力。

```作成後の確認.
aws ec2 describe-key-pairs \
--query "KeyPairs[?KeyName == \`${EC2_KEY_PAIR_NAME}\`].KeyName" \
--output text
```


## 3.ユーザデータの作成
ユーザデータとはインスタンス作成時に変更内容をスクリプトで記述しておくと、OS内部の設定を変更してくれる。
たとえばホスト名やyumアップデートなど。
インスタンス作成時に毎回実施する操作はユーザデータに残しておくとよい。
ヒアドキュメントの中にさらにヒアドキュメントで書くというテクニック！（知らなかった）

```UserDataを出力するファイルを指定.
FILE_USER_DATA="${DIR_USER_DATA}/${USER_DATA_NAME}.bash" \
  && echo ${FILE_USER_DATA}
```

```UserDataの処理を記述.
cat << EOF1 > ${FILE_USER_DATA}
#!/bin/bash 

# configure sshd
cat << EOF >> /etc/ssh/sshd_config

Port ${PORT_SSHD}
EOF
systemctl restart sshd.service

# yum
yum update -y
EOF1

cat ${FILE_USER_DATA}
```
※SSHが使用するポートを変更して、yumアップデートをかける。


## 4.セキュリティグループの設定
セキュリティグループはAWS版ファイアウォール。通信のインバウンド、アウトバンドはだいたいここで制御。
サブネット単位での通信制限はNetworkACL（NACL）。
セキュリティグループはステートフルなので、インバウンドを許可するとアウトバンドも許可される。
NACLはステートレス。インバウンド、アウトバンドそれぞれ設定が必要。

```インバウンドルール許可.
aws ec2 authorize-security-group-ingress \
  --group-name ${EC2_SECURITY_GROUP_NAME} \
  --protocol ${EC2_SECURITY_GROUP_PROTOCOL} \
  --port ${EC2_SECURITY_GROUP_PORT} \
  --cidr ${EC2_SECURITY_GROUP_CIDR}
```
authorize-security-group-ingress → 指定したプロトコル、ポート、通信元からのインバウンドルールを許可。
--group-name \${EC2_SECURITY_GROUP_NAME} → セキュリグループ名。
--protocol \${EC2_SECURITY_GROUP_PROTOCOL} → セキュリグループのプロトコル（tcp | udp | ICMP）を指定。
--port \${EC2_SECURITY_GROUP_PORT} → 許可するポート番号を指定。
--cidr \${EC2_SECURITY_GROUP_CIDR} → 許可するIPを指定。

```VPCIDの取得.
EC2_VPC_ID=$( \
  aws ec2 describe-vpcs \
    --filters Name=isDefault,Values=true \
    --query 'Vpcs[].VpcId' \
    --output text \
) \
  && echo ${EC2_VPC_ID}
```

```インバウンドルールの確認.
aws ec2 describe-security-groups \
  --filter Name=vpc-id,Values=${EC2_VPC_ID} \
    Name=group-name,Values=${EC2_SECURITY_GROUP_NAME} \
    Name=ip-permission.protocol,Values=${EC2_SECURITY_GROUP_PROTOCOL} \
    Name=ip-permission.to-port,Values=${EC2_SECURITY_GROUP_PORT} \
    Name=ip-permission.cidr,Values=${EC2_SECURITY_GROUP_CIDR} \
  --query "SecurityGroups[].IpPermissions[?IpProtocol == \`${EC2_SECURITY_GROUP_PROTOCOL}\` \
    && ToPort == \`${EC2_SECURITY_GROUP_PORT}\` \
    && IpRanges[?CidrIp == \`${EC2_SECURITY_GROUP_CIDR}\`]].IpRanges[][].CidrIp" \
  --output text
```
--filterの内容
デフォルトVPC、指定したセキュリティグループ名、指定したプロトコル、指定したポート、指定したソース元IPに合致するセキュリティグループを検索
--queryの内容
指定したポートと指定したソース元IPに合致するIPを抽出

## 5.インスタンス作成


```インスタンス作成.
aws ec2 run-instances \
  --image-id ${EC2_IMAGE_ID} \
  --instance-type ${EC2_INSTANCE_TYPE} \
  --tag-specifications ${STRING_TAG_CONF} \
  --user-data file://${FILE_USER_DATA} \
  --key-name ${EC2_KEY_PAIR_NAME} \
  --associate-public-ip-address
```
--image-id \${EC2_IMAGE_ID} = AMIのIDを指定する。GUIから使いたいAMIのIDを取得しておく。
　※AmazonLinux2無料利用枠の対象AMIはami-0a1c2ec61571737d
--instance-type \${EC2_INSTANCE_TYPE} = インスタンスタイプを指定する。無料利用枠だとt2.micro。
--tag-specifications \${STRING_TAG_CONF} = タグを指定。複数指定する場合は、カンマでつなげる。
　※"ResourceType=instance,Tags=[{Key=Name,Value=handson-cli-ec2-getting_started-instance},{Key=instancetype,Value=t2.micro}]"

--user-data file://\${FILE_USER_DATA} = 作成したUserdataのファイルを指定する。
--key-name ${EC2_KEY_PAIR_NAME} = 作成したキーペア名を指定する。
--associate-public-ip-address = パブリックIPのアサイン。

``` 作成後の確認.
aws ec2 describe-instances \
  --filters Name=tag-key,Values=Name \
            Name=tag-value,Values=${EC2_INSTANCE_TAG_NAME} \
            Name=instance-state-name,Values=running \
  --query Reservations[].Instances[].Tags[].Value \
  --output text
```

```SSHログイン.
ssh ${EC2_PUBLIC_IP} \
  -i ${FILE_SSH_KEY_SECRET} \
  -l ec2-user \
  -p ${PORT_SSHD}
```
-i \${FILE_SSH_KEY_SECRET} = 作成した鍵ファイルを指定。
-l ec2-user = ログインするユーザ名を指定。
-p \${PORT_SSHD} = 使用するポートを指定する。変更している場合に使用。


## 6.インスタンスメタデータの取得
インスタンスメタデータとはインスタンスがもつ情報を検索するための情報。
インスタンスを作成したときのAMIIDや持っているインスタンスタイプの情報を知りたいといったとき、
メタデータにアクセスするとメタデータをキーに実体となる情報（AMIIDやインスタンスタイプ）が得られる。
インスタンスメタデータにはv1とv2がある。
v2はv1に比べてセキュア。セッショントークン、PUTリクエストを利用してメタデータにアクセスすることで
v1よりもセキュアになっている。
サーバーワークスさんの書いたブログがわかりやすかった。
[★InstanceMetaDataV2を分かりやすく解説してみる～Serverworks](http://blog.serverworks.co.jp/tech/2019/11/27/imdsv2/)

[★インスタンスメタデータのカテゴリ～AWS公式](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/instancedata-data-categories.html)


``` トークンの取得.
EC2_METADATA_TOKEN=$( \
  curl -s \
    -X PUT "http://169.254.169.254/latest/api/token" \
    -H "X-aws-ec2-metadata-token-ttl-seconds: ${EC2_METADATA_SECOND}" \
) \
  && echo ${EC2_METADATA_TOKEN}
```
${EC2_METADATA_SECOND} = セッショントークンの有効期限。秒指定。

``` EC2メタデータのヘッダ設定.
EC2_METADATA_HEADER="X-aws-ec2-metadata-token: ${EC2_METADATA_TOKEN}" \
  && echo ${EC2_METADATA_HEADER}
```

``` メタ情報一覧の取得.
curl -H "${EC2_METADATA_HEADER}" \
  http://169.254.169.254/latest/meta-data/
```
``` インスタンスタイプの取得.
EC2_INSTANCE_TYPE=$( \
  curl -s -H "${EC2_METADATA_HEADER}" \
    http://169.254.169.254/latest/meta-data/instance-type \
) \
  && echo ${EC2_INSTANCE_TYPE}
```
```インスタンスIDの取得.
EC2_INSTANCE_ID=$( \
  curl -s -H "${EC2_METADATA_HEADER}" \
    http://169.254.169.254/latest/meta-data/instance-id \
) \
  && echo ${EC2_INSTANCE_ID}
```
```作成時に使用したAMIIDの取得.
EC2_IMAGE_ID=$( \
  curl -s -H "${EC2_METADATA_HEADER}" \
    http://169.254.169.254/latest/meta-data/ami-id \
) \
  && echo ${EC2_IMAGE_ID}
```
```グローバルIPの取得.
EC2_PUBLIC_IP=$( \
  curl -s -H "${EC2_METADATA_HEADER}" \
    http://169.254.169.254/latest/meta-data/public-ipv4 \
) \
  && echo ${EC2_PUBLIC_IP}
```
```所属するAZの取得.
EC2_AZ_NAME=$( \
  curl -s -H "${EC2_METADATA_HEADER}" \
    http://169.254.169.254/latest/meta-data/placement/availability-zone \
) \
  && echo ${EC2_AZ_NAME}
```
```所属するリージョン情報.
EC2_REGION_NAME=$( \
  curl -s -H "${EC2_METADATA_HEADER}" \
    http://169.254.169.254/latest/meta-data/placement/availability-zone \
  | sed -e 's/[a-z]*$//' \
) \
  && echo ${EC2_REGION_NAME}
```
※リージョンのメタデータはないのでAZのメタデータを取得したあとにAZを示すアルファベットをカットする。


## 7.インスタンスの破棄
```インスタンスIDの取得.
ARRAY_EC2_INSTANCE_IDS=$( \
  aws ec2 describe-instances \
    --filters Name=tag-key,Values=Name \
              Name=tag-value,Values=${EC2_INSTANCE_TAG_NAME} \
              Name=instance-state-name,Values=running \
    --query Reservations[].Instances[].InstanceId \
    --output text \
) \
  && echo ${ARRAY_EC2_INSTANCE_IDS}
```
${EC2_INSTANCE_TAG_NAME} = インスタンス名

```インスタンスの削除.
aws ec2 terminate-instances \
  --instance-ids ${ARRAY_EC2_INSTANCE_IDS}
```
実行するとステータスが"shutting-down"に変わる。

```削除後の確認.
aws ec2 describe-instances \
  --filters Name=tag-key,Values=Name \
            Name=tag-value,Values=${EC2_INSTANCE_TAG_NAME} \
            Name=instance-state-name,Values=running \
  --query Reservations[].Instances[].Tags[].Value \
  --output text
```

## 8.セキュリティグループの設定（インバウンドルール削除）
```インバウンドルールの削除.
aws ec2 revoke-security-group-ingress \
  --group-name ${EC2_SECURITY_GROUP_NAME} \
  --protocol ${EC2_SECURITY_GROUP_PROTOCOL} \
  --port ${EC2_SECURITY_GROUP_PORT} \
  --cidr ${EC2_SECURITY_GROUP_CIDR}
```
与える引数はauthorize-security-group-ingressを実行したときと同じ。
--group-name \${EC2_SECURITY_GROUP_NAME} → セキュリグループ名。
--protocol \${EC2_SECURITY_GROUP_PROTOCOL} → セキュリグループのプロトコル（tcp | udp | ICMP）を指定。
--port \${EC2_SECURITY_GROUP_PORT} → 許可するポート番号を指定。
--cidr \${EC2_SECURITY_GROUP_CIDR} → 許可するIPを指定。


## 9.キーペアの削除

```キーペアの削除.
aws ec2 delete-key-pair \
  --key-name ${EC2_KEY_PAIR_NAME}
```
※OS内からも秘密鍵のファイルを削除することを忘れないように。

## 10.デフォルトVPCの削除
作成は1コマンドだが、依存関係があるため削除はそれぞれ消していく必要がある。
ちなみにコンソールから消すときとAWS　CLIから消す場合とでは挙動が異なる。
コンソールからだとインスタンス、VPCピアリング、NATゲートウェイ、インタフェースエンドポイントがない状態であればサブネット、セキュリティグループ、NACL、ルートテーブル、ゲートウェイエンドポイント、IGW、DHCPオプションはAWS側で削除してくれる。
コマンドだと事前に手動で消しておく必要がある。

``` VPCIDの取得.
EC2_VPC_ID=$( \
  aws ec2 describe-vpcs \
    --filters Name=isDefault,Values=true \
    --query 'Vpcs[].VpcId' \
    --output text \
) \
  && echo ${EC2_VPC_ID}
```
``` IgwIDの取得.
EC2_IGW_ID=$( \
  aws ec2 describe-internet-gateways \
    --filters Name=attachment.vpc-id,Values=${EC2_VPC_ID} \
    --query "InternetGateways[].InternetGatewayId" \
    --output text \
) \
  && echo ${EC2_IGW_ID}
```
```IGWのデタッチ.
aws ec2 detach-internet-gateway \
  --internet-gateway-id ${EC2_IGW_ID} \
  --vpc-id ${EC2_VPC_ID}
```
``` IGWの削除.
aws ec2 delete-internet-gateway \
  --internet-gateway-id ${EC2_IGW_ID}
```

``` サブネットIDの取得.
ARRAY_EC2_SUBNET_IDS=$( \
  aws ec2 describe-subnets \
    --filters Name=vpcId,Values=${EC2_VPC_ID} \
    --query 'Subnets[].SubnetId' \
    --output text \
) \
  && echo ${ARRAY_EC2_SUBNET_IDS}
```
``` サブネット削除.
for i in ${ARRAY_EC2_SUBNET_IDS}; do
  aws ec2 delete-subnet \
    --subnet-id ${i}
done
```
サブネットの数分ループで回して削除する

``` デフォルトVPCの削除.
aws ec2 delete-vpc \
  --vpc-id ${EC2_VPC_ID}
```