Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement tags support for AWS Route 53 objects #565

Merged
merged 7 commits into from
Oct 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/565-route53-tagging.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
minor_changes:
- route53_zone - add support for tagging Route 53 zones (https://github.com/ansible-collections/community.aws/pull/565).
61 changes: 61 additions & 0 deletions plugins/module_utils/route53.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# This file is part of Ansible
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function
__metaclass__ = type

try:
import botocore
except ImportError:
pass # caught by AnsibleAWSModule

from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code
from ansible_collections.amazon.aws.plugins.module_utils.tagging import ansible_dict_to_boto3_tag_list
from ansible_collections.amazon.aws.plugins.module_utils.tagging import boto3_tag_list_to_ansible_dict
from ansible_collections.amazon.aws.plugins.module_utils.tagging import compare_aws_tags


def manage_tags(module, client, resource_type, resource_id, new_tags, purge_tags):
old_tags = get_tags(module, client, resource_type, resource_id)
tags_to_set, tags_to_delete = compare_aws_tags(old_tags, new_tags, purge_tags=purge_tags)

change_params = dict()
if tags_to_set:
change_params['AddTags'] = ansible_dict_to_boto3_tag_list(tags_to_set)
if tags_to_delete:
change_params['RemoveTagKeys'] = tags_to_delete

if not change_params:
return False

if module.check_mode:
return True

try:
client.change_tags_for_resource(
ResourceType=resource_type,
ResourceId=resource_id,
**change_params
)
except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
module.fail_json_aws(e, msg='Failed to update tags on {0}'.format(resource_type),
resource_id=resource_id, change_params=change_params)
return True


def get_tags(module, client, resource_type, resource_id):
try:
tagset = client.list_tags_for_resource(
ResourceType=resource_type,
ResourceId=resource_id,
)
except is_boto3_error_code('NoSuchHealthCheck'):
return {}
except is_boto3_error_code('NoSuchHostedZone'): # pylint: disable=duplicate-except
return {}
except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: # pylint: disable=duplicate-except
module.fail_json_aws(e, msg='Failed to fetch tags on {0}'.format(resource_type),
resource_id=resource_id)

tags = boto3_tag_list_to_ansible_dict(tagset['ResourceTagSet']['Tags'])
return tags
46 changes: 45 additions & 1 deletion plugins/modules/route53_zone.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from __future__ import absolute_import, division, print_function
__metaclass__ = type


DOCUMENTATION = '''
module: route53_zone
short_description: add or delete Route53 zones
Expand Down Expand Up @@ -47,6 +46,18 @@
- The reusable delegation set ID to be associated with the zone.
- Note that you can't associate a reusable delegation set with a private hosted zone.
type: str
tags:
description:
- A hash/dictionary of tags to add to the new instance or to add/remove from an existing one.
type: dict
badnetmask marked this conversation as resolved.
Show resolved Hide resolved
version_added: 2.1.0
purge_tags:
description:
- Delete any tags not specified in the task that are on the zone.
This means you have to specify all the desired tags on each task affecting a zone.
default: false
type: bool
badnetmask marked this conversation as resolved.
Show resolved Hide resolved
version_added: 2.1.0
extends_documentation_fragment:
- amazon.aws.aws
- amazon.aws.ec2
badnetmask marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -77,6 +88,21 @@
zone: example.com
comment: reusable delegation set example
delegation_set_id: A1BCDEF2GHIJKL

- name: create a public zone with tags
community.aws.route53_zone:
zone: example.com
comment: this is an example
tags:
Owner: Ansible Team

- name: modify a public zone, removing all previous tags and adding a new one
community.aws.route53_zone:
zone: example.com
comment: this is an example
tags:
Support: Ansible Community
purge_tags: true
'''

RETURN = '''
Expand Down Expand Up @@ -115,10 +141,16 @@
returned: for public hosted zones, if they have been associated with a reusable delegation set
type: str
sample: "A1BCDEF2GHIJKL"
tags:
description: tags associated with the zone
returned: when tags are defined
type: dict
'''

import time
from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule
from ansible_collections.community.aws.plugins.module_utils.route53 import manage_tags
from ansible_collections.community.aws.plugins.module_utils.route53 import get_tags

try:
from botocore.exceptions import BotoCoreError, ClientError
Expand Down Expand Up @@ -150,6 +182,8 @@ def create(module, client, matching_zones):
vpc_region = module.params.get('vpc_region')
comment = module.params.get('comment')
delegation_set_id = module.params.get('delegation_set_id')
tags = module.params.get('tags')
purge_tags = module.params.get('purge_tags')

if not zone_in.endswith('.'):
zone_in += "."
Expand All @@ -171,6 +205,14 @@ def create(module, client, matching_zones):
else:
changed, result = create_or_update_public(module, client, matching_zones, record)

zone_id = result.get('zone_id')
if zone_id:
if tags is not None:
changed |= manage_tags(module, client, 'hostedzone', zone_id, tags, purge_tags)
result['tags'] = get_tags(module, client, 'hostedzone', zone_id)
else:
result['tags'] = tags

return changed, result


Expand Down Expand Up @@ -394,6 +436,8 @@ def main():
comment=dict(default=''),
hosted_zone_id=dict(),
delegation_set_id=dict(),
tags=dict(type='dict'),
purge_tags=dict(type='bool', default=False),
)

mutually_exclusive = [
Expand Down
6 changes: 6 additions & 0 deletions tests/integration/targets/route53/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,15 @@
route53_zone:
zone: '{{ zone_one }}'
comment: 'Created in Ansible test {{ resource_prefix }}'
tags:
TestTag: '{{ resource_prefix }}.z1'
register: z1
- assert:
that:
- z1 is success
- z1 is changed
- "z1.comment == 'Created in Ansible test {{ resource_prefix }}'"
- "z1.tags.TestTag == '{{ resource_prefix }}.z1'"

- name: 'Get zone details'
route53_info:
Expand All @@ -53,12 +56,15 @@
vpc_id: '{{ vpc.vpc.id }}'
vpc_region: '{{ aws_region }}'
comment: Created in Ansible test {{ resource_prefix }}
tags:
TestTag: '{{ resource_prefix }}.z2'
register: z2
- assert:
that:
- z2 is success
- z2 is changed
- "z2.comment == 'Created in Ansible test {{ resource_prefix }}'"
- "z2.tags.TestTag == '{{ resource_prefix }}.z2'"

- name: Get zone details
route53_info:
Expand Down
47 changes: 45 additions & 2 deletions tests/integration/targets/route53_zone/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,18 @@
zone: "{{ resource_prefix }}.public"
comment: original comment
state: present
tags:
TestTag: "{{ resource_prefix }}"
another_tag: "{{ resource_prefix }} again"
register: output

- assert:
that:
- output.changed
- output.comment == 'original comment'
- output.name == '{{ resource_prefix }}.public.'
- output.tags.TestTag == '{{ resource_prefix }}'
- output.tags.another_tag == '{{ resource_prefix }} again'
- not output.private_zone

# ============================================================
Expand All @@ -42,6 +47,9 @@
zone: "{{ resource_prefix }}.check.public"
comment: original comment
state: present
tags:
TestTag: "{{ resource_prefix }}"
badnetmask marked this conversation as resolved.
Show resolved Hide resolved
another_tag: "{{ resource_prefix }} again"
register: output
check_mode: yes

Expand All @@ -50,6 +58,8 @@
- output.changed
- output.comment == 'original comment'
- output.name == '{{ resource_prefix }}.check.public.'
- output.tags.TestTag == '{{ resource_prefix }}'
- output.tags.another_tag == '{{ resource_prefix }} again'
- not output.private_zone

# ============================================================
Expand All @@ -58,20 +68,28 @@
zone: "{{ resource_prefix }}.public"
comment: original comment
state: present
tags:
TestTag: "{{ resource_prefix }}"
another_tag: "{{ resource_prefix }} again"
register: output

- assert:
that:
- not output.changed
- output.comment == 'original comment'
- output.name == '{{ resource_prefix }}.public.'
- output.tags.TestTag == '{{ resource_prefix }}'
- output.tags.another_tag == '{{ resource_prefix }} again'
- not output.private_zone

- name: Do an idemptotent update of a public zone (CHECK MODE)
route53_zone:
zone: "{{ resource_prefix }}.public"
comment: original comment
state: present
tags:
TestTag: "{{ resource_prefix }}"
another_tag: "{{ resource_prefix }} again"
register: output
check_mode: yes

Expand All @@ -80,33 +98,58 @@
- not output.changed
- output.comment == 'original comment'
- output.name == '{{ resource_prefix }}.public.'
- output.tags.TestTag == '{{ resource_prefix }}'
- output.tags.another_tag == '{{ resource_prefix }} again'
- not output.private_zone

# ============================================================
- name: Update comment of a public zone
- name: Modify tags on a public zone
route53_zone:
zone: "{{ resource_prefix }}.public"
comment: original comment
state: present
tags:
AnotherTag: "{{ resource_prefix }}.anothertag"
purge_tags: true
register: output

- assert:
that:
- output.changed
- "'TestTag' not in output.tags"
- output.tags.AnotherTag == '{{ resource_prefix }}.anothertag'

# ============================================================
- name: Update comment and remove tags of a public zone
route53_zone:
zone: "{{ resource_prefix }}.public"
comment: updated comment
state: present
purge_tags: true
tags: {}
register: output

- assert:
that:
- output.changed
- output.result.comment == "updated comment"
- not output.tags

- name: Update comment of a public zone (CHECK MODE)
- name: Update comment and remove tags of a public zone (CHECK MODE)
route53_zone:
zone: "{{ resource_prefix }}.public"
comment: updated comment for check
state: present
purge_tags: true
tags: {}
register: output
check_mode: yes

- assert:
that:
- output.changed
- output.result.comment == "updated comment for check"
- not output.tags

# ============================================================
- name: Delete public zone (CHECK MODE)
Expand Down