はじめに
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)
- testdomain.com
- private
- testdomain.local
- test.testdomain.local(Aレコード:複数IPアドレス)
- testdomain.local
ディレクトリ構成
site.yml
roles/
|--route53/
| |--tasks/
| | |--main.yml
hosts/aws #inventory
host_vars/
|--localhost.yml
inventory
AWSリソース関連モジュールはすべてlocalhostで実行するので、下記のようなインベントリファイルを用意します。
[aws]
localhost
vars
こんな感じに変数を定義します。今回はhost_varsで定義しました。
---
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名を参照するためにディクショナリを生成します。
---
- 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
---
- name: route53
hosts: localhost
connection: local
roles:
- role: route53
実行
$ ansible-playbook -i hosts/aws -l localhost site.yml
まとめ
Route53のコード化については、RoadWorkerというツールが非常に強力で、以前活用していたのですが、Ansibleでも同様のことができます。
リソース定義もYAMLの方が個人的にはわかりやすいです。