mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
* Use Boto3 for ec2_group Currently boto doesn't support ipv6. To support ipv6 in ec2_group, we need boto3. boto3 has significant API changes, which caused more re-factoring for ec2_group module. Added additional integration test to test_ec2_group role. * Follow the standard for boto3 ansible Fixed imports. Use boto3 ansible exception with camel_dict_to_snake_dict. Refactored the call to authorize/revoke ingress and egress. * Removed dependancy with module ipaddress Added new parameter called cidr_ipv6 for specifying ipv6 addresses inline with how boto3 handles ipv6 addresses. * Updated integration test * Added ipv6 integration test for ec2_group * Set purge_rules to false for integration test * Fixed import statements Added example for ipv6. Removed defining HAS_BOTO3 variable and import HAS_BOTO3 from ec2. Cleaned up import statements. * Fixed exception handling * Add IAM permissions for ec2_group tests Missing AuthorizeSecurityGroupEgress necessary for latest tests * Wrapped botocore import in try/except block Import just botocore to be more similar to other modules
This commit is contained in:
parent
879acf378d
commit
b980a5c02a
3 changed files with 370 additions and 156 deletions
|
@ -35,6 +35,7 @@
|
||||||
"Effect": "Allow",
|
"Effect": "Allow",
|
||||||
"Action": [
|
"Action": [
|
||||||
"ec2:AuthorizeSecurityGroupIngress",
|
"ec2:AuthorizeSecurityGroupIngress",
|
||||||
|
"ec2:AuthorizeSecurityGroupEgress",
|
||||||
"ec2:CreateTags",
|
"ec2:CreateTags",
|
||||||
"ec2:DeleteRouteTable",
|
"ec2:DeleteRouteTable",
|
||||||
"ec2:DeleteSecurityGroup",
|
"ec2:DeleteSecurityGroup",
|
||||||
|
|
|
@ -19,12 +19,12 @@ ANSIBLE_METADATA = {'metadata_version': '1.0',
|
||||||
'status': ['stableinterface'],
|
'status': ['stableinterface'],
|
||||||
'supported_by': 'curated'}
|
'supported_by': 'curated'}
|
||||||
|
|
||||||
|
|
||||||
DOCUMENTATION = '''
|
DOCUMENTATION = '''
|
||||||
---
|
---
|
||||||
module: ec2_group
|
module: ec2_group
|
||||||
author: "Andrew de Quincey (@adq)"
|
author: "Andrew de Quincey (@adq)"
|
||||||
version_added: "1.3"
|
version_added: "1.3"
|
||||||
|
requirements: [ boto3 ]
|
||||||
short_description: maintain an ec2 VPC security group.
|
short_description: maintain an ec2 VPC security group.
|
||||||
description:
|
description:
|
||||||
- maintains ec2 security groups. This module has a dependency on python-boto >= 2.5
|
- maintains ec2 security groups. This module has a dependency on python-boto >= 2.5
|
||||||
|
@ -143,6 +143,7 @@ EXAMPLES = '''
|
||||||
from_port: 80
|
from_port: 80
|
||||||
to_port: 80
|
to_port: 80
|
||||||
cidr_ip: 0.0.0.0/0
|
cidr_ip: 0.0.0.0/0
|
||||||
|
cidr_ipv6: 64:ff9b::/96
|
||||||
group_name: example-other
|
group_name: example-other
|
||||||
# description to use if example-other needs to be created
|
# description to use if example-other needs to be created
|
||||||
group_desc: other example EC2 group
|
group_desc: other example EC2 group
|
||||||
|
@ -178,6 +179,9 @@ EXAMPLES = '''
|
||||||
cidr_ip:
|
cidr_ip:
|
||||||
- 172.16.1.0/24
|
- 172.16.1.0/24
|
||||||
- 172.16.17.0/24
|
- 172.16.17.0/24
|
||||||
|
cidr_ipv6:
|
||||||
|
- 2607:F8B0::/32
|
||||||
|
- 64:ff9b::/96
|
||||||
group_id:
|
group_id:
|
||||||
- sg-edcd9784
|
- sg-edcd9784
|
||||||
|
|
||||||
|
@ -191,17 +195,17 @@ import json
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.ec2 import ec2_connect, ec2_argument_spec
|
from ansible.module_utils.ec2 import boto3_conn
|
||||||
|
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 camel_dict_to_snake_dict
|
||||||
|
from ansible.module_utils.ec2 import HAS_BOTO3
|
||||||
|
import traceback
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import boto.ec2
|
import botocore
|
||||||
from boto.ec2.securitygroup import SecurityGroup
|
|
||||||
from boto.exception import BotoServerError
|
|
||||||
HAS_BOTO = True
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
HAS_BOTO = False
|
pass # caught by imported HAS_BOTO3
|
||||||
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
|
|
||||||
def deduplicate_rules_args(rules):
|
def deduplicate_rules_args(rules):
|
||||||
|
@ -212,35 +216,33 @@ def deduplicate_rules_args(rules):
|
||||||
|
|
||||||
|
|
||||||
def make_rule_key(prefix, rule, group_id, cidr_ip):
|
def make_rule_key(prefix, rule, group_id, cidr_ip):
|
||||||
"""Creates a unique key for an individual group rule"""
|
if 'proto' in rule:
|
||||||
if isinstance(rule, dict):
|
|
||||||
proto, from_port, to_port = [rule.get(x, None) for x in ('proto', 'from_port', 'to_port')]
|
proto, from_port, to_port = [rule.get(x, None) for x in ('proto', 'from_port', 'to_port')]
|
||||||
# fix for 11177
|
elif 'IpProtocol' in rule:
|
||||||
if proto not in ['icmp', 'tcp', 'udp'] and from_port == -1 and to_port == -1:
|
proto, from_port, to_port = [rule.get(x, None) for x in ('IpProtocol', 'FromPort', 'ToPort')]
|
||||||
from_port = 'none'
|
if proto not in ['icmp', 'tcp', 'udp'] and from_port == -1 and to_port == -1:
|
||||||
to_port = 'none'
|
from_port = 'none'
|
||||||
|
to_port = 'none'
|
||||||
else: # isinstance boto.ec2.securitygroup.IPPermissions
|
|
||||||
proto, from_port, to_port = [getattr(rule, x, None) for x in ('ip_protocol', 'from_port', 'to_port')]
|
|
||||||
|
|
||||||
key = "%s-%s-%s-%s-%s-%s" % (prefix, proto, from_port, to_port, group_id, cidr_ip)
|
key = "%s-%s-%s-%s-%s-%s" % (prefix, proto, from_port, to_port, group_id, cidr_ip)
|
||||||
return key.lower().replace('-none', '-None')
|
return key.lower().replace('-none', '-None')
|
||||||
|
|
||||||
|
|
||||||
def addRulesToLookup(rules, prefix, rules_dict):
|
def add_rules_to_loopkup(ipPermissions, group_id, prefix, dict):
|
||||||
for rule in rules:
|
for rule in ipPermissions:
|
||||||
for grant in rule.grants:
|
for groupGrant in rule.get('UserIdGroupPairs'):
|
||||||
rules_dict[make_rule_key(prefix, rule, grant.group_id, grant.cidr_ip)] = (rule, grant)
|
dict[make_rule_key(prefix, rule, group_id, groupGrant.get('GroupId'))] = (rule, groupGrant)
|
||||||
|
for ipv4Grants in rule.get('IpRanges'):
|
||||||
|
dict[make_rule_key(prefix, rule, group_id, ipv4Grants.get('CidrIp'))] = (rule, ipv4Grants)
|
||||||
|
for ipv6Grants in rule.get('Ipv6Ranges'):
|
||||||
|
dict[make_rule_key(prefix, rule, group_id, ipv6Grants.get('CidrIpv6'))] = (rule, ipv6Grants)
|
||||||
|
|
||||||
|
|
||||||
def validate_rule(module, rule):
|
def validate_rule(module, rule):
|
||||||
VALID_PARAMS = ('cidr_ip',
|
VALID_PARAMS = ('cidr_ip', 'cidr_ipv6',
|
||||||
'group_id', 'group_name', 'group_desc',
|
'group_id', 'group_name', 'group_desc',
|
||||||
'proto', 'from_port', 'to_port')
|
'proto', 'from_port', 'to_port')
|
||||||
|
|
||||||
if not isinstance(rule, dict):
|
if not isinstance(rule, dict):
|
||||||
module.fail_json(msg='Invalid rule parameter type [%s].' % type(rule))
|
module.fail_json(msg='Invalid rule parameter type [%s].' % type(rule))
|
||||||
|
|
||||||
for k in rule:
|
for k in rule:
|
||||||
if k not in VALID_PARAMS:
|
if k not in VALID_PARAMS:
|
||||||
module.fail_json(msg='Invalid rule parameter \'{}\''.format(k))
|
module.fail_json(msg='Invalid rule parameter \'{}\''.format(k))
|
||||||
|
@ -249,6 +251,12 @@ def validate_rule(module, rule):
|
||||||
module.fail_json(msg='Specify group_id OR cidr_ip, not both')
|
module.fail_json(msg='Specify group_id OR cidr_ip, not both')
|
||||||
elif 'group_name' in rule and 'cidr_ip' in rule:
|
elif 'group_name' in rule and 'cidr_ip' in rule:
|
||||||
module.fail_json(msg='Specify group_name OR cidr_ip, not both')
|
module.fail_json(msg='Specify group_name OR cidr_ip, not both')
|
||||||
|
elif 'group_id' in rule and 'cidr_ipv6' in rule:
|
||||||
|
module.fail_json(msg="Specify group_id OR cidr_ipv6, not both")
|
||||||
|
elif 'group_name' in rule and 'cidr_ipv6' in rule:
|
||||||
|
module.fail_json(msg="Specify group_name OR cidr_ipv6, not both")
|
||||||
|
elif 'cidr_ip' in rule and 'cidr_ipv6' in rule:
|
||||||
|
module.fail_json(msg="Specify cidr_ip OR cidr_ipv6, not both")
|
||||||
elif 'group_id' in rule and 'group_name' in rule:
|
elif 'group_id' in rule and 'group_name' in 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')
|
||||||
|
|
||||||
|
@ -270,17 +278,25 @@ def get_target_from_rule(module, ec2, rule, name, group, groups, vpc_id):
|
||||||
group_id = None
|
group_id = None
|
||||||
group_name = None
|
group_name = None
|
||||||
ip = None
|
ip = None
|
||||||
|
ipv6 = None
|
||||||
target_group_created = False
|
target_group_created = False
|
||||||
|
|
||||||
if 'group_id' in rule and 'cidr_ip' in rule:
|
if 'group_id' in rule and 'cidr_ip' in rule:
|
||||||
module.fail_json(msg="Specify group_id OR cidr_ip, not both")
|
module.fail_json(msg="Specify group_id OR cidr_ip, not both")
|
||||||
elif 'group_name' in rule and 'cidr_ip' in rule:
|
elif 'group_name' in rule and 'cidr_ip' in rule:
|
||||||
module.fail_json(msg="Specify group_name OR cidr_ip, not both")
|
module.fail_json(msg="Specify group_name OR cidr_ip, not both")
|
||||||
|
elif 'group_id' in rule and 'cidr_ipv6' in rule:
|
||||||
|
module.fail_json(msg="Specify group_id OR cidr_ipv6, not both")
|
||||||
|
elif 'group_name' in rule and 'cidr_ipv6' in rule:
|
||||||
|
module.fail_json(msg="Specify group_name OR cidr_ipv6, not both")
|
||||||
elif 'group_id' in rule and 'group_name' in rule:
|
elif 'group_id' in rule and 'group_name' in 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")
|
||||||
elif rule.get('group_id') and re.match(FOREIGN_SECURITY_GROUP_REGEX, rule['group_id']):
|
elif 'cidr_ip' in rule and 'cidr_ipv6' in rule:
|
||||||
|
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']):
|
||||||
# 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 = SecurityGroup(owner_id=owner_id, name=group_name, id=group_id)
|
group_instance = SecurityGroup(owner_id=owner_id, group_name=group_name, id=group_id)
|
||||||
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:
|
||||||
|
@ -295,7 +311,8 @@ def get_target_from_rule(module, ec2, rule, name, group, groups, vpc_id):
|
||||||
group_id = groups[group_name].id
|
group_id = groups[group_name].id
|
||||||
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 no description was provided" % (group_name, rule))
|
module.fail_json(msg="group %s will be automatically created by rule %s and "
|
||||||
|
"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)
|
auto_group = ec2.create_security_group(group_name, rule['group_desc'], vpc_id=vpc_id)
|
||||||
group_id = auto_group.id
|
group_id = auto_group.id
|
||||||
|
@ -304,8 +321,10 @@ def get_target_from_rule(module, ec2, rule, name, group, groups, vpc_id):
|
||||||
target_group_created = True
|
target_group_created = True
|
||||||
elif 'cidr_ip' in rule:
|
elif 'cidr_ip' in rule:
|
||||||
ip = rule['cidr_ip']
|
ip = rule['cidr_ip']
|
||||||
|
elif 'cidr_ipv6' in rule:
|
||||||
|
ipv6 = rule['cidr_ipv6']
|
||||||
|
|
||||||
return group_id, ip, target_group_created
|
return group_id, ip, ipv6, target_group_created
|
||||||
|
|
||||||
|
|
||||||
def ports_expand(ports):
|
def ports_expand(ports):
|
||||||
|
@ -351,7 +370,7 @@ def rules_expand_ports(rules):
|
||||||
def rule_expand_source(rule, source_type):
|
def rule_expand_source(rule, source_type):
|
||||||
# takes a rule dict and returns a list of expanded rule dicts for specified source_type
|
# takes a rule dict and returns a list of expanded rule dicts for specified source_type
|
||||||
sources = rule[source_type] if isinstance(rule[source_type], list) else [rule[source_type]]
|
sources = rule[source_type] if isinstance(rule[source_type], list) else [rule[source_type]]
|
||||||
source_types_all = ('cidr_ip', 'group_id', 'group_name')
|
source_types_all = ('cidr_ip', 'cidr_ipv6', 'group_id', 'group_name')
|
||||||
|
|
||||||
rule_expanded = []
|
rule_expanded = []
|
||||||
for source in sources:
|
for source in sources:
|
||||||
|
@ -366,7 +385,7 @@ def rule_expand_source(rule, source_type):
|
||||||
|
|
||||||
def rule_expand_sources(rule):
|
def rule_expand_sources(rule):
|
||||||
# takes a rule dict and returns a list of expanded rule discts
|
# takes a rule dict and returns a list of expanded rule discts
|
||||||
source_types = (stype for stype in ('cidr_ip', 'group_id', 'group_name') if stype in rule)
|
source_types = (stype for stype in ('cidr_ip', 'cidr_ipv6', 'group_id', 'group_name') if stype in rule)
|
||||||
|
|
||||||
return [r for stype in source_types
|
return [r for stype in source_types
|
||||||
for r in rule_expand_source(rule, stype)]
|
for r in rule_expand_source(rule, stype)]
|
||||||
|
@ -381,6 +400,79 @@ def rules_expand_sources(rules):
|
||||||
for rule in rule_expand_sources(rule_complex)]
|
for rule in rule_expand_sources(rule_complex)]
|
||||||
|
|
||||||
|
|
||||||
|
def authorize_ip(type, changed, client, group, groupRules,
|
||||||
|
ip, ip_permission, module, rule, ethertype):
|
||||||
|
# If rule already exists, don't later delete it
|
||||||
|
for thisip in ip:
|
||||||
|
rule_id = make_rule_key(type, rule, group.id, thisip)
|
||||||
|
if rule_id in groupRules:
|
||||||
|
del groupRules[rule_id]
|
||||||
|
else:
|
||||||
|
if not module.check_mode:
|
||||||
|
ip_permission = serialize_ip_grant(rule, thisip, ethertype)
|
||||||
|
if ip_permission:
|
||||||
|
try:
|
||||||
|
if type == "in":
|
||||||
|
client.authorize_security_group_ingress(GroupId=group.group_id,
|
||||||
|
IpPermissions=[ip_permission])
|
||||||
|
elif type == "out":
|
||||||
|
client.authorize_security_group_egress(GroupId=group.group_id,
|
||||||
|
IpPermissions=[ip_permission])
|
||||||
|
except botocore.exceptions.ClientError as e:
|
||||||
|
module.fail_json(msg="Unable to authorize %s for ip %s security group '%s' - %s" %
|
||||||
|
(type, thisip, group.group_name, e),
|
||||||
|
exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
|
||||||
|
changed = True
|
||||||
|
return changed, ip_permission
|
||||||
|
|
||||||
|
|
||||||
|
def serialize_group_grant(group_id, rule):
|
||||||
|
permission = {'IpProtocol': rule['proto'],
|
||||||
|
'FromPort': rule['from_port'],
|
||||||
|
'ToPort': rule['to_port'],
|
||||||
|
'UserIdGroupPairs': [{'GroupId': group_id}]}
|
||||||
|
return permission
|
||||||
|
|
||||||
|
|
||||||
|
def serialize_revoke(grant, rule):
|
||||||
|
permission = dict()
|
||||||
|
fromPort = rule['FromPort'] if 'FromPort' in rule else None
|
||||||
|
toPort = rule['ToPort'] if 'ToPort' in rule else None
|
||||||
|
if 'GroupId' in grant:
|
||||||
|
permission = {'IpProtocol': rule['IpProtocol'],
|
||||||
|
'FromPort': fromPort,
|
||||||
|
'ToPort': toPort,
|
||||||
|
'UserIdGroupPairs': [{'GroupId': grant['GroupId'], 'UserId': grant['UserId']}]
|
||||||
|
}
|
||||||
|
elif 'CidrIp' in grant:
|
||||||
|
permission = {'IpProtocol': rule['IpProtocol'],
|
||||||
|
'FromPort': fromPort,
|
||||||
|
'ToPort': toPort,
|
||||||
|
'IpRanges': [grant]
|
||||||
|
}
|
||||||
|
elif 'CidrIpv6' in grant:
|
||||||
|
permission = {'IpProtocol': rule['IpProtocol'],
|
||||||
|
'FromPort': fromPort,
|
||||||
|
'ToPort': toPort,
|
||||||
|
'Ipv6Ranges': [grant]
|
||||||
|
}
|
||||||
|
if rule['IpProtocol'] in ('all', '-1', -1):
|
||||||
|
del permission['FromPort']
|
||||||
|
del permission['ToPort']
|
||||||
|
return permission
|
||||||
|
|
||||||
|
|
||||||
|
def serialize_ip_grant(rule, thisip, ethertype):
|
||||||
|
permission = {'IpProtocol': rule['proto'],
|
||||||
|
'FromPort': rule['from_port'],
|
||||||
|
'ToPort': rule['to_port']}
|
||||||
|
if ethertype == "ipv4":
|
||||||
|
permission.update({'IpRanges': [{'CidrIp': thisip}]})
|
||||||
|
elif ethertype == "ipv6":
|
||||||
|
permission.update({'Ipv6Ranges': [{'CidrIpv6': thisip}]})
|
||||||
|
return permission
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
argument_spec = ec2_argument_spec()
|
argument_spec = ec2_argument_spec()
|
||||||
argument_spec.update(dict(
|
argument_spec.update(dict(
|
||||||
|
@ -392,8 +484,7 @@ def main():
|
||||||
rules_egress=dict(type='list'),
|
rules_egress=dict(type='list'),
|
||||||
state=dict(default='present', type='str', choices=['present', 'absent']),
|
state=dict(default='present', type='str', choices=['present', 'absent']),
|
||||||
purge_rules=dict(default=True, required=False, type='bool'),
|
purge_rules=dict(default=True, required=False, type='bool'),
|
||||||
purge_rules_egress=dict(default=True, required=False, type='bool'),
|
purge_rules_egress=dict(default=True, required=False, type='bool')
|
||||||
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
module = AnsibleModule(
|
module = AnsibleModule(
|
||||||
|
@ -403,8 +494,8 @@ def main():
|
||||||
required_if=[['state', 'present', ['name']]],
|
required_if=[['state', 'present', ['name']]],
|
||||||
)
|
)
|
||||||
|
|
||||||
if not HAS_BOTO:
|
if not HAS_BOTO3:
|
||||||
module.fail_json(msg='boto required for this module')
|
module.fail_json(msg='boto3 required for this module')
|
||||||
|
|
||||||
name = module.params['name']
|
name = module.params['name']
|
||||||
group_id = module.params['group_id']
|
group_id = module.params['group_id']
|
||||||
|
@ -420,32 +511,43 @@ def main():
|
||||||
module.fail_json(msg='Must provide description when state is present.')
|
module.fail_json(msg='Must provide description when state is present.')
|
||||||
|
|
||||||
changed = False
|
changed = False
|
||||||
|
region, ec2_url, aws_connect_params = get_aws_connection_info(module, boto3=True)
|
||||||
ec2 = ec2_connect(module)
|
if not region:
|
||||||
|
module.fail_json(msg="The AWS region must be specified as an "
|
||||||
# find the group if present
|
"environment variable or in the AWS credentials "
|
||||||
|
"profile.")
|
||||||
|
client, ec2 = boto3_conn(module, conn_type='both', resource='ec2', endpoint=ec2_url, region=region, **aws_connect_params)
|
||||||
group = None
|
group = None
|
||||||
groups = {}
|
groups = dict()
|
||||||
|
security_groups = []
|
||||||
|
# do get all security groups
|
||||||
|
# find if the group is present
|
||||||
try:
|
try:
|
||||||
security_groups = ec2.get_all_security_groups()
|
response = client.describe_security_groups()
|
||||||
except BotoServerError as e:
|
if 'SecurityGroups' in response:
|
||||||
module.fail_json(msg="Error in get_all_security_groups: %s" % e.message, exception=traceback.format_exc())
|
security_groups = response.get('SecurityGroups')
|
||||||
|
except botocore.exceptions.NoCredentialsError as e:
|
||||||
|
module.fail_json(msg="Error in describe_security_groups: %s" % "Unable to locate credentials", exception=traceback.format_exc())
|
||||||
|
except botocore.exceptions.ClientError as e:
|
||||||
|
module.fail_json(msg="Error in describe_security_groups: %s" % e, exception=traceback.format_exc(),
|
||||||
|
**camel_dict_to_snake_dict(e.response))
|
||||||
|
|
||||||
for curGroup in security_groups:
|
for sg in security_groups:
|
||||||
groups[curGroup.id] = curGroup
|
curGroup = ec2.SecurityGroup(sg['GroupId'])
|
||||||
if curGroup.name in groups:
|
groups[curGroup.id] = ec2.SecurityGroup(curGroup.id)
|
||||||
|
groupName = curGroup.group_name
|
||||||
|
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 curGroup.vpc_id == vpc_id:
|
||||||
groups[curGroup.name] = curGroup
|
groups[groupName] = curGroup
|
||||||
else:
|
else:
|
||||||
groups[curGroup.name] = curGroup
|
groups[groupName] = curGroup
|
||||||
|
|
||||||
if group_id:
|
if group_id:
|
||||||
if curGroup.id == group_id:
|
if curGroup.id == group_id:
|
||||||
group = curGroup
|
group = curGroup
|
||||||
else:
|
else:
|
||||||
if curGroup.name == name and (vpc_id is None or curGroup.vpc_id == vpc_id):
|
if groupName == name and (vpc_id is None or curGroup.vpc_id == vpc_id):
|
||||||
group = curGroup
|
group = curGroup
|
||||||
|
|
||||||
# Ensure requested group is absent
|
# Ensure requested group is absent
|
||||||
|
@ -455,8 +557,9 @@ def main():
|
||||||
try:
|
try:
|
||||||
if not module.check_mode:
|
if not module.check_mode:
|
||||||
group.delete()
|
group.delete()
|
||||||
except BotoServerError as e:
|
except botocore.exceptions.ClientError as e:
|
||||||
module.fail_json(msg="Unable to delete security group '%s' - %s" % (group, e.message), exception=traceback.format_exc())
|
module.fail_json(msg="Unable to delete security group '%s' - %s" % (group, e),
|
||||||
|
exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
|
||||||
else:
|
else:
|
||||||
group = None
|
group = None
|
||||||
changed = True
|
changed = True
|
||||||
|
@ -469,40 +572,41 @@ def main():
|
||||||
if group:
|
if group:
|
||||||
# existing group
|
# existing group
|
||||||
if group.description != description:
|
if group.description != description:
|
||||||
module.fail_json(msg="Group description does not match existing group. ec2_group does not support this case.")
|
module.fail_json(
|
||||||
|
msg="Group description does not match existing group. ec2_group does not support this case.")
|
||||||
|
|
||||||
# if the group doesn't exist, create it now
|
# if the group doesn't exist, create it now
|
||||||
else:
|
else:
|
||||||
# no match found, create it
|
# no match found, create it
|
||||||
if not module.check_mode:
|
if not module.check_mode:
|
||||||
group = ec2.create_security_group(name, description, vpc_id=vpc_id)
|
group = client.create_security_group(GroupName=name, Description=description)
|
||||||
|
groupId = group.get('GroupId')
|
||||||
# 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(ec2.get_all_security_groups(filters={'group_id': group.id})) == 0:
|
while len(client.describe_security_groups(GroupIds=[groupId])
|
||||||
|
['SecurityGroups'][0]['IpPermissionsEgress']) == 0:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
group = ec2.get_all_security_groups(group_ids=(group.id,))[0]
|
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)
|
||||||
|
|
||||||
# create a lookup for all existing rules on the group
|
# create a lookup for all existing rules on the group
|
||||||
if group:
|
if group:
|
||||||
|
|
||||||
# Manage ingress rules
|
# Manage ingress rules
|
||||||
groupRules = {}
|
groupRules = {}
|
||||||
addRulesToLookup(group.rules, 'in', groupRules)
|
add_rules_to_loopkup(group.ip_permissions, group.id, '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 = []
|
||||||
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, target_group_created = get_target_from_rule(module, ec2, rule, name, group, groups, vpc_id)
|
group, groups, vpc_id)
|
||||||
if target_group_created:
|
if target_group_created:
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
|
@ -511,49 +615,63 @@ def main():
|
||||||
rule['from_port'] = None
|
rule['from_port'] = None
|
||||||
rule['to_port'] = None
|
rule['to_port'] = None
|
||||||
|
|
||||||
# Convert ip to list we can iterate over
|
if group_id:
|
||||||
if not isinstance(ip, list):
|
rule_id = make_rule_key('in', rule, group.id, group_id)
|
||||||
ip = [ip]
|
if rule_id in groupRules:
|
||||||
|
del groupRules[rule_id]
|
||||||
# If rule already exists, don't later delete it
|
|
||||||
for thisip in ip:
|
|
||||||
ruleId = make_rule_key('in', rule, group_id, thisip)
|
|
||||||
if ruleId not in groupRules:
|
|
||||||
grantGroup = None
|
|
||||||
if group_id:
|
|
||||||
grantGroup = groups[group_id]
|
|
||||||
|
|
||||||
if not module.check_mode:
|
|
||||||
group.authorize(rule['proto'], rule['from_port'], rule['to_port'], thisip, grantGroup)
|
|
||||||
changed = True
|
|
||||||
else:
|
else:
|
||||||
del groupRules[ruleId]
|
if not module.check_mode:
|
||||||
|
ip_permission = serialize_group_grant(group_id, rule)
|
||||||
|
if ip_permission:
|
||||||
|
ips = ip_permission
|
||||||
|
if vpc_id:
|
||||||
|
[useridpair.update({'VpcId': vpc_id}) for useridpair in
|
||||||
|
ip_permission.get('UserIdGroupPairs')]
|
||||||
|
try:
|
||||||
|
client.authorize_security_group_ingress(GroupId=group.group_id, IpPermissions=[ips])
|
||||||
|
except botocore.exceptions.ClientError as e:
|
||||||
|
module.fail_json(
|
||||||
|
msg="Unable to authorize ingress for group %s security group '%s' - %s" %
|
||||||
|
(group_id, group.group_name, e),
|
||||||
|
exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
|
||||||
|
changed = True
|
||||||
|
elif ip:
|
||||||
|
# Convert ip to list we can iterate over
|
||||||
|
if ip and not isinstance(ip, list):
|
||||||
|
ip = [ip]
|
||||||
|
|
||||||
|
changed, ip_permission = authorize_ip("in", changed, client, group, groupRules, ip, ip_permission,
|
||||||
|
module, rule, "ipv4")
|
||||||
|
elif ipv6:
|
||||||
|
# Convert ip to list we can iterate over
|
||||||
|
if not isinstance(ipv6, list):
|
||||||
|
ipv6 = [ipv6]
|
||||||
|
# If rule already exists, don't later delete it
|
||||||
|
changed, ip_permission = authorize_ip("in", changed, client, group, groupRules, ipv6, ip_permission,
|
||||||
|
module, rule, "ipv6")
|
||||||
# Finally, remove anything left in the groupRules -- these will be defunct rules
|
# Finally, remove anything left in the groupRules -- these will be defunct rules
|
||||||
if purge_rules:
|
if purge_rules:
|
||||||
for (rule, grant) in groupRules.values():
|
for (rule, grant) in groupRules.values():
|
||||||
grantGroup = None
|
ip_permission = serialize_revoke(grant, rule)
|
||||||
if grant.group_id:
|
|
||||||
if grant.owner_id != group.owner_id:
|
|
||||||
# this is a foreign Security Group. Since you can't fetch it you must create an instance of it
|
|
||||||
group_instance = SecurityGroup(owner_id=grant.owner_id, name=grant.name, id=grant.group_id)
|
|
||||||
groups[grant.group_id] = group_instance
|
|
||||||
groups[grant.name] = group_instance
|
|
||||||
grantGroup = groups[grant.group_id]
|
|
||||||
if not module.check_mode:
|
if not module.check_mode:
|
||||||
group.revoke(rule.ip_protocol, rule.from_port, rule.to_port, grant.cidr_ip, grantGroup)
|
try:
|
||||||
|
client.revoke_security_group_ingress(GroupId=group.group_id, IpPermissions=[ip_permission])
|
||||||
|
except botocore.exceptions.ClientError as e:
|
||||||
|
module.fail_json(
|
||||||
|
msg="Unable to revoke ingress for security group '%s' - %s" %
|
||||||
|
(group.group_name, e),
|
||||||
|
exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
# Manage egress rules
|
# Manage egress rules
|
||||||
groupRules = {}
|
groupRules = {}
|
||||||
addRulesToLookup(group.rules_egress, 'out', groupRules)
|
add_rules_to_loopkup(group.ip_permissions_egress, group.id, '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, target_group_created = get_target_from_rule(module, ec2, rule, name, group, groups, vpc_id)
|
group, groups, vpc_id)
|
||||||
if target_group_created:
|
if target_group_created:
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
|
@ -562,45 +680,58 @@ def main():
|
||||||
rule['from_port'] = None
|
rule['from_port'] = None
|
||||||
rule['to_port'] = None
|
rule['to_port'] = None
|
||||||
|
|
||||||
# Convert ip to list we can iterate over
|
if group_id:
|
||||||
if not isinstance(ip, list):
|
rule_id = make_rule_key('out', rule, group.id, group_id)
|
||||||
ip = [ip]
|
if rule_id in groupRules:
|
||||||
|
del groupRules[rule_id]
|
||||||
# If rule already exists, don't later delete it
|
|
||||||
for thisip in ip:
|
|
||||||
ruleId = make_rule_key('out', rule, group_id, thisip)
|
|
||||||
if ruleId in groupRules:
|
|
||||||
del groupRules[ruleId]
|
|
||||||
# Otherwise, add new rule
|
|
||||||
else:
|
else:
|
||||||
grantGroup = None
|
|
||||||
if group_id:
|
|
||||||
grantGroup = groups[group_id].id
|
|
||||||
|
|
||||||
if not module.check_mode:
|
if not module.check_mode:
|
||||||
ec2.authorize_security_group_egress(
|
ip_permission = serialize_group_grant(group_id, rule)
|
||||||
group_id=group.id,
|
if ip_permission:
|
||||||
ip_protocol=rule['proto'],
|
ips = ip_permission
|
||||||
from_port=rule['from_port'],
|
if vpc_id:
|
||||||
to_port=rule['to_port'],
|
[useridpair.update({'VpcId': vpc_id}) for useridpair in
|
||||||
src_group_id=grantGroup,
|
ip_permission.get('UserIdGroupPairs')]
|
||||||
cidr_ip=thisip)
|
try:
|
||||||
|
client.authorize_security_group_egress(GroupId=group.group_id, IpPermissions=[ips])
|
||||||
|
except botocore.exceptions.ClientError as e:
|
||||||
|
module.fail_json(
|
||||||
|
msg="Unable to authorize egress for group %s security group '%s' - %s" %
|
||||||
|
(group_id, group.group_name, e),
|
||||||
|
exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
|
||||||
changed = True
|
changed = True
|
||||||
|
elif ip:
|
||||||
|
# Convert ip to list we can iterate over
|
||||||
|
if not isinstance(ip, list):
|
||||||
|
ip = [ip]
|
||||||
|
changed, ip_permission = authorize_ip("out", changed, client, group, groupRules, ip,
|
||||||
|
ip_permission, module, rule, "ipv4")
|
||||||
|
elif ipv6:
|
||||||
|
# Convert ip to list we can iterate over
|
||||||
|
if not isinstance(ipv6, list):
|
||||||
|
ipv6 = [ipv6]
|
||||||
|
# If rule already exists, don't later delete it
|
||||||
|
changed, ip_permission = authorize_ip("out", changed, client, group, groupRules, ipv6,
|
||||||
|
ip_permission, module, rule, "ipv6")
|
||||||
else:
|
else:
|
||||||
# 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-None-0.0.0.0/0'
|
default_egress_rule = 'out--1-None-None-' + group.id + '-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:
|
||||||
ec2.authorize_security_group_egress(
|
ip_permission = [{'IpProtocol': '-1',
|
||||||
group_id=group.id,
|
'IpRanges': [{'CidrIp': '0.0.0.0/0'}]
|
||||||
ip_protocol=-1,
|
}
|
||||||
from_port=None,
|
]
|
||||||
to_port=None,
|
try:
|
||||||
src_group_id=None,
|
client.authorize_security_group_egress(GroupId=group.group_id, IpPermissions=ip_permission)
|
||||||
cidr_ip='0.0.0.0/0'
|
except botocore.exceptions.ClientError as e:
|
||||||
)
|
module.fail_json(msg="Unable to authorize egress for ip %s security group '%s' - %s" %
|
||||||
|
('0.0.0.0/0',
|
||||||
|
group.group_name,
|
||||||
|
e),
|
||||||
|
exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
# make sure the default egress rule is not removed
|
# make sure the default egress rule is not removed
|
||||||
|
@ -609,24 +740,24 @@ def main():
|
||||||
# Finally, remove anything left in the groupRules -- these will be defunct rules
|
# Finally, remove anything left in the groupRules -- these will be defunct rules
|
||||||
if purge_rules_egress:
|
if purge_rules_egress:
|
||||||
for (rule, grant) in groupRules.values():
|
for (rule, grant) in groupRules.values():
|
||||||
grantGroup = None
|
# we shouldn't be revoking 0.0.0.0 egress
|
||||||
if grant.group_id:
|
if grant != '0.0.0.0/0':
|
||||||
grantGroup = groups[grant.group_id].id
|
ip_permission = serialize_revoke(grant, rule)
|
||||||
if not module.check_mode:
|
if not module.check_mode:
|
||||||
ec2.revoke_security_group_egress(
|
try:
|
||||||
group_id=group.id,
|
client.revoke_security_group_egress(GroupId=group.group_id, IpPermissions=[ip_permission])
|
||||||
ip_protocol=rule.ip_protocol,
|
except botocore.exceptions.ClientError as e:
|
||||||
from_port=rule.from_port,
|
module.fail_json(msg="Unable to revoke egress for ip %s security group '%s' - %s" %
|
||||||
to_port=rule.to_port,
|
(grant,
|
||||||
src_group_id=grantGroup,
|
group.group_name,
|
||||||
cidr_ip=grant.cidr_ip)
|
e),
|
||||||
changed = True
|
exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
|
||||||
|
changed = True
|
||||||
|
|
||||||
if group:
|
if group:
|
||||||
module.exit_json(changed=changed, group_id=group.id)
|
module.exit_json(changed=changed, group_id=group.id)
|
||||||
else:
|
else:
|
||||||
module.exit_json(changed=changed, group_id=None)
|
module.exit_json(changed=changed, group_id=None)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -63,21 +63,6 @@
|
||||||
- 'result.failed'
|
- 'result.failed'
|
||||||
- 'result.msg == "Must provide description when state is present."'
|
- 'result.msg == "Must provide description when state is present."'
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
- name: test invalid region parameter
|
|
||||||
ec2_group:
|
|
||||||
name='{{ec2_group_name}}'
|
|
||||||
description='{{ec2_group_description}}'
|
|
||||||
region='asdf querty 1234'
|
|
||||||
register: result
|
|
||||||
ignore_errors: true
|
|
||||||
|
|
||||||
- name: assert invalid region parameter
|
|
||||||
assert:
|
|
||||||
that:
|
|
||||||
- 'result.failed'
|
|
||||||
- 'result.msg.startswith("Region asdf querty 1234 does not seem to be available for aws module boto.ec2. If the region definitely exists, you may need to upgrade boto or extend with endpoints_path")'
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
- name: test valid region parameter
|
- name: test valid region parameter
|
||||||
ec2_group:
|
ec2_group:
|
||||||
|
@ -91,7 +76,7 @@
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- 'result.failed'
|
- 'result.failed'
|
||||||
- 'result.msg.startswith("No handler was ready to authenticate.")'
|
- '"Unable to locate credentials" in result.msg'
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
- name: test environment variable EC2_REGION
|
- name: test environment variable EC2_REGION
|
||||||
|
@ -107,7 +92,7 @@
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- 'result.failed'
|
- 'result.failed'
|
||||||
- 'result.msg.startswith("No handler was ready to authenticate.")'
|
- '"Unable to locate credentials" in result.msg'
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
- name: test invalid ec2_url parameter
|
- name: test invalid ec2_url parameter
|
||||||
|
@ -123,7 +108,7 @@
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- 'result.failed'
|
- 'result.failed'
|
||||||
- 'result.msg.startswith("No handler was ready to authenticate.")'
|
- 'result.msg.startswith("The AWS region must be specified as an environment variable or in the AWS credentials profile")'
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
- name: test valid ec2_url parameter
|
- name: test valid ec2_url parameter
|
||||||
|
@ -139,7 +124,7 @@
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- 'result.failed'
|
- 'result.failed'
|
||||||
- 'result.msg.startswith("No handler was ready to authenticate.")'
|
- 'result.msg.startswith("The AWS region must be specified as an environment variable or in the AWS credentials profile")'
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
- name: test credentials from environment
|
- name: test credentials from environment
|
||||||
|
@ -157,7 +142,7 @@
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- 'result.failed'
|
- 'result.failed'
|
||||||
- '"Error in get_all_security_groups: AWS was not able to validate the provided access credentials" in result.msg'
|
- '"validate the provided access credentials" in result.msg'
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
- name: test credential parameters
|
- name: test credential parameters
|
||||||
|
@ -174,7 +159,7 @@
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- 'result.failed'
|
- 'result.failed'
|
||||||
- '"Error in get_all_security_groups: AWS was not able to validate the provided access credentials" in result.msg'
|
- '"validate the provided access credentials" in result.msg'
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
- name: test state=absent
|
- name: test state=absent
|
||||||
|
@ -243,6 +228,103 @@
|
||||||
- 'not result.changed'
|
- 'not result.changed'
|
||||||
- 'result.group_id.startswith("sg-")'
|
- 'result.group_id.startswith("sg-")'
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
- name: test state=present for ipv6 (expected changed=true)
|
||||||
|
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
|
||||||
|
rules:
|
||||||
|
- proto: "tcp"
|
||||||
|
from_port: 8182
|
||||||
|
to_port: 8182
|
||||||
|
cidr_ipv6: "64:ff9b::/96"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: assert state=present (expected changed=true)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- 'result.changed'
|
||||||
|
- 'result.group_id.startswith("sg-")'
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
- name: test rules_egress state=present for ipv6 (expected changed=true)
|
||||||
|
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
|
||||||
|
rules:
|
||||||
|
- proto: "tcp"
|
||||||
|
from_port: 8182
|
||||||
|
to_port: 8182
|
||||||
|
cidr_ipv6: "64:ff9b::/96"
|
||||||
|
rules_egress:
|
||||||
|
- proto: "tcp"
|
||||||
|
from_port: 8181
|
||||||
|
to_port: 8181
|
||||||
|
cidr_ipv6: "64:ff9b::/96"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: assert state=present (expected changed=true)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- 'result.changed'
|
||||||
|
- 'result.group_id.startswith("sg-")'
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
- name: test state=present for ipv4 (expected changed=true)
|
||||||
|
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
|
||||||
|
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.group_id.startswith("sg-")'
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
- name: add same rule to the existing group (expected changed=false)
|
||||||
|
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
|
||||||
|
rules:
|
||||||
|
- proto: "tcp"
|
||||||
|
from_port: 8182
|
||||||
|
to_port: 8182
|
||||||
|
cidr_ip: "1.1.1.1/32"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: assert state=present (expected changed=false)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- 'not result.changed'
|
||||||
|
- 'result.group_id.startswith("sg-")'
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
- name: test state=absent (expected changed=true)
|
- name: test state=absent (expected changed=true)
|
||||||
ec2_group:
|
ec2_group:
|
||||||
|
|
Loading…
Reference in a new issue