[cloud] fix VPC behavior for ec2_group module, improve integration tests (#27038)

* Add tests for group in a VPC

* Improve ec2_group output and documentation

Update ec2_group to provide full security group information
Add RETURN documentation to match

* Fix ec2_group creation within a VPC

Ensure VPC ID gets passed when creating security group

* Add test for auto creating SG

* Fix ec2_group auto group creation

* Add backoff to describe_security_groups

Getting LimitExceeded from describe_security_groups is definitely
possible (source: me) so add backoff to increase likelihood of
success.

To ensure that all `describe_security_group` calls are backed off,
remove implicit ones that use `ec2.SecurityGroup`. From there,
the decision to remove the `ec2` boto3 resource and rely on the client
alone makes good sense.

* Tidy up auto created security group

Add resource_prefix to auto created security group and delete
it in the `always` section.
Use YAML argument form for all module parameters
This commit is contained in:
Will Thames 2017-08-01 20:53:43 +10:00 committed by Ryan Brown
parent 2d734c7ea7
commit f972994662
3 changed files with 282 additions and 116 deletions

View file

@ -20,6 +20,7 @@
"ec2:CreateVpc", "ec2:CreateVpc",
"ec2:DeleteKeyPair", "ec2:DeleteKeyPair",
"ec2:DeleteNatGateway", "ec2:DeleteNatGateway",
"ec2:DeleteVpc",
"ec2:Describe*", "ec2:Describe*",
"ec2:DisassociateAddress", "ec2:DisassociateAddress",
"ec2:DisassociateRouteTable", "ec2:DisassociateRouteTable",

View file

@ -191,6 +191,65 @@ EXAMPLES = '''
state: absent state: absent
''' '''
RETURN = '''
group_name:
description: Security group name
sample: My Security Group
type: string
returned: on create/update
group_id:
description: Security group id
sample: sg-abcd1234
type: string
returned: on create/update
description:
description: Description of security group
sample: My Security Group
type: string
returned: on create/update
tags:
description: Tags associated with the security group
sample:
Name: My Security Group
Purpose: protecting stuff
type: dict
returned: on create/update
vpc_id:
description: ID of VPC to which the security group belongs
sample: vpc-abcd1234
type: string
returned: on create/update
ip_permissions:
description: Inbound rules associated with the security group.
sample:
- from_port: 8182
ip_protocol: tcp
ip_ranges:
- cidr_ip: "1.1.1.1/32"
ipv6_ranges: []
prefix_list_ids: []
to_port: 8182
user_id_group_pairs: []
type: list
returned: on create/update
ip_permissions_egress:
description: Outbound rules associated with the security group.
sample:
- ip_protocol: -1
ip_ranges:
- cidr_ip: "0.0.0.0/0"
ipv6_ranges: []
prefix_list_ids: []
user_id_group_pairs: []
type: list
returned: on create/update
owner_id:
description: AWS Account ID of the security group
sample: 123456789012
type: int
returned: on create/update
'''
import json import json
import re import re
import time import time
@ -200,6 +259,8 @@ from ansible.module_utils.ec2 import get_aws_connection_info
from ansible.module_utils.ec2 import ec2_argument_spec from ansible.module_utils.ec2 import ec2_argument_spec
from ansible.module_utils.ec2 import camel_dict_to_snake_dict from ansible.module_utils.ec2 import camel_dict_to_snake_dict
from ansible.module_utils.ec2 import HAS_BOTO3 from ansible.module_utils.ec2 import HAS_BOTO3
from ansible.module_utils.ec2 import boto3_tag_list_to_ansible_dict
from ansible.module_utils.ec2 import AWSRetry
import traceback import traceback
try: try:
@ -208,6 +269,11 @@ except ImportError:
pass # caught by imported HAS_BOTO3 pass # caught by imported HAS_BOTO3
@AWSRetry.backoff(tries=5, delay=5, backoff=2.0)
def get_security_groups_with_backoff(connection, **kwargs):
return connection.describe_security_groups(**kwargs)
def deduplicate_rules_args(rules): def deduplicate_rules_args(rules):
"""Returns unique rules""" """Returns unique rules"""
if rules is None: if rules is None:
@ -227,7 +293,7 @@ def make_rule_key(prefix, rule, group_id, cidr_ip):
return key.lower().replace('-none', '-None') return key.lower().replace('-none', '-None')
def add_rules_to_loopkup(ipPermissions, group_id, prefix, dict): def add_rules_to_lookup(ipPermissions, group_id, prefix, dict):
for rule in ipPermissions: for rule in ipPermissions:
for groupGrant in rule.get('UserIdGroupPairs'): for groupGrant in rule.get('UserIdGroupPairs'):
dict[make_rule_key(prefix, rule, group_id, groupGrant.get('GroupId'))] = (rule, groupGrant) dict[make_rule_key(prefix, rule, group_id, groupGrant.get('GroupId'))] = (rule, groupGrant)
@ -261,7 +327,7 @@ def validate_rule(module, rule):
module.fail_json(msg='Specify group_id OR group_name, not both') module.fail_json(msg='Specify group_id OR group_name, not both')
def get_target_from_rule(module, ec2, rule, name, group, groups, vpc_id): def get_target_from_rule(module, client, rule, name, group, groups, vpc_id):
""" """
Returns tuple of (group_id, ip) after validating rule params. Returns tuple of (group_id, ip) after validating rule params.
@ -293,10 +359,10 @@ def get_target_from_rule(module, ec2, rule, name, group, groups, vpc_id):
module.fail_json(msg="Specify group_id OR group_name, not both") module.fail_json(msg="Specify group_id OR group_name, not both")
elif 'cidr_ip' in rule and 'cidr_ipv6' in rule: elif 'cidr_ip' in rule and 'cidr_ipv6' in rule:
module.fail_json(msg="Specify cidr_ip OR cidr_ipv6, not both") module.fail_json(msg="Specify cidr_ip OR cidr_ipv6, not both")
elif 'group_id' in rule and re.match(FOREIGN_SECURITY_GROUP_REGEX, rule['group_id']): elif rule.get('group_id') and re.match(FOREIGN_SECURITY_GROUP_REGEX, rule['group_id']):
# this is a foreign Security Group. Since you can't fetch it you must create an instance of it # this is a foreign Security Group. Since you can't fetch it you must create an instance of it
owner_id, group_id, group_name = re.match(FOREIGN_SECURITY_GROUP_REGEX, rule['group_id']).groups() owner_id, group_id, group_name = re.match(FOREIGN_SECURITY_GROUP_REGEX, rule['group_id']).groups()
group_instance = ec2.SecurityGroup(owner_id=owner_id, group_name=group_name, id=group_id) group_instance = dict(GroupId=group_id, GroupName=group_name)
groups[group_id] = group_instance groups[group_id] = group_instance
groups[group_name] = group_instance groups[group_name] = group_instance
elif 'group_id' in rule: elif 'group_id' in rule:
@ -304,18 +370,21 @@ def get_target_from_rule(module, ec2, rule, name, group, groups, vpc_id):
elif 'group_name' in rule: elif 'group_name' in rule:
group_name = rule['group_name'] group_name = rule['group_name']
if group_name == name: if group_name == name:
group_id = group.id group_id = group['GroupId']
groups[group_id] = group groups[group_id] = group
groups[group_name] = group groups[group_name] = group
elif group_name in groups and (vpc_id is None or groups[group_name].vpc_id == vpc_id): elif group_name in groups and (vpc_id is None or groups[group_name]['VpcId'] == vpc_id):
group_id = groups[group_name].id group_id = groups[group_name]['GroupId']
else: else:
if not rule.get('group_desc', '').strip(): if not rule.get('group_desc', '').strip():
module.fail_json(msg="group %s will be automatically created by rule %s and " module.fail_json(msg="group %s will be automatically created by rule %s and "
"no description was provided" % (group_name, rule)) "no description was provided" % (group_name, rule))
if not module.check_mode: if not module.check_mode:
auto_group = ec2.create_security_group(group_name, rule['group_desc'], vpc_id=vpc_id) params = dict(GroupName=group_name, Description=rule['group_desc'])
group_id = auto_group.id if vpc_id:
params['VpcId'] = vpc_id
auto_group = client.create_security_group(**params)
group_id = auto_group['GroupId']
groups[group_id] = auto_group groups[group_id] = auto_group
groups[group_name] = auto_group groups[group_name] = auto_group
target_group_created = True target_group_created = True
@ -404,7 +473,7 @@ def authorize_ip(type, changed, client, group, groupRules,
ip, ip_permission, module, rule, ethertype): ip, ip_permission, module, rule, ethertype):
# If rule already exists, don't later delete it # If rule already exists, don't later delete it
for thisip in ip: for thisip in ip:
rule_id = make_rule_key(type, rule, group.id, thisip) rule_id = make_rule_key(type, rule, group['GroupId'], thisip)
if rule_id in groupRules: if rule_id in groupRules:
del groupRules[rule_id] del groupRules[rule_id]
else: else:
@ -413,14 +482,14 @@ def authorize_ip(type, changed, client, group, groupRules,
if ip_permission: if ip_permission:
try: try:
if type == "in": if type == "in":
client.authorize_security_group_ingress(GroupId=group.group_id, client.authorize_security_group_ingress(GroupId=group['GroupId'],
IpPermissions=[ip_permission]) IpPermissions=[ip_permission])
elif type == "out": elif type == "out":
client.authorize_security_group_egress(GroupId=group.group_id, client.authorize_security_group_egress(GroupId=group['GroupId'],
IpPermissions=[ip_permission]) IpPermissions=[ip_permission])
except botocore.exceptions.ClientError as e: except botocore.exceptions.ClientError as e:
module.fail_json(msg="Unable to authorize %s for ip %s security group '%s' - %s" % module.fail_json(msg="Unable to authorize %s for ip %s security group '%s' - %s" %
(type, thisip, group.group_name, e), (type, thisip, group['GroupName'], e),
exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
changed = True changed = True
return changed, ip_permission return changed, ip_permission
@ -516,16 +585,15 @@ def main():
module.fail_json(msg="The AWS region must be specified as an " module.fail_json(msg="The AWS region must be specified as an "
"environment variable or in the AWS credentials " "environment variable or in the AWS credentials "
"profile.") "profile.")
client, ec2 = boto3_conn(module, conn_type='both', resource='ec2', endpoint=ec2_url, region=region, **aws_connect_params) client = boto3_conn(module, conn_type='client', resource='ec2', endpoint=ec2_url, region=region, **aws_connect_params)
group = None group = None
groups = dict() groups = dict()
security_groups = [] security_groups = []
# do get all security groups # do get all security groups
# find if the group is present # find if the group is present
try: try:
response = client.describe_security_groups() response = get_security_groups_with_backoff(client)
if 'SecurityGroups' in response: security_groups = response.get('SecurityGroups', [])
security_groups = response.get('SecurityGroups')
except botocore.exceptions.NoCredentialsError as e: except botocore.exceptions.NoCredentialsError as e:
module.fail_json(msg="Error in describe_security_groups: %s" % "Unable to locate credentials", exception=traceback.format_exc()) module.fail_json(msg="Error in describe_security_groups: %s" % "Unable to locate credentials", exception=traceback.format_exc())
except botocore.exceptions.ClientError as e: except botocore.exceptions.ClientError as e:
@ -533,22 +601,21 @@ def main():
**camel_dict_to_snake_dict(e.response)) **camel_dict_to_snake_dict(e.response))
for sg in security_groups: for sg in security_groups:
curGroup = ec2.SecurityGroup(sg['GroupId']) groups[sg['GroupId']] = sg
groups[curGroup.id] = ec2.SecurityGroup(curGroup.id) groupName = sg['GroupName']
groupName = curGroup.group_name
if groupName in groups: if groupName in groups:
# Prioritise groups from the current VPC # Prioritise groups from the current VPC
if vpc_id is None or curGroup.vpc_id == vpc_id: if vpc_id is None or sg['VpcId'] == vpc_id:
groups[groupName] = curGroup groups[groupName] = sg
else: else:
groups[groupName] = curGroup groups[groupName] = sg
if group_id: if group_id:
if curGroup.id == group_id: if sg['GroupId'] == group_id:
group = curGroup group = sg
else: else:
if groupName == name and (vpc_id is None or curGroup.vpc_id == vpc_id): if groupName == name and (vpc_id is None or sg['VpcId'] == vpc_id):
group = curGroup group = sg
# Ensure requested group is absent # Ensure requested group is absent
if state == 'absent': if state == 'absent':
@ -556,7 +623,7 @@ def main():
# found a match, delete it # found a match, delete it
try: try:
if not module.check_mode: if not module.check_mode:
group.delete() client.delete_security_group(GroupId=group['GroupId'])
except botocore.exceptions.ClientError as e: except botocore.exceptions.ClientError as e:
module.fail_json(msg="Unable to delete security group '%s' - %s" % (group, e), module.fail_json(msg="Unable to delete security group '%s' - %s" % (group, e),
exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
@ -571,7 +638,7 @@ def main():
elif state == 'present': elif state == 'present':
if group: if group:
# existing group # existing group
if group.description != description: if group['Description'] != description:
module.fail_json( module.fail_json(
msg="Group description does not match existing group. ec2_group does not support this case.") msg="Group description does not match existing group. ec2_group does not support this case.")
@ -579,18 +646,22 @@ def main():
else: else:
# no match found, create it # no match found, create it
if not module.check_mode: if not module.check_mode:
group = client.create_security_group(GroupName=name, Description=description) params = dict(GroupName=name, Description=description)
groupId = group.get('GroupId') if vpc_id:
params['VpcId'] = vpc_id
group = client.create_security_group(**params)
# When a group is created, an egress_rule ALLOW ALL # When a group is created, an egress_rule ALLOW ALL
# to 0.0.0.0/0 is added automatically but it's not # to 0.0.0.0/0 is added automatically but it's not
# reflected in the object returned by the AWS API # reflected in the object returned by the AWS API
# call. We re-read the group for getting an updated object # call. We re-read the group for getting an updated object
# amazon sometimes takes a couple seconds to update the security group so wait till it exists # amazon sometimes takes a couple seconds to update the security group so wait till it exists
while len(client.describe_security_groups(GroupIds=[groupId]) while True:
['SecurityGroups'][0]['IpPermissionsEgress']) == 0: group = get_security_groups_with_backoff(client, GroupIds=[group['GroupId']])['SecurityGroups'][0]
if not group['IpPermissionsEgress']:
time.sleep(0.1) time.sleep(0.1)
else:
break
group = ec2.SecurityGroup(groupId)
changed = True changed = True
else: else:
module.fail_json(msg="Unsupported state requested: %s" % state) module.fail_json(msg="Unsupported state requested: %s" % state)
@ -599,13 +670,13 @@ def main():
if group: if group:
# Manage ingress rules # Manage ingress rules
groupRules = {} groupRules = {}
add_rules_to_loopkup(group.ip_permissions, group.id, 'in', groupRules) add_rules_to_lookup(group['IpPermissions'], group['GroupId'], 'in', groupRules)
# Now, go through all provided rules and ensure they are there. # Now, go through all provided rules and ensure they are there.
if rules is not None: if rules is not None:
ip_permission = [] ip_permission = []
for rule in rules: for rule in rules:
validate_rule(module, rule) validate_rule(module, rule)
group_id, ip, ipv6, target_group_created = get_target_from_rule(module, ec2, rule, name, group_id, ip, ipv6, target_group_created = get_target_from_rule(module, client, rule, name,
group, groups, vpc_id) group, groups, vpc_id)
if target_group_created: if target_group_created:
changed = True changed = True
@ -616,7 +687,7 @@ def main():
rule['to_port'] = None rule['to_port'] = None
if group_id: if group_id:
rule_id = make_rule_key('in', rule, group.id, group_id) rule_id = make_rule_key('in', rule, group['GroupId'], group_id)
if rule_id in groupRules: if rule_id in groupRules:
del groupRules[rule_id] del groupRules[rule_id]
else: else:
@ -628,11 +699,11 @@ def main():
[useridpair.update({'VpcId': vpc_id}) for useridpair in [useridpair.update({'VpcId': vpc_id}) for useridpair in
ip_permission.get('UserIdGroupPairs')] ip_permission.get('UserIdGroupPairs')]
try: try:
client.authorize_security_group_ingress(GroupId=group.group_id, IpPermissions=[ips]) client.authorize_security_group_ingress(GroupId=group['GroupId'], IpPermissions=[ips])
except botocore.exceptions.ClientError as e: except botocore.exceptions.ClientError as e:
module.fail_json( module.fail_json(
msg="Unable to authorize ingress for group %s security group '%s' - %s" % msg="Unable to authorize ingress for group %s security group '%s' - %s" %
(group_id, group.group_name, e), (group_id, group['GroupName'], e),
exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
changed = True changed = True
elif ip: elif ip:
@ -655,22 +726,22 @@ def main():
ip_permission = serialize_revoke(grant, rule) ip_permission = serialize_revoke(grant, rule)
if not module.check_mode: if not module.check_mode:
try: try:
client.revoke_security_group_ingress(GroupId=group.group_id, IpPermissions=[ip_permission]) client.revoke_security_group_ingress(GroupId=group['GroupId'], IpPermissions=[ip_permission])
except botocore.exceptions.ClientError as e: except botocore.exceptions.ClientError as e:
module.fail_json( module.fail_json(
msg="Unable to revoke ingress for security group '%s' - %s" % msg="Unable to revoke ingress for security group '%s' - %s" %
(group.group_name, e), (group['GroupName'], e),
exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
changed = True changed = True
# Manage egress rules # Manage egress rules
groupRules = {} groupRules = {}
add_rules_to_loopkup(group.ip_permissions_egress, group.id, 'out', groupRules) add_rules_to_lookup(group['IpPermissionsEgress'], group['GroupId'], 'out', groupRules)
# Now, go through all provided rules and ensure they are there. # Now, go through all provided rules and ensure they are there.
if rules_egress is not None: if rules_egress is not None:
for rule in rules_egress: for rule in rules_egress:
validate_rule(module, rule) validate_rule(module, rule)
group_id, ip, ipv6, target_group_created = get_target_from_rule(module, ec2, rule, name, group_id, ip, ipv6, target_group_created = get_target_from_rule(module, client, rule, name,
group, groups, vpc_id) group, groups, vpc_id)
if target_group_created: if target_group_created:
changed = True changed = True
@ -681,7 +752,7 @@ def main():
rule['to_port'] = None rule['to_port'] = None
if group_id: if group_id:
rule_id = make_rule_key('out', rule, group.id, group_id) rule_id = make_rule_key('out', rule, group['GroupId'], group_id)
if rule_id in groupRules: if rule_id in groupRules:
del groupRules[rule_id] del groupRules[rule_id]
else: else:
@ -693,11 +764,11 @@ def main():
[useridpair.update({'VpcId': vpc_id}) for useridpair in [useridpair.update({'VpcId': vpc_id}) for useridpair in
ip_permission.get('UserIdGroupPairs')] ip_permission.get('UserIdGroupPairs')]
try: try:
client.authorize_security_group_egress(GroupId=group.group_id, IpPermissions=[ips]) client.authorize_security_group_egress(GroupId=group['GroupId'], IpPermissions=[ips])
except botocore.exceptions.ClientError as e: except botocore.exceptions.ClientError as e:
module.fail_json( module.fail_json(
msg="Unable to authorize egress for group %s security group '%s' - %s" % msg="Unable to authorize egress for group %s security group '%s' - %s" %
(group_id, group.group_name, e), (group_id, group['GroupName'], e),
exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
changed = True changed = True
elif ip: elif ip:
@ -717,7 +788,7 @@ def main():
# when no egress rules are specified, # when no egress rules are specified,
# we add in a default allow all out rule, which was the # we add in a default allow all out rule, which was the
# default behavior before egress rules were added # default behavior before egress rules were added
default_egress_rule = 'out--1-None-None-' + group.id + '-0.0.0.0/0' default_egress_rule = 'out--1-None-None-' + group['GroupId'] + '-0.0.0.0/0'
if default_egress_rule not in groupRules: if default_egress_rule not in groupRules:
if not module.check_mode: if not module.check_mode:
ip_permission = [{'IpProtocol': '-1', ip_permission = [{'IpProtocol': '-1',
@ -725,11 +796,11 @@ def main():
} }
] ]
try: try:
client.authorize_security_group_egress(GroupId=group.group_id, IpPermissions=ip_permission) client.authorize_security_group_egress(GroupId=group['GroupId'], IpPermissions=ip_permission)
except botocore.exceptions.ClientError as e: except botocore.exceptions.ClientError as e:
module.fail_json(msg="Unable to authorize egress for ip %s security group '%s' - %s" % module.fail_json(msg="Unable to authorize egress for ip %s security group '%s' - %s" %
('0.0.0.0/0', ('0.0.0.0/0',
group.group_name, group['GroupName'],
e), e),
exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
changed = True changed = True
@ -745,17 +816,19 @@ def main():
ip_permission = serialize_revoke(grant, rule) ip_permission = serialize_revoke(grant, rule)
if not module.check_mode: if not module.check_mode:
try: try:
client.revoke_security_group_egress(GroupId=group.group_id, IpPermissions=[ip_permission]) client.revoke_security_group_egress(GroupId=group['GroupId'], IpPermissions=[ip_permission])
except botocore.exceptions.ClientError as e: except botocore.exceptions.ClientError as e:
module.fail_json(msg="Unable to revoke egress for ip %s security group '%s' - %s" % module.fail_json(msg="Unable to revoke egress for ip %s security group '%s' - %s" %
(grant, (grant, group['GroupName'], e),
group.group_name,
e),
exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
changed = True changed = True
if group: if group:
module.exit_json(changed=changed, group_id=group.id) security_group = get_security_groups_with_backoff(client, GroupIds=[group['GroupId']])['SecurityGroups'][0]
security_group = camel_dict_to_snake_dict(security_group)
security_group['tags'] = boto3_tag_list_to_ansible_dict(security_group.get('tags', {}),
tag_name_key_name='key', tag_value_key_name='value')
module.exit_json(changed=changed, **security_group)
else: else:
module.exit_json(changed=changed, group_id=None) module.exit_json(changed=changed, group_id=None)

View file

@ -6,7 +6,7 @@
# - EC2_REGION -> AWS_REGION # - EC2_REGION -> AWS_REGION
# #
# - include: ../../setup_ec2/tasks/common.yml module_name=ec2_group # - include: ../../setup_ec2/tasks/common.yml module_name: ec2_group
- block: - block:
@ -25,7 +25,7 @@
# ============================================================ # ============================================================
- name: test failure with only name - name: test failure with only name
ec2_group: ec2_group:
name='{{ec2_group_name}}' name: '{{ec2_group_name}}'
register: result register: result
ignore_errors: true ignore_errors: true
@ -38,7 +38,7 @@
# ============================================================ # ============================================================
- name: test failure with only description - name: test failure with only description
ec2_group: ec2_group:
description='{{ec2_group_description}}' description: '{{ec2_group_description}}'
register: result register: result
ignore_errors: true ignore_errors: true
@ -51,9 +51,9 @@
# ============================================================ # ============================================================
- name: test failure with empty description (AWS API requires non-empty string desc) - name: test failure with empty description (AWS API requires non-empty string desc)
ec2_group: ec2_group:
name='{{ec2_group_name}}' name: '{{ec2_group_name}}'
description='' description: ''
region='{{ec2_region}}' region: '{{ec2_region}}'
register: result register: result
ignore_errors: true ignore_errors: true
@ -66,9 +66,9 @@
# ============================================================ # ============================================================
- name: test valid region parameter - name: test valid region parameter
ec2_group: ec2_group:
name='{{ec2_group_name}}' name: '{{ec2_group_name}}'
description='{{ec2_group_description}}' description: '{{ec2_group_description}}'
region='{{ec2_region}}' region: '{{ec2_region}}'
register: result register: result
ignore_errors: true ignore_errors: true
@ -81,8 +81,8 @@
# ============================================================ # ============================================================
- name: test environment variable EC2_REGION - name: test environment variable EC2_REGION
ec2_group: ec2_group:
name='{{ec2_group_name}}' name: '{{ec2_group_name}}'
description='{{ec2_group_description}}' description: '{{ec2_group_description}}'
environment: environment:
EC2_REGION: '{{ec2_region}}' EC2_REGION: '{{ec2_region}}'
register: result register: result
@ -97,8 +97,8 @@
# ============================================================ # ============================================================
- name: test invalid ec2_url parameter - name: test invalid ec2_url parameter
ec2_group: ec2_group:
name='{{ec2_group_name}}' name: '{{ec2_group_name}}'
description='{{ec2_group_description}}' description: '{{ec2_group_description}}'
environment: environment:
EC2_URL: bogus.example.com EC2_URL: bogus.example.com
register: result register: result
@ -113,8 +113,8 @@
# ============================================================ # ============================================================
- name: test valid ec2_url parameter - name: test valid ec2_url parameter
ec2_group: ec2_group:
name='{{ec2_group_name}}' name: '{{ec2_group_name}}'
description='{{ec2_group_description}}' description: '{{ec2_group_description}}'
environment: environment:
EC2_URL: '{{ec2_url}}' EC2_URL: '{{ec2_url}}'
register: result register: result
@ -129,8 +129,8 @@
# ============================================================ # ============================================================
- name: test credentials from environment - name: test credentials from environment
ec2_group: ec2_group:
name='{{ec2_group_name}}' name: '{{ec2_group_name}}'
description='{{ec2_group_description}}' description: '{{ec2_group_description}}'
environment: environment:
EC2_REGION: '{{ec2_region}}' EC2_REGION: '{{ec2_region}}'
EC2_ACCESS_KEY: bogus_access_key EC2_ACCESS_KEY: bogus_access_key
@ -147,11 +147,11 @@
# ============================================================ # ============================================================
- name: test credential parameters - name: test credential parameters
ec2_group: ec2_group:
name='{{ec2_group_name}}' name: '{{ec2_group_name}}'
description='{{ec2_group_description}}' description: '{{ec2_group_description}}'
ec2_region='{{ec2_region}}' ec2_region: '{{ec2_region}}'
ec2_access_key='bogus_access_key' ec2_access_key: 'bogus_access_key'
ec2_secret_key='bogus_secret_key' ec2_secret_key: 'bogus_secret_key'
register: result register: result
ignore_errors: true ignore_errors: true
@ -164,25 +164,25 @@
# ============================================================ # ============================================================
- name: test state=absent - name: test state=absent
ec2_group: ec2_group:
name='{{ec2_group_name}}' name: '{{ec2_group_name}}'
description='{{ec2_group_description}}' description: '{{ec2_group_description}}'
ec2_region='{{ec2_region}}' ec2_region: '{{ec2_region}}'
ec2_access_key='{{ec2_access_key}}' ec2_access_key: '{{ec2_access_key}}'
ec2_secret_key='{{ec2_secret_key}}' ec2_secret_key: '{{ec2_secret_key}}'
security_token='{{security_token}}' security_token: '{{security_token}}'
state=absent state: absent
register: result register: result
# ============================================================ # ============================================================
- name: test state=present (expected changed=true) - name: test state=present (expected changed=true)
ec2_group: ec2_group:
name='{{ec2_group_name}}' name: '{{ec2_group_name}}'
description='{{ec2_group_description}}' description: '{{ec2_group_description}}'
ec2_region='{{ec2_region}}' ec2_region: '{{ec2_region}}'
ec2_access_key='{{ec2_access_key}}' ec2_access_key: '{{ec2_access_key}}'
ec2_secret_key='{{ec2_secret_key}}' ec2_secret_key: '{{ec2_secret_key}}'
security_token='{{security_token}}' security_token: '{{security_token}}'
state=present state: present
register: result register: result
- name: assert state=present (expected changed=true) - name: assert state=present (expected changed=true)
@ -194,13 +194,13 @@
# ============================================================ # ============================================================
- name: test state=present different description raises error - name: test state=present different description raises error
ec2_group: ec2_group:
name='{{ec2_group_name}}' name: '{{ec2_group_name}}'
description='{{ec2_group_description}}CHANGED' description: '{{ec2_group_description}}CHANGED'
ec2_region='{{ec2_region}}' ec2_region: '{{ec2_region}}'
ec2_access_key='{{ec2_access_key}}' ec2_access_key: '{{ec2_access_key}}'
ec2_secret_key='{{ec2_secret_key}}' ec2_secret_key: '{{ec2_secret_key}}'
security_token='{{security_token}}' security_token: '{{security_token}}'
state=present state: present
ignore_errors: true ignore_errors: true
register: result register: result
@ -213,13 +213,13 @@
# ============================================================ # ============================================================
- name: test state=present (expected changed=false) - name: test state=present (expected changed=false)
ec2_group: ec2_group:
name='{{ec2_group_name}}' name: '{{ec2_group_name}}'
description='{{ec2_group_description}}' description: '{{ec2_group_description}}'
ec2_region='{{ec2_region}}' ec2_region: '{{ec2_region}}'
ec2_access_key='{{ec2_access_key}}' ec2_access_key: '{{ec2_access_key}}'
ec2_secret_key='{{ec2_secret_key}}' ec2_secret_key: '{{ec2_secret_key}}'
security_token='{{security_token}}' security_token: '{{security_token}}'
state=present state: present
register: result register: result
- name: assert state=present (expected changed=false) - name: assert state=present (expected changed=false)
@ -325,11 +325,91 @@
- 'not result.changed' - 'not result.changed'
- 'result.group_id.startswith("sg-")' - 'result.group_id.startswith("sg-")'
- name: add a rule that auto creates another security group
ec2_group:
name: '{{ec2_group_name}}'
description: '{{ec2_group_description}}'
ec2_region: '{{ec2_region}}'
ec2_access_key: '{{ec2_access_key}}'
ec2_secret_key: '{{ec2_secret_key}}'
security_token: '{{security_token}}'
state: present
purge_rules: no
rules:
- proto: "tcp"
group_name: "{{ resource_prefix }} - Another security group"
group_desc: Another security group
ports: 7171
register: result
- name: check that there are now two rules
assert:
that:
- result.changed
- result.ip_permissions|length == 2
- result.ip_permissions[0].user_id_group_pairs or
result.ip_permissions[1].user_id_group_pairs
# ============================================================ # ============================================================
- name: test state=absent (expected changed=true) - name: test state=absent (expected changed=true)
ec2_group: ec2_group:
name='{{ec2_group_name}}' name: '{{ec2_group_name}}'
state=absent state: absent
environment:
EC2_REGION: '{{ec2_region}}'
EC2_ACCESS_KEY: '{{ec2_access_key}}'
EC2_SECRET_KEY: '{{ec2_secret_key}}'
EC2_SECURITY_TOKEN: '{{security_token|default("")}}'
register: result
- name: assert state=absent (expected changed=true)
assert:
that:
- 'result.changed'
- 'not result.group_id'
- name: create a VPC
ec2_vpc_net:
name: "{{ resource_prefix }}-vpc"
state: present
cidr_block: "10.232.232.128/26"
ec2_region: '{{ec2_region}}'
ec2_access_key: '{{ec2_access_key}}'
ec2_secret_key: '{{ec2_secret_key}}'
security_token: '{{security_token}}'
tags:
Name: "{{ resource_prefix }}-vpc"
Description: "Created by ansible-test"
register: vpc_result
- name: create security group in the VPC
ec2_group:
name: '{{ec2_group_name}}'
description: '{{ec2_group_description}}'
ec2_region: '{{ec2_region}}'
ec2_access_key: '{{ec2_access_key}}'
ec2_secret_key: '{{ec2_secret_key}}'
security_token: '{{security_token}}'
vpc_id: '{{ vpc_result.vpc.id }}'
state: present
rules:
- proto: "tcp"
from_port: 8182
to_port: 8182
cidr_ip: "1.1.1.1/32"
register: result
- name: assert state=present (expected changed=true)
assert:
that:
- 'result.changed'
- 'result.vpc_id == vpc_result.vpc.id'
- 'result.group_id.startswith("sg-")'
- name: test state=absent (expected changed=true)
ec2_group:
name: '{{ec2_group_name}}'
state: absent
environment: environment:
EC2_REGION: '{{ec2_region}}' EC2_REGION: '{{ec2_region}}'
EC2_ACCESS_KEY: '{{ec2_access_key}}' EC2_ACCESS_KEY: '{{ec2_access_key}}'
@ -346,19 +426,31 @@
always: always:
# ============================================================ # ============================================================
- name: test state=absent (expected changed=false) - name: tidy up security group
ec2_group: ec2_group:
name='{{ec2_group_name}}' name: '{{ec2_group_name}}'
state=absent state: absent
environment: environment:
EC2_REGION: '{{ec2_region}}' EC2_REGION: '{{ec2_region}}'
EC2_ACCESS_KEY: '{{ec2_access_key}}' EC2_ACCESS_KEY: '{{ec2_access_key}}'
EC2_SECRET_KEY: '{{ec2_secret_key}}' EC2_SECRET_KEY: '{{ec2_secret_key}}'
EC2_SECURITY_TOKEN: '{{security_token|default("")}}' EC2_SECURITY_TOKEN: '{{security_token|default("")}}'
register: result
- name: assert state=absent (expected changed=false) - name: tidy up automatically created SG
assert: ec2_group:
that: name: "{{ resource_prefix }} - Another security group"
- 'not result.changed' state: absent
- 'not result.group_id' ec2_region: '{{ec2_region}}'
ec2_access_key: '{{ec2_access_key}}'
ec2_secret_key: '{{ec2_secret_key}}'
security_token: '{{security_token}}'
- name: tidy up VPC
ec2_vpc_net:
name: "{{ resource_prefix }}-vpc"
state: absent
cidr_block: "10.232.232.128/26"
ec2_region: '{{ec2_region}}'
ec2_access_key: '{{ec2_access_key}}'
ec2_secret_key: '{{ec2_secret_key}}'
security_token: '{{security_token}}'