mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
parent
a78cc15099
commit
60e3af42d5
13 changed files with 676 additions and 154 deletions
3
changelogs/fragments/sns_topic_boto3_port.yaml
Normal file
3
changelogs/fragments/sns_topic_boto3_port.yaml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
---
|
||||||
|
minor_changes:
|
||||||
|
- sns_topic - Port sns_topic module to boto3 and add an integration test suite.
|
|
@ -246,9 +246,13 @@
|
||||||
"Action": [
|
"Action": [
|
||||||
"SNS:CreateTopic",
|
"SNS:CreateTopic",
|
||||||
"SNS:DeleteTopic",
|
"SNS:DeleteTopic",
|
||||||
"SNS:ListTopics",
|
|
||||||
"SNS:GetTopicAttributes",
|
"SNS:GetTopicAttributes",
|
||||||
"SNS:ListSubscriptionsByTopic"
|
"SNS:ListSubscriptions",
|
||||||
|
"SNS:ListSubscriptionsByTopic",
|
||||||
|
"SNS:ListTopics",
|
||||||
|
"SNS:SetTopicAttributes",
|
||||||
|
"SNS:Subscribe",
|
||||||
|
"SNS:Unsubscribe"
|
||||||
],
|
],
|
||||||
"Resource": [
|
"Resource": [
|
||||||
"*"
|
"*"
|
||||||
|
|
|
@ -3,11 +3,14 @@
|
||||||
"Statement": [
|
"Statement": [
|
||||||
{
|
{
|
||||||
"Action": [
|
"Action": [
|
||||||
|
"iam:GetInstanceProfile",
|
||||||
"iam:GetPolicy",
|
"iam:GetPolicy",
|
||||||
"iam:GetPolicyVersion",
|
"iam:GetPolicyVersion",
|
||||||
"iam:GetRole",
|
"iam:GetRole",
|
||||||
|
"iam:GetRolePolicy",
|
||||||
"iam:ListAttachedRolePolicies",
|
"iam:ListAttachedRolePolicies",
|
||||||
"iam:ListGroups",
|
"iam:ListGroups",
|
||||||
|
"iam:ListInstanceProfiles",
|
||||||
"iam:ListInstanceProfilesForRole",
|
"iam:ListInstanceProfilesForRole",
|
||||||
"iam:ListPolicies",
|
"iam:ListPolicies",
|
||||||
"iam:ListRoles",
|
"iam:ListRoles",
|
||||||
|
|
|
@ -552,6 +552,9 @@ def _hashable_policy(policy, policy_list):
|
||||||
tupleified = tuple(tupleified)
|
tupleified = tuple(tupleified)
|
||||||
policy_list.append(tupleified)
|
policy_list.append(tupleified)
|
||||||
elif isinstance(policy, string_types) or isinstance(policy, binary_type):
|
elif isinstance(policy, string_types) or isinstance(policy, binary_type):
|
||||||
|
# convert root account ARNs to just account IDs
|
||||||
|
if policy.startswith('arn:aws:iam::') and policy.endswith(':root'):
|
||||||
|
policy = policy.split(':')[4]
|
||||||
return [(to_text(policy))]
|
return [(to_text(policy))]
|
||||||
elif isinstance(policy, dict):
|
elif isinstance(policy, dict):
|
||||||
sorted_keys = list(policy.keys())
|
sorted_keys = list(policy.keys())
|
||||||
|
|
|
@ -16,15 +16,17 @@ DOCUMENTATION = """
|
||||||
module: sns_topic
|
module: sns_topic
|
||||||
short_description: Manages AWS SNS topics and subscriptions
|
short_description: Manages AWS SNS topics and subscriptions
|
||||||
description:
|
description:
|
||||||
- The C(sns_topic) module allows you to create, delete, and manage subscriptions for AWS SNS topics.
|
- The C(sns_topic) module allows you to create, delete, and manage subscriptions for AWS SNS topics. As of 2.6,
|
||||||
|
this module can be use to subscribe and unsubscribe to topics outside of your AWS account.
|
||||||
version_added: 2.0
|
version_added: 2.0
|
||||||
author:
|
author:
|
||||||
- "Joel Thompson (@joelthompson)"
|
- "Joel Thompson (@joelthompson)"
|
||||||
- "Fernando Jose Pando (@nand0p)"
|
- "Fernando Jose Pando (@nand0p)"
|
||||||
|
- "Will Thames (@willthames)"
|
||||||
options:
|
options:
|
||||||
name:
|
name:
|
||||||
description:
|
description:
|
||||||
- The name or ARN of the SNS topic to converge
|
- The name or ARN of the SNS topic to manage
|
||||||
required: True
|
required: True
|
||||||
state:
|
state:
|
||||||
description:
|
description:
|
||||||
|
@ -45,6 +47,13 @@ options:
|
||||||
- List of subscriptions to apply to the topic. Note that AWS requires
|
- List of subscriptions to apply to the topic. Note that AWS requires
|
||||||
subscriptions to be confirmed, so you will need to confirm any new
|
subscriptions to be confirmed, so you will need to confirm any new
|
||||||
subscriptions.
|
subscriptions.
|
||||||
|
suboptions:
|
||||||
|
endpoint:
|
||||||
|
description: Endpoint of subscription
|
||||||
|
required: yes
|
||||||
|
protocol:
|
||||||
|
description: Protocol of subscription
|
||||||
|
required: yes
|
||||||
default: []
|
default: []
|
||||||
purge_subscriptions:
|
purge_subscriptions:
|
||||||
description:
|
description:
|
||||||
|
@ -91,42 +100,123 @@ sns_arn:
|
||||||
description: The ARN of the topic you are modifying
|
description: The ARN of the topic you are modifying
|
||||||
type: string
|
type: string
|
||||||
returned: always
|
returned: always
|
||||||
sample: "arn:aws:sns:us-east-1:123456789012:my_topic_name"
|
sample: "arn:aws:sns:us-east-2:111111111111:my_topic_name"
|
||||||
|
|
||||||
sns_topic:
|
sns_topic:
|
||||||
description: Dict of sns topic details
|
description: Dict of sns topic details
|
||||||
type: dict
|
type: complex
|
||||||
returned: always
|
returned: always
|
||||||
sample:
|
contains:
|
||||||
name: sns-topic-name
|
attributes_set:
|
||||||
state: present
|
description: list of attributes set during this run
|
||||||
display_name: default
|
returned: always
|
||||||
policy: {}
|
type: list
|
||||||
delivery_policy: {}
|
sample: []
|
||||||
subscriptions_new: []
|
check_mode:
|
||||||
subscriptions_existing: []
|
description: whether check mode was on
|
||||||
subscriptions_deleted: []
|
returned: always
|
||||||
subscriptions_added: []
|
type: bool
|
||||||
subscriptions_purge': false
|
sample: false
|
||||||
check_mode: false
|
delivery_policy:
|
||||||
topic_created: false
|
description: Delivery policy for the SNS topic
|
||||||
topic_deleted: false
|
returned: when topic is owned by this AWS account
|
||||||
attributes_set: []
|
type: string
|
||||||
|
sample: >
|
||||||
|
{"http":{"defaultHealthyRetryPolicy":{"minDelayTarget":20,"maxDelayTarget":20,"numRetries":3,"numMaxDelayRetries":0,
|
||||||
|
"numNoDelayRetries":0,"numMinDelayRetries":0,"backoffFunction":"linear"},"disableSubscriptionOverrides":false}}
|
||||||
|
display_name:
|
||||||
|
description: Display name for SNS topic
|
||||||
|
returned: when topic is owned by this AWS account
|
||||||
|
type: string
|
||||||
|
sample: My topic name
|
||||||
|
name:
|
||||||
|
description: Topic name
|
||||||
|
returned: always
|
||||||
|
type: string
|
||||||
|
sample: ansible-test-dummy-topic
|
||||||
|
owner:
|
||||||
|
description: AWS account that owns the topic
|
||||||
|
returned: when topic is owned by this AWS account
|
||||||
|
type: string
|
||||||
|
sample: '111111111111'
|
||||||
|
policy:
|
||||||
|
description: Policy for the SNS topic
|
||||||
|
returned: when topic is owned by this AWS account
|
||||||
|
type: string
|
||||||
|
sample: >
|
||||||
|
{"Version":"2012-10-17","Id":"SomePolicyId","Statement":[{"Sid":"ANewSid","Effect":"Allow","Principal":{"AWS":"arn:aws:iam::111111111111:root"},
|
||||||
|
"Action":"sns:Subscribe","Resource":"arn:aws:sns:us-east-2:111111111111:ansible-test-dummy-topic","Condition":{"StringEquals":{"sns:Protocol":"email"}}}]}
|
||||||
|
state:
|
||||||
|
description: whether the topic is present or absent
|
||||||
|
returned: always
|
||||||
|
type: string
|
||||||
|
sample: present
|
||||||
|
subscriptions:
|
||||||
|
description: List of subscribers to the topic in this AWS account
|
||||||
|
returned: always
|
||||||
|
type: list
|
||||||
|
sample: []
|
||||||
|
subscriptions_added:
|
||||||
|
description: List of subscribers added in this run
|
||||||
|
returned: always
|
||||||
|
type: list
|
||||||
|
sample: []
|
||||||
|
subscriptions_confirmed:
|
||||||
|
description: Count of confirmed subscriptions
|
||||||
|
returned: when topic is owned by this AWS account
|
||||||
|
type: list
|
||||||
|
sample: []
|
||||||
|
subscriptions_deleted:
|
||||||
|
description: Count of deleted subscriptions
|
||||||
|
returned: when topic is owned by this AWS account
|
||||||
|
type: list
|
||||||
|
sample: []
|
||||||
|
subscriptions_existing:
|
||||||
|
description: List of existing subscriptions
|
||||||
|
returned: always
|
||||||
|
type: list
|
||||||
|
sample: []
|
||||||
|
subscriptions_new:
|
||||||
|
description: List of new subscriptions
|
||||||
|
returned: always
|
||||||
|
type: list
|
||||||
|
sample: []
|
||||||
|
subscriptions_pending:
|
||||||
|
description: Count of pending subscriptions
|
||||||
|
returned: when topic is owned by this AWS account
|
||||||
|
type: string
|
||||||
|
sample: '0'
|
||||||
|
subscriptions_purge:
|
||||||
|
description: Whether or not purge_subscriptions was set
|
||||||
|
returned: always
|
||||||
|
type: bool
|
||||||
|
sample: true
|
||||||
|
topic_arn:
|
||||||
|
description: ARN of the SNS topic (equivalent to sns_arn)
|
||||||
|
returned: when topic is owned by this AWS account
|
||||||
|
type: string
|
||||||
|
sample: arn:aws:sns:us-east-2:111111111111:ansible-test-dummy-topic
|
||||||
|
topic_created:
|
||||||
|
description: Whether the topic was created
|
||||||
|
returned: always
|
||||||
|
type: bool
|
||||||
|
sample: false
|
||||||
|
topic_deleted:
|
||||||
|
description: Whether the topic was deleted
|
||||||
|
returned: always
|
||||||
|
type: bool
|
||||||
|
sample: false
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
import time
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import boto.sns
|
import botocore
|
||||||
from boto.exception import BotoServerError
|
|
||||||
HAS_BOTO = True
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
HAS_BOTO = False
|
pass # handled by AnsibleAWSModule
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.aws.core import AnsibleAWSModule
|
||||||
from ansible.module_utils.ec2 import connect_to_aws, ec2_argument_spec, get_aws_connection_info
|
from ansible.module_utils.ec2 import compare_policies, AWSRetry, camel_dict_to_snake_dict
|
||||||
|
|
||||||
|
|
||||||
class SnsTopicManager(object):
|
class SnsTopicManager(object):
|
||||||
|
@ -141,14 +231,9 @@ class SnsTopicManager(object):
|
||||||
delivery_policy,
|
delivery_policy,
|
||||||
subscriptions,
|
subscriptions,
|
||||||
purge_subscriptions,
|
purge_subscriptions,
|
||||||
check_mode,
|
check_mode):
|
||||||
region,
|
|
||||||
**aws_connect_params):
|
|
||||||
|
|
||||||
self.region = region
|
self.connection = module.client('sns')
|
||||||
self.aws_connect_params = aws_connect_params
|
|
||||||
self.connection = self._get_boto_connection()
|
|
||||||
self.changed = False
|
|
||||||
self.module = module
|
self.module = module
|
||||||
self.name = name
|
self.name = name
|
||||||
self.state = state
|
self.state = state
|
||||||
|
@ -163,151 +248,193 @@ class SnsTopicManager(object):
|
||||||
self.check_mode = check_mode
|
self.check_mode = check_mode
|
||||||
self.topic_created = False
|
self.topic_created = False
|
||||||
self.topic_deleted = False
|
self.topic_deleted = False
|
||||||
self.arn_topic = None
|
self.topic_arn = None
|
||||||
self.attributes_set = []
|
self.attributes_set = []
|
||||||
|
|
||||||
def _get_boto_connection(self):
|
@AWSRetry.jittered_backoff()
|
||||||
try:
|
def _list_topics_with_backoff(self):
|
||||||
return connect_to_aws(boto.sns, self.region,
|
paginator = self.connection.get_paginator('list_topics')
|
||||||
**self.aws_connect_params)
|
return paginator.paginate().build_full_result()['Topics']
|
||||||
except BotoServerError as err:
|
|
||||||
self.module.fail_json(msg=err.message)
|
|
||||||
|
|
||||||
def _get_all_topics(self):
|
@AWSRetry.jittered_backoff()
|
||||||
next_token = None
|
def _list_topic_subscriptions_with_backoff(self):
|
||||||
topics = []
|
paginator = self.connection.get_paginator('list_subscriptions_by_topic')
|
||||||
while True:
|
return paginator.paginate(TopicArn=self.topic_arn).build_full_result()['Subscriptions']
|
||||||
try:
|
|
||||||
response = self.connection.get_all_topics(next_token)
|
@AWSRetry.jittered_backoff()
|
||||||
except BotoServerError as err:
|
def _list_subscriptions_with_backoff(self):
|
||||||
self.module.fail_json(msg=err.message)
|
paginator = self.connection.get_paginator('list_subscriptions')
|
||||||
topics.extend(response['ListTopicsResponse']['ListTopicsResult']['Topics'])
|
return paginator.paginate().build_full_result()['Subscriptions']
|
||||||
next_token = response['ListTopicsResponse']['ListTopicsResult']['NextToken']
|
|
||||||
if not next_token:
|
def _list_topics(self):
|
||||||
break
|
try:
|
||||||
|
topics = self._list_topics_with_backoff()
|
||||||
|
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||||
|
self.module.fail_json_aws(e, msg="Couldn't get topic list")
|
||||||
return [t['TopicArn'] for t in topics]
|
return [t['TopicArn'] for t in topics]
|
||||||
|
|
||||||
def _arn_topic_lookup(self):
|
def _topic_arn_lookup(self):
|
||||||
# topic names cannot have colons, so this captures the full topic name
|
# topic names cannot have colons, so this captures the full topic name
|
||||||
all_topics = self._get_all_topics()
|
all_topics = self._list_topics()
|
||||||
lookup_topic = ':%s' % self.name
|
lookup_topic = ':%s' % self.name
|
||||||
for topic in all_topics:
|
for topic in all_topics:
|
||||||
if topic.endswith(lookup_topic):
|
if topic.endswith(lookup_topic):
|
||||||
return topic
|
return topic
|
||||||
|
|
||||||
def _create_topic(self):
|
def _create_topic(self):
|
||||||
self.changed = True
|
|
||||||
self.topic_created = True
|
|
||||||
if not self.check_mode:
|
if not self.check_mode:
|
||||||
self.connection.create_topic(self.name)
|
try:
|
||||||
self.arn_topic = self._arn_topic_lookup()
|
response = self.connection.create_topic(Name=self.name)
|
||||||
while not self.arn_topic:
|
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||||
time.sleep(3)
|
self.module.fail_json_aws(e, msg="Couldn't create topic %s" % self.name)
|
||||||
self.arn_topic = self._arn_topic_lookup()
|
self.topic_arn = response['TopicArn']
|
||||||
|
return True
|
||||||
|
|
||||||
def _set_topic_attrs(self):
|
def _set_topic_attrs(self):
|
||||||
topic_attributes = self.connection.get_topic_attributes(self.arn_topic)['GetTopicAttributesResponse']['GetTopicAttributesResult']['Attributes']
|
changed = False
|
||||||
|
try:
|
||||||
|
topic_attributes = self.connection.get_topic_attributes(TopicArn=self.topic_arn)['Attributes']
|
||||||
|
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||||
|
self.module.fail_json_aws(e, msg="Couldn't get topic attributes for topic %s" % self.topic_arn)
|
||||||
|
|
||||||
if self.display_name and self.display_name != topic_attributes['DisplayName']:
|
if self.display_name and self.display_name != topic_attributes['DisplayName']:
|
||||||
self.changed = True
|
changed = True
|
||||||
self.attributes_set.append('display_name')
|
self.attributes_set.append('display_name')
|
||||||
if not self.check_mode:
|
if not self.check_mode:
|
||||||
self.connection.set_topic_attributes(self.arn_topic, 'DisplayName',
|
try:
|
||||||
self.display_name)
|
self.connection.set_topic_attributes(TopicArn=self.topic_arn, AttributeName='DisplayName',
|
||||||
|
AttributeValue=self.display_name)
|
||||||
|
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||||
|
self.module.fail_json_aws(e, msg="Couldn't set display name")
|
||||||
|
|
||||||
if self.policy and self.policy != json.loads(topic_attributes['Policy']):
|
if self.policy and compare_policies(self.policy, json.loads(topic_attributes['Policy'])):
|
||||||
self.changed = True
|
changed = True
|
||||||
self.attributes_set.append('policy')
|
self.attributes_set.append('policy')
|
||||||
if not self.check_mode:
|
if not self.check_mode:
|
||||||
self.connection.set_topic_attributes(self.arn_topic, 'Policy',
|
try:
|
||||||
json.dumps(self.policy))
|
self.connection.set_topic_attributes(TopicArn=self.topic_arn, AttributeName='Policy',
|
||||||
|
AttributeValue=json.dumps(self.policy))
|
||||||
|
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||||
|
self.module.fail_json_aws(e, msg="Couldn't set topic policy")
|
||||||
|
|
||||||
if self.delivery_policy and ('DeliveryPolicy' not in topic_attributes or
|
if self.delivery_policy and ('DeliveryPolicy' not in topic_attributes or
|
||||||
self.delivery_policy != json.loads(topic_attributes['DeliveryPolicy'])):
|
compare_policies(self.delivery_policy, json.loads(topic_attributes['DeliveryPolicy']))):
|
||||||
self.changed = True
|
changed = True
|
||||||
self.attributes_set.append('delivery_policy')
|
self.attributes_set.append('delivery_policy')
|
||||||
if not self.check_mode:
|
if not self.check_mode:
|
||||||
self.connection.set_topic_attributes(self.arn_topic, 'DeliveryPolicy',
|
try:
|
||||||
json.dumps(self.delivery_policy))
|
self.connection.set_topic_attributes(TopicArn=self.topic_arn, AttributeName='DeliveryPolicy',
|
||||||
|
AttributeValue=json.dumps(self.delivery_policy))
|
||||||
|
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||||
|
self.module.fail_json_aws(e, msg="Couldn't set topic delivery policy")
|
||||||
|
return changed
|
||||||
|
|
||||||
def _canonicalize_endpoint(self, protocol, endpoint):
|
def _canonicalize_endpoint(self, protocol, endpoint):
|
||||||
if protocol == 'sms':
|
if protocol == 'sms':
|
||||||
return re.sub('[^0-9]*', '', endpoint)
|
return re.sub('[^0-9]*', '', endpoint)
|
||||||
return endpoint
|
return endpoint
|
||||||
|
|
||||||
def _get_topic_subs(self):
|
|
||||||
next_token = None
|
|
||||||
while True:
|
|
||||||
response = self.connection.get_all_subscriptions_by_topic(self.arn_topic, next_token)
|
|
||||||
self.subscriptions_existing.extend(response['ListSubscriptionsByTopicResponse']
|
|
||||||
['ListSubscriptionsByTopicResult']['Subscriptions'])
|
|
||||||
next_token = response['ListSubscriptionsByTopicResponse']['ListSubscriptionsByTopicResult']['NextToken']
|
|
||||||
if not next_token:
|
|
||||||
break
|
|
||||||
|
|
||||||
def _set_topic_subs(self):
|
def _set_topic_subs(self):
|
||||||
subscriptions_existing_list = []
|
changed = False
|
||||||
|
subscriptions_existing_list = set()
|
||||||
desired_subscriptions = [(sub['protocol'],
|
desired_subscriptions = [(sub['protocol'],
|
||||||
self._canonicalize_endpoint(sub['protocol'], sub['endpoint'])) for sub in
|
self._canonicalize_endpoint(sub['protocol'], sub['endpoint'])) for sub in
|
||||||
self.subscriptions]
|
self.subscriptions]
|
||||||
|
|
||||||
if self.subscriptions_existing:
|
for sub in self._list_topic_subscriptions():
|
||||||
for sub in self.subscriptions_existing:
|
sub_key = (sub['Protocol'], sub['Endpoint'])
|
||||||
sub_key = (sub['Protocol'], sub['Endpoint'])
|
subscriptions_existing_list.add(sub_key)
|
||||||
subscriptions_existing_list.append(sub_key)
|
if (self.purge_subscriptions and sub_key not in desired_subscriptions and
|
||||||
if (self.purge_subscriptions and sub_key not in desired_subscriptions and
|
sub['SubscriptionArn'] not in ('PendingConfirmation', 'Deleted')):
|
||||||
sub['SubscriptionArn'] not in ('PendingConfirmation', 'Deleted')):
|
changed = True
|
||||||
self.changed = True
|
self.subscriptions_deleted.append(sub_key)
|
||||||
self.subscriptions_deleted.append(sub_key)
|
|
||||||
if not self.check_mode:
|
|
||||||
self.connection.unsubscribe(sub['SubscriptionArn'])
|
|
||||||
|
|
||||||
for (protocol, endpoint) in desired_subscriptions:
|
|
||||||
if (protocol, endpoint) not in subscriptions_existing_list:
|
|
||||||
self.changed = True
|
|
||||||
self.subscriptions_added.append((protocol, endpoint))
|
|
||||||
if not self.check_mode:
|
if not self.check_mode:
|
||||||
self.connection.subscribe(self.arn_topic, protocol, endpoint)
|
try:
|
||||||
|
self.connection.unsubscribe(SubscriptionArn=sub['SubscriptionArn'])
|
||||||
|
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||||
|
self.module.fail_json_aws(e, msg="Couldn't unsubscribe from topic")
|
||||||
|
|
||||||
|
for protocol, endpoint in set(desired_subscriptions).difference(subscriptions_existing_list):
|
||||||
|
changed = True
|
||||||
|
self.subscriptions_added.append((protocol, endpoint))
|
||||||
|
if not self.check_mode:
|
||||||
|
try:
|
||||||
|
self.connection.subscribe(TopicArn=self.topic_arn, Protocol=protocol, Endpoint=endpoint)
|
||||||
|
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||||
|
self.module.fail_json_aws(e, msg="Couldn't subscribe to topic %s" % self.topic_arn)
|
||||||
|
return changed
|
||||||
|
|
||||||
|
def _list_topic_subscriptions(self):
|
||||||
|
try:
|
||||||
|
return self._list_topic_subscriptions_with_backoff()
|
||||||
|
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||||
|
try:
|
||||||
|
# potentially AuthorizationError when listing subscriptions for third party topic
|
||||||
|
return [sub for sub in self._list_subscriptions_with_backoff()
|
||||||
|
if sub['TopicArn'] == self.topic_arn]
|
||||||
|
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||||
|
self.module.fail_json_aws(e, msg="Couldn't get subscriptions list for topic %s" % self.topic_arn)
|
||||||
|
|
||||||
def _delete_subscriptions(self):
|
def _delete_subscriptions(self):
|
||||||
# NOTE: subscriptions in 'PendingConfirmation' timeout in 3 days
|
# NOTE: subscriptions in 'PendingConfirmation' timeout in 3 days
|
||||||
# https://forums.aws.amazon.com/thread.jspa?threadID=85993
|
# https://forums.aws.amazon.com/thread.jspa?threadID=85993
|
||||||
for sub in self.subscriptions_existing:
|
subscriptions = self._list_topic_subscriptions()
|
||||||
|
if not subscriptions:
|
||||||
|
return False
|
||||||
|
for sub in subscriptions:
|
||||||
if sub['SubscriptionArn'] not in ('PendingConfirmation', 'Deleted'):
|
if sub['SubscriptionArn'] not in ('PendingConfirmation', 'Deleted'):
|
||||||
self.subscriptions_deleted.append(sub['SubscriptionArn'])
|
self.subscriptions_deleted.append(sub['SubscriptionArn'])
|
||||||
self.changed = True
|
|
||||||
if not self.check_mode:
|
if not self.check_mode:
|
||||||
self.connection.unsubscribe(sub['SubscriptionArn'])
|
try:
|
||||||
|
self.connection.unsubscribe(SubscriptionArn=sub['SubscriptionArn'])
|
||||||
|
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||||
|
self.module.fail_json_aws(e, msg="Couldn't unsubscribe from topic")
|
||||||
|
return True
|
||||||
|
|
||||||
def _delete_topic(self):
|
def _delete_topic(self):
|
||||||
self.topic_deleted = True
|
self.topic_deleted = True
|
||||||
self.changed = True
|
|
||||||
if not self.check_mode:
|
if not self.check_mode:
|
||||||
self.connection.delete_topic(self.arn_topic)
|
try:
|
||||||
|
self.connection.delete_topic(TopicArn=self.topic_arn)
|
||||||
|
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||||
|
self.module.fail_json_aws(e, msg="Couldn't delete topic %s" % self.topic_arn)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _name_is_arn(self):
|
||||||
|
return self.name.startswith('arn:')
|
||||||
|
|
||||||
def ensure_ok(self):
|
def ensure_ok(self):
|
||||||
self.arn_topic = self._arn_topic_lookup()
|
changed = False
|
||||||
if not self.arn_topic:
|
if self._name_is_arn():
|
||||||
self._create_topic()
|
self.topic_arn = self.name
|
||||||
self._set_topic_attrs()
|
else:
|
||||||
self._get_topic_subs()
|
self.topic_arn = self._topic_arn_lookup()
|
||||||
self._set_topic_subs()
|
if not self.topic_arn:
|
||||||
|
changed = self._create_topic()
|
||||||
|
if self.topic_arn in self._list_topics():
|
||||||
|
changed |= self._set_topic_attrs()
|
||||||
|
elif self.display_name or self.policy or self.delivery_policy:
|
||||||
|
self.module.fail_json(msg="Cannot set display name, policy or delivery policy for SNS topics not owned by this account")
|
||||||
|
changed |= self._set_topic_subs()
|
||||||
|
return changed
|
||||||
|
|
||||||
def ensure_gone(self):
|
def ensure_gone(self):
|
||||||
self.arn_topic = self._arn_topic_lookup()
|
changed = False
|
||||||
if self.arn_topic:
|
if self._name_is_arn():
|
||||||
self._get_topic_subs()
|
self.topic_arn = self.name
|
||||||
if self.subscriptions_existing:
|
else:
|
||||||
self._delete_subscriptions()
|
self.topic_arn = self._topic_arn_lookup()
|
||||||
self._delete_topic()
|
if self.topic_arn:
|
||||||
|
if self.topic_arn not in self._list_topics():
|
||||||
|
self.module.fail_json(msg="Cannot use state=absent with third party ARN. Use subscribers=[] to unsubscribe")
|
||||||
|
changed = self._delete_subscriptions()
|
||||||
|
changed |= self._delete_topic()
|
||||||
|
return changed
|
||||||
|
|
||||||
def get_info(self):
|
def get_info(self):
|
||||||
info = {
|
info = {
|
||||||
'name': self.name,
|
'name': self.name,
|
||||||
'state': self.state,
|
'state': self.state,
|
||||||
'display_name': self.display_name,
|
|
||||||
'policy': self.policy,
|
|
||||||
'delivery_policy': self.delivery_policy,
|
|
||||||
'subscriptions_new': self.subscriptions,
|
'subscriptions_new': self.subscriptions,
|
||||||
'subscriptions_existing': self.subscriptions_existing,
|
'subscriptions_existing': self.subscriptions_existing,
|
||||||
'subscriptions_deleted': self.subscriptions_deleted,
|
'subscriptions_deleted': self.subscriptions_deleted,
|
||||||
|
@ -316,32 +443,30 @@ class SnsTopicManager(object):
|
||||||
'check_mode': self.check_mode,
|
'check_mode': self.check_mode,
|
||||||
'topic_created': self.topic_created,
|
'topic_created': self.topic_created,
|
||||||
'topic_deleted': self.topic_deleted,
|
'topic_deleted': self.topic_deleted,
|
||||||
'attributes_set': self.attributes_set
|
'attributes_set': self.attributes_set,
|
||||||
}
|
}
|
||||||
|
if self.state != 'absent':
|
||||||
|
if self.topic_arn in self._list_topics():
|
||||||
|
info.update(camel_dict_to_snake_dict(self.connection.get_topic_attributes(TopicArn=self.topic_arn)['Attributes']))
|
||||||
|
info['delivery_policy'] = info.pop('effective_delivery_policy')
|
||||||
|
info['subscriptions'] = [camel_dict_to_snake_dict(sub) for sub in self._list_topic_subscriptions()]
|
||||||
|
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
argument_spec = ec2_argument_spec()
|
argument_spec = dict(
|
||||||
argument_spec.update(
|
name=dict(required=True),
|
||||||
dict(
|
state=dict(default='present', choices=['present', 'absent']),
|
||||||
name=dict(type='str', required=True),
|
display_name=dict(),
|
||||||
state=dict(type='str', default='present', choices=['present',
|
policy=dict(type='dict'),
|
||||||
'absent']),
|
delivery_policy=dict(type='dict'),
|
||||||
display_name=dict(type='str', required=False),
|
subscriptions=dict(default=[], type='list'),
|
||||||
policy=dict(type='dict', required=False),
|
purge_subscriptions=dict(type='bool', default=True),
|
||||||
delivery_policy=dict(type='dict', required=False),
|
|
||||||
subscriptions=dict(default=[], type='list', required=False),
|
|
||||||
purge_subscriptions=dict(type='bool', default=True),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec=argument_spec,
|
module = AnsibleAWSModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
if not HAS_BOTO:
|
|
||||||
module.fail_json(msg='boto required for this module')
|
|
||||||
|
|
||||||
name = module.params.get('name')
|
name = module.params.get('name')
|
||||||
state = module.params.get('state')
|
state = module.params.get('state')
|
||||||
|
@ -352,10 +477,6 @@ def main():
|
||||||
purge_subscriptions = module.params.get('purge_subscriptions')
|
purge_subscriptions = module.params.get('purge_subscriptions')
|
||||||
check_mode = module.check_mode
|
check_mode = module.check_mode
|
||||||
|
|
||||||
region, ec2_url, aws_connect_params = get_aws_connection_info(module)
|
|
||||||
if not region:
|
|
||||||
module.fail_json(msg="region must be specified")
|
|
||||||
|
|
||||||
sns_topic = SnsTopicManager(module,
|
sns_topic = SnsTopicManager(module,
|
||||||
name,
|
name,
|
||||||
state,
|
state,
|
||||||
|
@ -364,18 +485,16 @@ def main():
|
||||||
delivery_policy,
|
delivery_policy,
|
||||||
subscriptions,
|
subscriptions,
|
||||||
purge_subscriptions,
|
purge_subscriptions,
|
||||||
check_mode,
|
check_mode)
|
||||||
region,
|
|
||||||
**aws_connect_params)
|
|
||||||
|
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
sns_topic.ensure_ok()
|
changed = sns_topic.ensure_ok()
|
||||||
|
|
||||||
elif state == 'absent':
|
elif state == 'absent':
|
||||||
sns_topic.ensure_gone()
|
changed = sns_topic.ensure_gone()
|
||||||
|
|
||||||
sns_facts = dict(changed=sns_topic.changed,
|
sns_facts = dict(changed=changed,
|
||||||
sns_arn=sns_topic.arn_topic,
|
sns_arn=sns_topic.topic_arn,
|
||||||
sns_topic=sns_topic.get_info())
|
sns_topic=sns_topic.get_info())
|
||||||
|
|
||||||
module.exit_json(**sns_facts)
|
module.exit_json(**sns_facts)
|
||||||
|
|
2
test/integration/targets/sns_topic/aliases
Normal file
2
test/integration/targets/sns_topic/aliases
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
cloud/aws
|
||||||
|
unsupported
|
8
test/integration/targets/sns_topic/defaults/main.yml
Normal file
8
test/integration/targets/sns_topic/defaults/main.yml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
sns_topic_topic_name: "{{ resource_prefix }}-topic"
|
||||||
|
sns_topic_subscriptions:
|
||||||
|
- endpoint: "{{ sns_topic_subscriber_arn }}"
|
||||||
|
protocol: "lambda"
|
||||||
|
sns_topic_third_party_topic_arn: "arn:aws:sns:us-east-1:806199016981:AmazonIpSpaceChanged"
|
||||||
|
sns_topic_third_party_region: "{{ sns_topic_third_party_topic_arn.split(':')[3] }}"
|
||||||
|
sns_topic_lambda_function: "sns_topic_lambda"
|
||||||
|
sns_topic_lambda_name: "{{ resource_prefix }}-{{ sns_topic_lambda_function }}"
|
14
test/integration/targets/sns_topic/files/lambda-policy.json
Normal file
14
test/integration/targets/sns_topic/files/lambda-policy.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"Version":"2012-10-17",
|
||||||
|
"Statement":[
|
||||||
|
{
|
||||||
|
"Effect":"Allow",
|
||||||
|
"Action":[
|
||||||
|
"logs:CreateLogStream",
|
||||||
|
"logs:CreateLogGroup",
|
||||||
|
"logs:PutLogEvents"
|
||||||
|
],
|
||||||
|
"Resource":"*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "lambda.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Action": "sts:AssumeRole"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
|
||||||
|
def handler(event, context):
|
||||||
|
print(event)
|
||||||
|
return True
|
308
test/integration/targets/sns_topic/tasks/main.yml
Normal file
308
test/integration/targets/sns_topic/tasks/main.yml
Normal file
|
@ -0,0 +1,308 @@
|
||||||
|
- block:
|
||||||
|
|
||||||
|
- name: set up AWS connection info
|
||||||
|
set_fact:
|
||||||
|
aws_connection_info: &aws_connection_info
|
||||||
|
aws_secret_key: "{{ aws_secret_key|default() }}"
|
||||||
|
aws_access_key: "{{ aws_access_key|default() }}"
|
||||||
|
security_token: "{{ security_token|default() }}"
|
||||||
|
region: "{{ aws_region|default() }}"
|
||||||
|
no_log: yes
|
||||||
|
|
||||||
|
# This should exist, but there's no expectation that the test user should be able to
|
||||||
|
# create/update this role, merely validate that it's there.
|
||||||
|
# Use ansible -m iam_role -a 'name=ansible_lambda_role
|
||||||
|
# assume_role_policy_document={{ lookup("file", "test/integration/targets/sns_topic/files/lambda-trust-policy.json", convert_data=False) }}
|
||||||
|
# ' -vvv localhost
|
||||||
|
# to create this through more privileged credentials before running this test suite.
|
||||||
|
- name: create minimal lambda role
|
||||||
|
iam_role:
|
||||||
|
name: ansible_lambda_role
|
||||||
|
assume_role_policy_document: "{{ lookup('file', 'lambda-trust-policy.json', convert_data=False) }}"
|
||||||
|
create_instance_profile: no
|
||||||
|
<<: *aws_connection_info
|
||||||
|
register: iam_role
|
||||||
|
|
||||||
|
- name: pause if role was created
|
||||||
|
pause:
|
||||||
|
seconds: 10
|
||||||
|
when: iam_role is changed
|
||||||
|
|
||||||
|
- name: ensure lambda role policy exists
|
||||||
|
iam_policy:
|
||||||
|
policy_name: "ansible_lambda_role_policy"
|
||||||
|
iam_name: ansible_lambda_role
|
||||||
|
iam_type: role
|
||||||
|
policy_json: "{{ lookup('file', 'lambda-policy.json') }}"
|
||||||
|
state: present
|
||||||
|
<<: *aws_connection_info
|
||||||
|
register: iam_policy
|
||||||
|
|
||||||
|
- name: pause if policy was created
|
||||||
|
pause:
|
||||||
|
seconds: 10
|
||||||
|
when: iam_policy is changed
|
||||||
|
|
||||||
|
- name: create topic
|
||||||
|
sns_topic:
|
||||||
|
name: "{{ sns_topic_topic_name }}"
|
||||||
|
display_name: "My topic name"
|
||||||
|
<<: *aws_connection_info
|
||||||
|
register: sns_topic_create
|
||||||
|
|
||||||
|
- name: assert that creation worked
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- sns_topic_create.changed
|
||||||
|
|
||||||
|
- name: set sns_arn fact
|
||||||
|
set_fact:
|
||||||
|
sns_arn: "{{ sns_topic_create.sns_arn }}"
|
||||||
|
|
||||||
|
- name: create topic again (expect changed=False)
|
||||||
|
sns_topic:
|
||||||
|
name: "{{ sns_topic_topic_name }}"
|
||||||
|
display_name: "My topic name"
|
||||||
|
<<: *aws_connection_info
|
||||||
|
register: sns_topic_no_change
|
||||||
|
|
||||||
|
- name: assert that recreation had no effect
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not sns_topic_no_change.changed
|
||||||
|
- sns_topic_no_change.sns_arn == sns_topic_create.sns_arn
|
||||||
|
|
||||||
|
- name: update display name
|
||||||
|
sns_topic:
|
||||||
|
name: "{{ sns_topic_topic_name }}"
|
||||||
|
display_name: "My new topic name"
|
||||||
|
<<: *aws_connection_info
|
||||||
|
register: sns_topic_update_name
|
||||||
|
|
||||||
|
- name: assert that updating name worked
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- sns_topic_update_name.changed
|
||||||
|
- 'sns_topic_update_name.sns_topic.display_name == "My new topic name"'
|
||||||
|
|
||||||
|
- name: add policy
|
||||||
|
sns_topic:
|
||||||
|
name: "{{ sns_topic_topic_name }}"
|
||||||
|
display_name: "My new topic name"
|
||||||
|
policy: "{{ lookup('template', 'initial-policy.json') }}"
|
||||||
|
<<: *aws_connection_info
|
||||||
|
register: sns_topic_add_policy
|
||||||
|
|
||||||
|
- name: assert that adding policy worked
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- sns_topic_add_policy.changed
|
||||||
|
|
||||||
|
- name: rerun same policy
|
||||||
|
sns_topic:
|
||||||
|
name: "{{ sns_topic_topic_name }}"
|
||||||
|
display_name: "My new topic name"
|
||||||
|
policy: "{{ lookup('template', 'initial-policy.json') }}"
|
||||||
|
<<: *aws_connection_info
|
||||||
|
register: sns_topic_rerun_policy
|
||||||
|
|
||||||
|
- name: assert that rerunning policy had no effect
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not sns_topic_rerun_policy.changed
|
||||||
|
|
||||||
|
- name: update policy
|
||||||
|
sns_topic:
|
||||||
|
name: "{{ sns_topic_topic_name }}"
|
||||||
|
display_name: "My new topic name"
|
||||||
|
policy: "{{ lookup('template', 'updated-policy.json') }}"
|
||||||
|
<<: *aws_connection_info
|
||||||
|
register: sns_topic_update_policy
|
||||||
|
|
||||||
|
- name: assert that updating policy worked
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- sns_topic_update_policy.changed
|
||||||
|
|
||||||
|
- name: create temp dir
|
||||||
|
tempfile:
|
||||||
|
state: directory
|
||||||
|
register: tempdir
|
||||||
|
|
||||||
|
- name: ensure zip file exists
|
||||||
|
archive:
|
||||||
|
path: "{{ lookup('first_found', sns_topic_lambda_function) }}"
|
||||||
|
dest: "{{ tempdir.path }}/{{ sns_topic_lambda_function }}.zip"
|
||||||
|
format: zip
|
||||||
|
|
||||||
|
- name: create lambda for subscribing (only auto-subscribing target available)
|
||||||
|
lambda:
|
||||||
|
name: '{{ sns_topic_lambda_name }}'
|
||||||
|
state: present
|
||||||
|
zip_file: '{{ tempdir.path }}/{{ sns_topic_lambda_function }}.zip'
|
||||||
|
runtime: 'python2.7'
|
||||||
|
role: ansible_lambda_role
|
||||||
|
handler: '{{ sns_topic_lambda_function }}.handler'
|
||||||
|
<<: *aws_connection_info
|
||||||
|
register: lambda_result
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
sns_topic_subscriber_arn: "{{ lambda_result.configuration.function_arn }}"
|
||||||
|
|
||||||
|
- name: subscribe to topic
|
||||||
|
sns_topic:
|
||||||
|
name: "{{ sns_topic_topic_name }}"
|
||||||
|
display_name: "My new topic name"
|
||||||
|
purge_subscriptions: no
|
||||||
|
subscriptions: "{{ sns_topic_subscriptions }}"
|
||||||
|
<<: *aws_connection_info
|
||||||
|
register: sns_topic_subscribe
|
||||||
|
|
||||||
|
- name: assert that subscribing worked
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- sns_topic_subscribe.changed
|
||||||
|
- sns_topic_subscribe.sns_topic.subscriptions|length == 1
|
||||||
|
|
||||||
|
- name: run again with purge_subscriptions set to false
|
||||||
|
sns_topic:
|
||||||
|
name: "{{ sns_topic_topic_name }}"
|
||||||
|
display_name: "My new topic name"
|
||||||
|
purge_subscriptions: no
|
||||||
|
<<: *aws_connection_info
|
||||||
|
register: sns_topic_no_purge
|
||||||
|
|
||||||
|
- name: assert that not purging subscriptions had no effect
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not sns_topic_no_purge.changed
|
||||||
|
- sns_topic_no_purge.sns_topic.subscriptions|length == 1
|
||||||
|
|
||||||
|
- name: run again with purge_subscriptions set to true
|
||||||
|
sns_topic:
|
||||||
|
name: "{{ sns_topic_topic_name }}"
|
||||||
|
display_name: "My new topic name"
|
||||||
|
purge_subscriptions: yes
|
||||||
|
<<: *aws_connection_info
|
||||||
|
register: sns_topic_purge
|
||||||
|
|
||||||
|
- name: assert that purging subscriptions worked
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- sns_topic_purge.changed
|
||||||
|
- sns_topic_purge.sns_topic.subscriptions|length == 0
|
||||||
|
|
||||||
|
- name: delete topic
|
||||||
|
sns_topic:
|
||||||
|
name: "{{ sns_topic_topic_name }}"
|
||||||
|
state: absent
|
||||||
|
<<: *aws_connection_info
|
||||||
|
|
||||||
|
- name: no-op with third party topic (effectively get existing subscriptions)
|
||||||
|
sns_topic:
|
||||||
|
name: "{{ sns_topic_third_party_topic_arn }}"
|
||||||
|
<<: *aws_connection_info
|
||||||
|
region: "{{ sns_topic_third_party_region }}"
|
||||||
|
register: third_party_topic
|
||||||
|
|
||||||
|
- name: subscribe to third party topic
|
||||||
|
sns_topic:
|
||||||
|
name: "{{ sns_topic_third_party_topic_arn }}"
|
||||||
|
subscriptions: "{{ sns_topic_subscriptions }}"
|
||||||
|
<<: *aws_connection_info
|
||||||
|
region: "{{ sns_topic_third_party_region }}"
|
||||||
|
register: third_party_topic_subscribe
|
||||||
|
|
||||||
|
- name: assert that subscribing worked
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- third_party_topic_subscribe is changed
|
||||||
|
- (third_party_topic_subscribe.sns_topic.subscriptions|length) - (third_party_topic.sns_topic.subscriptions|length) == 1
|
||||||
|
|
||||||
|
- name: attempt to change name of third party topic
|
||||||
|
sns_topic:
|
||||||
|
name: "{{ sns_topic_third_party_topic_arn }}"
|
||||||
|
display_name: "This should not work"
|
||||||
|
subscriptions: "{{ sns_topic_subscriptions }}"
|
||||||
|
<<: *aws_connection_info
|
||||||
|
region: "{{ sns_topic_third_party_region }}"
|
||||||
|
ignore_errors: yes
|
||||||
|
register: third_party_name_change
|
||||||
|
|
||||||
|
- name: assert that attempting to change display name does not work
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- third_party_name_change is failed
|
||||||
|
|
||||||
|
- name: unsubscribe from third party topic (purge_subscription defaults to true)
|
||||||
|
sns_topic:
|
||||||
|
name: "{{ sns_topic_third_party_topic_arn }}"
|
||||||
|
subscriptions: "{{ third_party_topic.sns_topic.subscriptions }}"
|
||||||
|
<<: *aws_connection_info
|
||||||
|
region: "{{ sns_topic_third_party_region }}"
|
||||||
|
register: third_party_unsubscribe
|
||||||
|
|
||||||
|
- name: assert that unsubscribing from third party topic works
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- third_party_unsubscribe.changed
|
||||||
|
- third_party_topic.sns_topic.subscriptions|length == third_party_unsubscribe.sns_topic.subscriptions|length
|
||||||
|
|
||||||
|
- name: attempt to delete third party topic
|
||||||
|
sns_topic:
|
||||||
|
name: "{{ sns_topic_third_party_topic_arn }}"
|
||||||
|
state: absent
|
||||||
|
subscriptions: "{{ subscriptions }}"
|
||||||
|
<<: *aws_connection_info
|
||||||
|
region: "{{ sns_topic_third_party_region }}"
|
||||||
|
ignore_errors: yes
|
||||||
|
register: third_party_deletion
|
||||||
|
|
||||||
|
- name: no-op after third party deletion
|
||||||
|
sns_topic:
|
||||||
|
name: "{{ sns_topic_third_party_topic_arn }}"
|
||||||
|
<<: *aws_connection_info
|
||||||
|
region: "{{ sns_topic_third_party_region }}"
|
||||||
|
register: third_party_deletion_facts
|
||||||
|
|
||||||
|
- name: assert that attempting to delete third party topic does not work and preser
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- third_party_deletion is failed
|
||||||
|
- third_party_topic.sns_topic.subscriptions|length == third_party_deletion_facts.sns_topic.subscriptions|length
|
||||||
|
|
||||||
|
always:
|
||||||
|
|
||||||
|
- name: announce teardown start
|
||||||
|
debug:
|
||||||
|
msg: "************** TEARDOWN STARTS HERE *******************"
|
||||||
|
|
||||||
|
- name: remove topic
|
||||||
|
sns_topic:
|
||||||
|
name: "{{ sns_topic_topic_name }}"
|
||||||
|
state: absent
|
||||||
|
<<: *aws_connection_info
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
- name: unsubscribe from third party topic
|
||||||
|
sns_topic:
|
||||||
|
name: "{{ sns_topic_third_party_topic_arn }}"
|
||||||
|
subscriptions: []
|
||||||
|
purge_subscriptions: yes
|
||||||
|
<<: *aws_connection_info
|
||||||
|
region: "{{ sns_topic_third_party_region }}"
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
- name: remove lambda
|
||||||
|
lambda:
|
||||||
|
name: '{{ sns_topic_lambda_name }}'
|
||||||
|
state: absent
|
||||||
|
<<: *aws_connection_info
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
- name: remove tempdir
|
||||||
|
file:
|
||||||
|
path: "{{ tempdir.path }}"
|
||||||
|
state: absent
|
||||||
|
when: tempdir is defined
|
||||||
|
ignore_errors: yes
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"Version":"2012-10-17",
|
||||||
|
"Id":"SomePolicyId",
|
||||||
|
"Statement" :[
|
||||||
|
{
|
||||||
|
"Sid":"Statement1",
|
||||||
|
"Effect":"Allow",
|
||||||
|
"Principal" :{
|
||||||
|
"AWS":"{{ sns_arn.split(':')[4] }}"
|
||||||
|
},
|
||||||
|
"Action":["sns:Subscribe"],
|
||||||
|
"Resource": "{{ sns_arn }}",
|
||||||
|
"Condition" :{
|
||||||
|
"StringEquals" :{
|
||||||
|
"sns:Protocol":"email"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"Version":"2012-10-17",
|
||||||
|
"Id":"SomePolicyId",
|
||||||
|
"Statement" :[
|
||||||
|
{
|
||||||
|
"Sid":"ANewSid",
|
||||||
|
"Effect":"Allow",
|
||||||
|
"Principal" :{
|
||||||
|
"AWS":"{{ sns_arn.split(':')[4] }}"
|
||||||
|
},
|
||||||
|
"Action":["sns:Subscribe"],
|
||||||
|
"Resource": "{{ sns_arn }}",
|
||||||
|
"Condition" :{
|
||||||
|
"StringEquals" :{
|
||||||
|
"sns:Protocol":"email"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in a new issue