Route 53でレコードを登録する機会があったのでawspecを使ってテストしてみた

最近、仕事で[Route 53](https://aws.amazon.com/jp/route53/)に対して数十件のレコードをAWSマネジメントコンソールの画面から手動で登録したんですが、登録したレコードの中にはMXレコードやSPFレコードが含まれていたため、ミスってたら怖いなぁと思い、以前から気になっていたawspecを触ってみるよい機会だと思い使ってみました。
ということで、ここではawspecを使ってRoute 53のレコードが正しく設定されているかを確認する手順を示します。

# awspecとは？
[awspecのGitHubのページ](https://github.com/k1LoW/awspec)を見ると「RSpec tests for your AWS resources.」とのこと。
RSpecの記法で書けるServerspecのAWS版といったところでしょうか。

# 今回の実行環境
- macOS Catalina バージョン 10.15.5


# 必要なパッケージ
- Ruby
- Bundler

RubyとBundlerを利用するため、実行環境にインストール済みか確認します。

```
$ ruby -v
ruby 2.6.6p146 (2020-03-31 revision 67876) [x86_64-darwin19]
$ bundler -v
Bundler version 2.2.16
```

# 事前準備
awspecではAWSの各リソースの読み取り権限のポリシーが付与されたIAMユーザーが必要です。
[別の記事](https://qiita.com/akase244/items/95f99500aa020ffc7d0d)で紹介した[「SecurityAudit」というIAMポリシー](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies_job-functions.html#jf_security-auditor)を利用することでAWSの多くのリソースにアクセスすることが可能ですが、今回はRoute 53に対してのみ読み取り権限があれば十分なので、「AmazonRoute53ReadOnlyAccess」のポリシーを付与してIAMユーザーを作成しました。

ユーザー名は「Route53ReadOnly」としました。マネジメントコンソールでは利用しないため、「プログラムによるアクセス」にのみチェックしておきます。
![スクリーンショット 2021-04-15 9.55.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23093/572bde95-255b-8586-3a85-10ac68d523bf.png)

「既存のポリシーを直接アタッチ」を選択して、「AmazonRoute53ReadOnlyAccess」にチェックします。
![スクリーンショット 2021-04-15 9.55.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23093/26839842-e033-52aa-41f2-493f30ab086e.png)

「Name」というキー名に「Route53ReadOnly」を設定します。（このステップはスキップしてもOKです）
![スクリーンショット 2021-04-15 9.55.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23093/75319c66-6b4f-953e-127d-2dfcb7f3883e.png)

確認画面で内容がOKだったら作成します。
![スクリーンショット 2021-04-15 9.56.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23093/35188205-2e8f-1b7d-3fff-4a5382dd2dbb.png)

上記手順で作成したIAMユーザーの認証情報はあとの手順で利用するので控えておきます。

```
$ cat ~/Downloads/new_user_credentials.csv
User name,Password,Access key ID,Secret access key,Console login link
Route53ReadOnly,,XXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,https://XXXXXXXXXXXX.signin.aws.amazon.com/console
```

# awspecのインストール

インストール作業用のディレクトリを作成し、そのディレクトリ配下にGemfileを作成します。

```
$ mkdir awspec_r53
$ cd awspec_r53 
$ bundle init
Writing new Gemfile to /PATH_TO/awspec_r53/Gemfile
$ cat <<EOF >> Gemfile
gem "awspec"
gem "rake"
EOF
```

カレントディレクトリ配下にインストールするために「bundle config set --local path」を実行した後に「 bundle install」を行います。

```
$ bundle config set --local path 'vendor/bundle'
$ cat .bundle/config 
---
BUNDLE_PATH: "vendor/bundle"
$ bundle install
Fetching rake 13.0.3
Installing rake 13.0.3
・
・
・
Bundle complete! 2 Gemfile dependencies, 290 gems now installed.
Bundled gems are installed into `./vendor/bundle`
```

以前は「bundle install --path」のように実行していたと思いますが、この手順は非推奨になっているようです。

```
$ bundle install --path vendor/bundle
[DEPRECATED] The `--path` flag is deprecated because it relies on being remembered across bundler invocations, which bundler will no longer do in future versions. Instead please use `bundle config set --local path 'vendor/bundle'`, and stop using this flag
```

# awspecの初期設定

awspecの初期設定を行うために、「bundle exec awspec init」でawspecのファイル群を生成します。
今回はawspecをローカル環境にインストールしたので、「bundle exec awspec init」を実行します。グローバルにインストールした場合は「awspec init」でよいはずです。

```
$ bundle exec awspec init
 + spec/
 + spec/spec_helper.rb
 + Rakefile
 + spec/.gitignore
 + .rspec
```

事前に控えておいたIAMユーザーの認証情報を以下のように入力し、「spec/secrets.yml」ファイルを作成します。

```
$ cat <<EOF > spec/secrets.yml
region: ap-northeast-1
aws_access_key_id: XXXXXXXXXXXXXXXXXXXX
aws_secret_access_key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
EOF
```

これで準備が整いました。

# specファイルの書き方について

Route 53のホストゾーンに対応するspecファイルの書き方については、[ドキュメントがあります](https://github.com/k1LoW/awspec/blob/v1.23.0/doc/_resource_types/route53_hosted_zone.md)ので事前に目を通しておきましょう。

Route 53で登録可能なレコードタイプには以下のようなものがあります。
![スクリーンショット 2021-04-18 9.03.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23093/f84115ff-51a0-441d-ad9f-d880417bf612.png)

「awspec/lib/awspec/matcher/have_record_set.rb」の[以下のコード](https://github.com/k1LoW/awspec/blob/v1.23.0/lib/awspec/matcher/have_record_set.rb#L6)を見ると、対応しているレコードタイプを確認できます。

```
  %w(soa a txt ns cname mx ptr srv spf aaaa caa).each do |type|
    chain type do |value|
      @type = type
      @value = value
      @options = {} if @options.nil?
    end
  end
```

# ホストゾーンの作成

本番環境で実際に登録したレコード群をここで例示することはできないので、今回は個人で管理しているドメインを元に一時的にRoute 53のホストゾーンとして登録し、テストを書いてみようと思います。

Route 53の画面で「fifty-four.rocks」をホストゾーンとして登録します。

![スクリーンショット 2021-04-18 9.19.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23093/d8bb9e4f-3968-4992-831e-d305556f1974.png)

初期状態ではNSレコードとSOAレコードが作成されています。

![スクリーンショット 2021-04-18 9.21.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23093/f36f99fd-77f5-f2c2-ff1d-973b3c276a0e.png)

# specファイルの作成と実行

上記の初期状態のレコードに対してテストを作成します。
まず、「fifty-four.rocks」のホストゾーンが存在しているかを確認します。
次に初期状態のレコード数は2レコードなので、カウントが合致しているかを確認します。
NSレコードはデフォルトで4行登録されているので、ここでは配列の形式で定義し、引数に渡す際に改行を区切り文字として指定しています。
SOAレコードは文字列なので、そのまま引数に渡します。

```
$ cat spec/route53_spec.rb
require 'spec_helper'

describe route53_hosted_zone('fifty-four.rocks.') do
  it { should exist }

  its(:resource_record_set_count) { should eq 2 }

  ns = [
    'ns-1931.awsdns-49.co.uk.',
    'ns-296.awsdns-37.com.',
    'ns-902.awsdns-48.net.',
    'ns-1286.awsdns-32.org.',
  ]
  it { should have_record_set('fifty-four.rocks.').ns(ns.join("\n")).ttl(172800) }

  soa = 'ns-1931.awsdns-49.co.uk. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400'
  it { should have_record_set('fifty-four.rocks.').soa(soa).ttl(900) }
end
```

それではテストを実行してみます。
今回はrakeをローカル環境にインストールしたので、「bundle exec rake spec」を実行します。グローバルにインストールした場合は「rake spec」でよいはずです。

```
$ bundle exec rake spec
route53_hosted_zone 'fifty-four.rocks.'
  is expected to exist
  is expected to have record set "fifty-four.rocks."
  is expected to have record set "fifty-four.rocks."
  resource_record_set_count
    is expected to eq 2

Finished in 1.8 seconds (files took 2.95 seconds to load)
4 examples, 0 failures
```

4件のテストがすべて成功しました。

次にfifty-four.rocksのAレコードをRoute 53に登録します。
その前に、現状のAレコードをdigコマンドで確認してみましょう。

```
$ dig fifty-four.rocks a +short    
3.0.239.142
104.248.158.121
```

上記の2レコードが返ってきましたので、このテストをspecファイルに追加します。変更箇所は以下の通りです。

```
cat spec/route53_spec.rb 
require 'spec_helper'

describe route53_hosted_zone('fifty-four.rocks.') do
  ・
  ・
  ・
  its(:resource_record_set_count) { should eq 3 }
  ・
  ・
  ・
  a = [
    '3.0.239.142',
    '104.248.158.121',
  ]
  it { should have_record_set('fifty-four.rocks.').a(a.join("\n")).ttl(300) }
  
end
```

テストを実行して、失敗することを確認します。

```
$ bundle exec rake spec      
route53_hosted_zone 'fifty-four.rocks.'
  is expected to exist
  is expected to have record set "fifty-four.rocks."
  is expected to have record set "fifty-four.rocks."
  is expected to have record set "fifty-four.rocks."
  resource_record_set_count
    is expected to eq 3 (FAILED - 1)

Failures:

  1) route53_hosted_zone 'fifty-four.rocks.' resource_record_set_count is expected to eq 3
     Failure/Error: its(:resource_record_set_count) { should eq 3 }
     
       expected: 3
            got: 2
     
       (compared using ==)
     # ./spec/route53_spec.rb:6:in `block (2 levels) in <top (required)>'

Finished in 1.76 seconds (files took 4.06 seconds to load)
5 examples, 1 failure

Failed examples:

rspec ./spec/route53_spec.rb:6 # route53_hosted_zone 'fifty-four.rocks.' resource_record_set_count is expected to eq 3
```

Route 53にAレコードを登録します。
![スクリーンショット 2021-04-18 9.44.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23093/e97a98b8-2cb9-a601-2cb8-371a68707fa4.png)

テストを再度実行します。

```
$ bundle exec rake spec

route53_hosted_zone 'fifty-four.rocks.'
  is expected to exist
  is expected to have record set "fifty-four.rocks."
  is expected to have record set "fifty-four.rocks."
  is expected to have record set "fifty-four.rocks."
  resource_record_set_count
    is expected to eq 3

Finished in 1.56 seconds (files took 4.11 seconds to load)
5 examples, 0 failures
```

５件のテストがすべて成功しました。

こういった感じで、後は「テストを作成 → レコードを登録 → テストを実行」を繰り返すことで安心してDNSの設定変更作業を行うことができるのではないでしょうか。

# 参考URL
- [awspec](https://github.com/k1LoW/awspec)
- [awspecを活用して、AWS Route53でのサブドメイン追加業務を堅実に実施する](https://qiita.com/k1LoW/items/b7ba289a235525ec00b9)
- [awspecでAWSリソースをテストする](https://dev.classmethod.jp/articles/use-awspec-to-test-aws-resrouces/)
- [bundler(1.17.2)で--path=vendor/bundleつけたら警告出た話](https://blog.mothule.com/ruby/bundler/ruby-bundler-warning-path-option)