【AWS SDK】EC2の自動構築

##目標
AWS SDK（AWS SDK for Ruby）を利用してEC2自動構築を行う。

##はじめに
AWS SDKの基礎知識をまとめた後、実際の動作をAWS SDK for Rubyを利用したEC2自動構築で試してみます。

##AWS SDK
AWSが提供しているAPIの一種で、AWSがサポートする様々なプログラミング言語にライブラリとしてインポートして利用します。
現在以下の言語がサポートされており、各種プログラムからのAWSリソース操作を可能にします。

・C++
・Go
・Java
・JavaScript
・.NET
・Node.js
・PHP
・Python
・**Ruby（本記事ではこれを利用）**

##AWS CLI、SDK利用時の認証
AWS CLI（OSコマンドライン上やシェルスクリプト、PowerShell上で利用）、AWS SDK（各種プログラミング言語上で利用）を利用する際には認証情報が必要となります。
認証情報の設定方法には以下3つの方法が存在しており、参照優先度が異なります。

| 項番| 優先度 | 認証情報の設定箇所 |
|:----:|:----:|:-------------|
| 1 | 高 | OS環境変数 |
| 2| 中 | 認証情報ファイル|
| 3| 低 | インスタンスプロファイル（IAMロール認証）|

①OS環境変数
環境変数のAWS_ACCESS_KEY_ID および AWS_SECRET_ACCESS_KEYという環境変数を利用して認証情報の設定が可能です。
認証設定方法の中で最も優先度が高いです。
例えば以下のように設定を行います。

```bash

# For LINUX
export AWS_ACCESS_KEY_ID=your_access_key_id
export AWS_SECRET_ACCESS_KEY=your_secret_access_key

# For Windows
set AWS_ACCESS_KEY_ID=your_access_key_id
set AWS_SECRET_ACCESS_KEY=your_secret_access_key
```

②認証情報ファイル
OSユーザのホームディレクトリ内の「aws」ディレクトリに存在する「credentials」というファイルが認証情報ファイルです。
そのファイル内のaws_access_key_idとaws_secret_access_keyにアクセスキーとシークレットアクセスキーを投入します。

```bash

# 認証情報ファイル
~/.aws/credentials
```

```bash

[default]
aws_access_key_id = your_access_key_id
aws_secret_access_key = your_secret_access_key
```

③インスタンスプロファイル
インスタンスプロファイルとは、IAMロールを利用した認証で利用される実行環境ことです。
IAMロール作成時に自動作成されます。
IAMロールを納めるための容器であり、EC2にアタッチする時に必要なコネクターの役割をするようです。
**IAMロールを利用するこの認証方法はAWS SDK及びAWS CLI利用時のベストプラクティスであり、アクセスキーとシークレットアクセスキーを利用する上記2つの方法より認証情報漏洩のリスクが軽減されるため推奨されております。**
ちなみに、認証方法としての優先度が最も低いため、環境変数や認証情報ファイル内の情報に注意を払う必要があります。

インスタンスプロファイルの詳細に関しては、以下サイトが参考になりました。
[EC2にIAMRole情報を渡すインスタンスプロファイルを知っていますか？](https://dev.classmethod.jp/articles/do_you_know_iaminstanceprofile/)
##作業の流れ
| 項番 | タイトル |
|:----:|:-------------|
| 1 | [AWS SDK for Rubyのセットアップ](#1aws-sdk-for-rubyのセットアップ) |
| 2 | [EC2自動構築用スクリプトのセットアップ](#2ec2自動構築用スクリプトのセットアップ)|
| 3 | [動作検証](#3動作検証)|

##手順
###1.AWS SDK for Rubyのセットアップ
**①AWS SDK for Rubyインストール**
かなり時間かかるので注意…

```bash

gem install aws-sdk
```

**②アクセスキーとシークレットアクセスキーの発行**
アクセスキーとシークレットアクセスキーが未発行の場合は新規作成する必要があります。
手順は以下を参考
[【AWS CLI】Red Hat Enterprise Linux 8でAWS CLIを使用可能にする（3. アクセスキーIDとシークレットアクセスキーの発行）](https://qiita.com/aWdfcfG2jLr73pe/items/688d183ae6e1d678ea84#3-%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%82%AD%E3%83%BCid%E3%81%A8%E3%82%B7%E3%83%BC%E3%82%AF%E3%83%AC%E3%83%83%E3%83%88%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%82%AD%E3%83%BC%E3%81%AE%E7%99%BA%E8%A1%8C)

**③認証情報ファイルの編集**
今回はAWS SDKの認証情報を認証情報ファイル内に設定する形で保存します。
認証情報ファイル`~/.aws/credentials`内の`aws_access_key_id`と`aws_secret_access_key`にアクセスキーとシークレットアクセスキーを記載してください。

```bash

# 認証情報ファイル編集
vi ~/.aws/credentials
```

```bash

[default]
aws_access_key_id = your_access_key_id
aws_secret_access_key = your_secret_access_key
```

###2.EC2自動構築用スクリプトのセットアップ
**①EC2自動構築用スクリプトをAWS SDKをセットアップした環境に配備**
スクリプト内の各種環境依存の変数（`<ami_id>`、`<keypair_name>`、`<security_group_id>`、`<instance_type>`、`<az_name>`、`<subnet_id>`、`<userdata_pass>`）は環境に従って書き換えをします。

また、`<userdata_pass>`に指定したファイルの中にはEC2に設定したいユーザデータ処理を書いておきます。

```

ファイル名：　ec2_create.rb
```

```bash

# **********************************************************************************
# 機能概要： EC2の自動構築
# スクリプト用法： ruby <スクリプトパス> <インスタンス名>
# **********************************************************************************

unless ARGV.size() == 1
  puts "The number of arguments is incorrect."
  exit
end

require 'aws-sdk'
require 'base64'

# EC2の構成要素定義（以下変数を環境に応じて編集）
image_id = '<ami_id>'                         # AMIID
key_name = '<keypair_name>'                   # キーペア名
security_group_ids = '<security_group_id>'    # セキュリティグループID
instance_type = '<instance_type>'             # インスタンスタイプ
availability_zone = '<az_name>'               # 利用AZ
subnet_id = '<subnet_id>'                     # 利用サブネットID
user_data = '<userdata_pass>'                 # ユーザデータのファイルパス

# ユーザデータ設定
if File.exist?(user_data)
  file = File.open(user_data)
  script = file.read
  file.close
else
  script = ''
end
encoded_script = Base64.encode64(script)

# EC2リソース操作用インスタンス作成
ec2 = Aws::EC2::Resource.new

# EC2作成実施
instance = ec2.create_instances({
  image_id: image_id,
  min_count: 1,
  max_count: 1,
  key_name: key_name,
  security_group_ids: [security_group_ids],
  user_data: encoded_script,
  instance_type: instance_type,
  placement: {
    availability_zone: availability_zone
  },
  subnet_id: subnet_id
})

# インスタンスが利用可能になるまで待機
ec2.client.wait_until(:instance_running, {instance_ids: [instance[0].id]})

# インスタンス名（Nameタグ）を付与
instance_name = ARGV[0]
instance.batch_create_tags({ tags: [{ key: 'Name', value: instance_name }]})

puts "#{instance_name}（#{instance[0].id}）が作成されました！"
```

###3.動作検証
**＜前提＞**
以下のパラメータでEC2を作成します。

```bash

# EC2の構成要素定義（以下変数を環境に応じて編集）
image_id = 'ami-067152a7c26866dcb'        # AMIID
key_name = 'mykeypair'                    # キーペア名
security_group_ids = 'sg-64a59718'        # セキュリティグループID
instance_type = 't2.medium'               # インスタンスタイプ
availability_zone = 'ap-northeast-1a'     # 利用AZ
subnet_id = 'subnet-41d23b09'             # 利用サブネットID
user_data = '/tmp/userdata'               # ユーザデータのファイルパス
```

また、EC2に設定予定のユーザデータは以下の内容

```bash

$ cat /tmp/userdata
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
```



**①スクリプト実行**
インスタンス名を第一引数に指定してスクリプト実行します。

```bash

$ ruby ec2_create.rb test_server
test_server（i-0a4fd9cfe1613a8d6）が作成されました！
```

**②結果確認**
インスタンス名、インスタンスタイプ、サブネットID、キーペア名、AZ名、AMIIDがスクリプト内での設定値通り設定されています。
![tempsnip.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647616/d49759af-5778-5ed2-b4f0-63dd0e3f47f9.png)

付与されているセキュリティグループもOK
![tempsnip.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647616/4627dcaa-49f5-b9fd-3527-8077c6e3dd44.png)

ユーザデータも内容適切でした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647616/d13a22ea-1801-aff0-0f79-97e991ca4418.png)

一応OSログイン確認と、ユーザデータが正常に実行されているかも確認してみます。

```bash

# httpdがユーザデータ実行により起動している
$ systemctl status httpd
● httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled)
   Active: active (running) since Sat 2020-07-11 03:03:01 UTC; 12min ago
     Docs: man:httpd.service(8)
 Main PID: 21821 (httpd)
   Status: "Total requests: 0; Idle/Busy workers 100/0;Requests/sec: 0; Bytes served/sec:   0 B/sec"
   CGroup: /system.slice/httpd.service
           tq21821 /usr/sbin/httpd -DFOREGROUND
           tq21831 /usr/sbin/httpd -DFOREGROUND
           tq21832 /usr/sbin/httpd -DFOREGROUND
           tq21833 /usr/sbin/httpd -DFOREGROUND
           tq21834 /usr/sbin/httpd -DFOREGROUND
           mq21835 /usr/sbin/httpd -DFOREGROUND

Jul 11 03:03:01 ip-172-31-40-241.ap-northeast-1.compute.internal systemd[1]: Starting The Apache HTTP Server...
Jul 11 03:03:01 ip-172-31-40-241.ap-northeast-1.compute.internal systemd[1]: Started The Apache HTTP Server.

# また、enabledにもなっている
$ systemctl is-enabled httpd
enabled
```

全て設定値通りに作成されているのでOKとします！

##参考サイト
各種サービスにおけるAWS SDK For Rubyを利用した実装例が確認できます。
[AWS SDK for Ruby コード例](https://docs.aws.amazon.com/ja_jp/sdk-for-ruby/v3/developer-guide/examples.html)