LoginSignup
10

More than 5 years have passed since last update.

AnsibleでRoute53を管理する

Last updated at Posted at 2017-06-06

はじめに

AnsibleにはAWSのリソースを操作できるモジュールが豊富に用意されています。

今回は、CLIだと何かと心が折れるRoute53をAnsibleで管理してみます。

やること

  • パブリックゾーン作成/削除
  • プライベートゾーン作成/削除
  • Aレコード他各種レコード作成/削除
  • ALIASレコード作成/削除

ポイント

ELBについては、DNS名を自動取得してALIASレコードを登録するようにします。
レコード/ゾーンの削除についてはenable: noを設定することで実現します。
未定義だからといって既存レコードを削除しないようにします。

前提

  • AWS関連のモジュール実行にはbotoが必要です。
  • credential情報は環境変数かaws configureでセットしてある必要があります。

下記リソースを前提に進めます。

  • VPC
    • AnsibleVPC

※プライベートゾーン作成時に必要

注意

ALIASレコード作成時のhosted_zone_idについては、リソースごとに固定となっています。
http://docs.aws.amazon.com/ja_jp/general/latest/gr/rande.html

sample

以下のようなDNSゾーンおよびレコードを作成します。

  • public
    • testdomain.com
      • www.testdomain.com (Aレコード)
      • text.testdomain.com (TXTレコード)
      • cname.testdomain.com (CNAMEレコード)
      • elb.testdomain.com (ALIASレコード:ELB)
      • s3.testdomain.com (ALIASレコード:s3)
  • private
    • testdomain.local
      • test.testdomain.local(Aレコード:複数IPアドレス)

ディレクトリ構成

ディレクトリ構成
site.yml
roles/
|--route53/
|  |--tasks/
|  |  |--main.yml
hosts/aws    #inventory
host_vars/
|--localhost.yml

inventory

AWSリソース関連モジュールはすべてlocalhostで実行するので、下記のようなインベントリファイルを用意します。

hosts/aws
[aws]
localhost

vars

こんな感じに変数を定義します。今回はhost_varsで定義しました。

host_vars/localhost.yml
---
my_vars:
  aws:
    common:
      region: ap-northeast-1
    vpc:
      name: AnsibleVPC    # ターゲットのVPC名
    route53:
      - name: testdomain.com
        enable: yes       # デフォルトはyes。noを指定すると削除になる
        description: public zone
        records:
          - record: testdomain.com #レコード名がドメイン名と同じになる場合はドメイン名を指定する
            record_values:
              - 10 mail.testdomain.com
            ttl: 300
            type: MX
          - record: www
            type: A       # 指定なしだとAレコード
            ttl: 7200     # 指定なしだと300
            record_values:
              - 10.1.1.1
          - record: text
            type: TXT
            record_values:
              - '"var"'   # このようにクォートする
          - record: cname
            type: CNAME
            record_values:
              - www.example.com
          - record: elb
            alias: yes    # ALIASレコードの場合
            target: elb   # ELBを指定するとrecord_valuesのELB名からDNS名を取得
            record_values:
              - testelb
            hosted_zone_id: Z14GRHDCWA56QT #固定値(ELB)
          - record: s3
            alias: yes
            record_values:
              - s3-website-ap-northeast-1.amazonaws.com
            hosted_zone_id: Z2M4EHUR26P7ZW #固定値(s3)
          - record: cloudfront
            alias: yes
            record_values:
              - XXXX.cloudfront.net
            hosted_zone_id: Z2FDTNDATAQYW2 #固定値(CloudFront)
      - name: testdomain.local
        description: private zone
        private: yes      # プライベートゾーン
        records:
          - record: test
            type: A
            record_values:
              - 192.168.1.2 # 複数指定する場合
              - 192.168.1.3
              - 192.168.1.4
            enable: yes   # デフォルトはyes。noを指定すると削除になる
          - record: test2
            type: A
            record_values:
              - 192.168.1.5
            enable: no    # 削除する場合

Role

Route53はリージョン、VPCに依存しませんが、プライベートゾーン作成にはVPC情報が必要になります。
そのためのVPCを特定するためにはidが必要ですが、こちらと同様、VPC名でidを取得します。

ec2_elb_factsモジュールでELBのエイリアスレコード作成で必要となるELBのDNS名を取得し、ELB名からDNS名を参照するためにディクショナリを生成します。

roles/route53/tasks/main.yml
---
- name: vpc_id取得
  ec2_vpc_net_facts:
    region: "{{ my_vars.aws.common.region }}"
    filters:
      "tag:Name": "{{ my_vars.aws.vpc.name }}"
  register: vpc_net_fact
  check_mode: no

- name: Zone作成
  route53_zone:
    zone: "{{ item.name }}"
    state: >-
      {%- if item.enable is defined and item.enable == False -%}
        absent
      {%- else -%}
        present
      {%- endif -%}
    vpc_id: >-
      {%- if item.private is defined and item.private == True -%}
        {{ vpc_net_fact.vpcs[0].id }}
      {%- else -%}
        {{ omit }}
      {%- endif -%}
    vpc_region: >-
      {%- if item.private is defined and item.private == True -%}
        {{ my_vars.aws.common.region }}
      {%- else -%}
        {{ omit }}
      {%- endif -%}
    comment: "{{ item.description | default(omit) }}"
  register: route53_zone
  with_items: "{{ my_vars.aws.route53 }}"
  when: item.name is defined

- debug: var=route53_zone

- name: elb情報収集
  ec2_elb_facts:
    region: "{{ my_vars.aws.common.region }}"
    names: "{{ item.1.record_values }}"
  register: elb_facts
  with_subelements:
    - "{{ my_vars.aws.route53 }}"
    - records
  when: item.1.target is defined and item.1.target == "elb"
  check_mode: no

- name: elb dict作成
  set_fact:
    elb_dict: >-
      {%- set dict = {} -%}
      {%- for i in range(elb_facts.results|length) -%}
      {%-   if elb_facts.results[i].elbs is defined -%}
      {%-     set _ = dict.update({elb_facts.results[i].elbs[0].name: elb_facts.results[i].elbs[0].dns_name}) -%}
      {%-   endif -%}
      {%- endfor -%}
      {{ dict }}
  when: elb_facts is defined
  check_mode: no

- name: record作成
  route53:
    zone: "{{ item.0.name }}"
    private_zone: "{{ item.0.private | default('False') }}"
    command: >-
      {%- if item.1.enable is defined and item.1.enable == False -%}
        delete
      {%- else -%}
        create
      {%- endif -%}
    overwrite: yes
    record: >-
      {%- if item.1.record == item.0.name -%}
        {{ item.1.record }}
      {%- else -%}
        {{ item.1.record }}.{{ item.0.name }}
      {%- endif -%}
    type: "{{ item.1.type | default('A')}}"
    alias: "{{ item.1.alias | default('False')}}"
    alias_hosted_zone_id: "{{ item.1.hosted_zone_id | default(omit)}}"
    ttl: "{{ item.1.ttl | default('300')}}"
    value: >-
      {%- if item.1.target is defined and item.1.target == "elb" -%}
        {{ elb_dict[item.1.record_values.0] }}
      {%- else -%}
        {{ item.1.record_values | join(',') }}
      {%- endif -%}
  register: route53_record
  with_subelements:
    - "{{ my_vars.aws.route53 }}"
    - records
  when: item.0.enable is undefined or item.0.enable == True

- debug: var=route53_record  

site.yml

site.yml
---
- name: route53
  hosts: localhost
  connection: local
  roles:
    - role: route53

実行

Command
$ ansible-playbook -i hosts/aws -l localhost site.yml

まとめ

Route53のコード化については、RoadWorkerというツールが非常に強力で、以前活用していたのですが、Ansibleでも同様のことができます。
リソース定義もYAMLの方が個人的にはわかりやすいです。

参考

AnsibleでAWSリソースを管理するシリーズ

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10