Skip to content

Commit

Permalink
ssm_parameter: add support for tags (ansible-collections#1573) (ansib…
Browse files Browse the repository at this point in the history
…le-collections#1575)

ssm_parameter: add support for tags (ansible-collections#1573)

SUMMARY

Adding support for tags following community guidelines and other practices from other modules. secretsmanager_secret was used along with helper functions from ec2 code. Addresses open issue for feature request ansible-collections#1573.

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

ssm_parameter
ADDITIONAL INFORMATION

Reviewed-by: Alina Buzachis <None>
Reviewed-by: Michael Haskell (mikehas) <None>
Reviewed-by: Dennis Qian <None>
Reviewed-by: Markus Bergholz <[email protected]>
Reviewed-by: Mark Chappell <None>
  • Loading branch information
mikehas authored Feb 2, 2023
1 parent 9440394 commit 2c9513f
Showing 1 changed file with 117 additions and 3 deletions.
120 changes: 117 additions & 3 deletions ssm_parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@
- amazon.aws.aws
- amazon.aws.ec2
- amazon.aws.boto3
- amazon.aws.tags
notes:
- Support for I(tags) and I(purge_tags) was added in release 5.3.0.
'''

EXAMPLES = '''
Expand Down Expand Up @@ -137,6 +142,29 @@
- name: recommend to use with aws_ssm lookup plugin
ansible.builtin.debug:
msg: "{{ lookup('amazon.aws.aws_ssm', 'Hello') }}"
- name: Create or update key/value pair in AWS SSM parameter store w/ tags
community.aws.ssm_parameter:
name: "Hello"
description: "This is your first key"
value: "World"
tags:
Environment: "dev"
Version: "1.0"
Confidentiality: "low"
Tag With Space: "foo bar"
- name: Add or update a tag on an existing parameter w/o removing existing tags
community.aws.ssm_parameter:
name: "Hello"
purge_tags: false
tags:
Contact: "person1"
- name: Delete all tags on an existing parameter
community.aws.ssm_parameter:
name: "Hello"
tags: {}
'''

RETURN = '''
Expand Down Expand Up @@ -208,12 +236,19 @@
description: Parameter version number
example: 3
returned: success
tags:
description: A dictionary representing the tags associated with the parameter.
type: dict
returned: when the parameter has tags
example: {'MyTagName': 'Some Value'}
version_added: 5.3.0
'''

import time

try:
import botocore
from botocore.exceptions import BotoCoreError, ClientError
except ImportError:
pass # Handled by AnsibleAWSModule

Expand All @@ -223,6 +258,9 @@
from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry
from ansible_collections.community.aws.plugins.module_utils.base import BaseWaiterFactory
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


class ParameterWaiterFactory(BaseWaiterFactory):
Expand Down Expand Up @@ -301,6 +339,58 @@ def _wait_deleted(client, module, name):
module.fail_json_aws(e, msg="Failed to describe parameter while waiting for deletion")


def tag_parameter(client, module, parameter_name, tags):
try:
return client.add_tags_to_resource(aws_retry=True, ResourceType='Parameter',
ResourceId=parameter_name, Tags=tags)
except (BotoCoreError, ClientError) as e:
module.fail_json_aws(e, msg="Failed to add tag(s) to parameter")


def untag_parameter(client, module, parameter_name, tag_keys):
try:
return client.remove_tags_from_resource(aws_retry=True, ResourceType='Parameter',
ResourceId=parameter_name, TagKeys=tag_keys)
except (BotoCoreError, ClientError) as e:
module.fail_json_aws(e, msg="Failed to remove tag(s) from parameter")


def get_parameter_tags(client, module, parameter_name):
try:
tags = client.list_tags_for_resource(aws_retry=True, ResourceType='Parameter',
ResourceId=parameter_name)['TagList']
tags_dict = boto3_tag_list_to_ansible_dict(tags)
return tags_dict
except (BotoCoreError, ClientError) as e:
module.fail_json_aws(e, msg="Unable to retrieve parameter tags")


def update_parameter_tags(client, module, parameter_name, supplied_tags):
changed = False
response = {}

if supplied_tags is None:
return False, response

current_tags = get_parameter_tags(client, module, parameter_name)
tags_to_add, tags_to_remove = compare_aws_tags(current_tags, supplied_tags,
module.params.get('purge_tags'))

if tags_to_add:
if module.check_mode:
return True, response
response = tag_parameter(client, module, parameter_name,
ansible_dict_to_boto3_tag_list(tags_to_add))
changed = True
if tags_to_remove:
if module.check_mode:
return True, response
response = untag_parameter(client, module, parameter_name, tags_to_remove)
changed = True

return changed, response


def update_parameter(client, module, **args):
changed = False
response = {}
Expand All @@ -310,8 +400,8 @@ def update_parameter(client, module, **args):
try:
response = client.put_parameter(aws_retry=True, **args)
changed = True
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="setting parameter")
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as exc:
module.fail_json_aws(exc, msg="setting parameter")

return changed, response

Expand All @@ -324,6 +414,9 @@ def describe_parameter(client, module, **args):
if not existing_parameter['Parameters']:
return None

tags_dict = get_parameter_tags(client, module, module.params.get('name'))
existing_parameter['Parameters'][0]['tags'] = tags_dict

return existing_parameter['Parameters'][0]


Expand Down Expand Up @@ -387,7 +480,25 @@ def create_update_parameter(client, module):
(changed, response) = update_parameter(client, module, **args)
if changed:
_wait_updated(client, module, module.params.get('name'), original_version)

# Handle tag updates for existing parameters
if module.params.get('overwrite_value') != 'never':
tags_changed, tags_response = update_parameter_tags(
client, module, existing_parameter['Parameter']['Name'],
module.params.get('tags'))

changed = changed or tags_changed

if tags_response:
response['tag_updates'] = tags_response

else:
# Add tags in initial creation request
if module.params.get('tags'):
args.update(Tags=ansible_dict_to_boto3_tag_list(module.params.get('tags')))
# Overwrite=True conflicts with tags and is not needed for new param
args.update(Overwrite=False)

(changed, response) = update_parameter(client, module, **args)
_wait_exists(client, module, module.params.get('name'))

Expand Down Expand Up @@ -444,6 +555,8 @@ def setup_module_object():
key_id=dict(default="alias/aws/ssm"),
overwrite_value=dict(default='changed', choices=['never', 'changed', 'always']),
tier=dict(default='Standard', choices=['Standard', 'Advanced', 'Intelligent-Tiering']),
tags=dict(type='dict', aliases=['resource_tags']),
purge_tags=dict(type='bool', default=True),
)

return AnsibleAWSModule(
Expand Down Expand Up @@ -474,7 +587,8 @@ def main():
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="to describe parameter")
if parameter_metadata:
result['parameter_metadata'] = camel_dict_to_snake_dict(parameter_metadata)
result['parameter_metadata'] = camel_dict_to_snake_dict(parameter_metadata,
ignore_list=['tags'])

module.exit_json(changed=changed, **result)

Expand Down

0 comments on commit 2c9513f

Please sign in to comment.