@ -19,12 +19,12 @@ ANSIBLE_METADATA = {'metadata_version': '1.0',
' status ' : [ ' stableinterface ' ] ,
' supported_by ' : ' curated ' }
DOCUMENTATION = '''
- - -
module : ec2_group
author : " Andrew de Quincey (@adq) "
version_added : " 1.3 "
requirements : [ boto3 ]
short_description : maintain an ec2 VPC security group .
description :
- maintains ec2 security groups . This module has a dependency on python - boto > = 2.5
@ -143,6 +143,7 @@ EXAMPLES = '''
from_port : 80
to_port : 80
cidr_ip : 0.0 .0 .0 / 0
cidr_ipv6 : 64 : ff9b : : / 96
group_name : example - other
# description to use if example-other needs to be created
group_desc : other example EC2 group
@ -178,6 +179,9 @@ EXAMPLES = '''
cidr_ip :
- 172.16 .1 .0 / 24
- 172.16 .17 .0 / 24
cidr_ipv6 :
- 2607 : F8B0 : : / 32
- 64 : ff9b : : / 96
group_id :
- sg - edcd9784
@ -191,17 +195,17 @@ import json
import re
import time
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 :
import boto . ec2
from boto . ec2 . securitygroup import SecurityGroup
from boto . exception import BotoServerError
HAS_BOTO = True
import botocore
except ImportError :
HAS_BOTO = False
import traceback
pass # caught by imported HAS_BOTO3
def deduplicate_rules_args ( rules ) :
@ -212,35 +216,33 @@ def deduplicate_rules_args(rules):
def make_rule_key ( prefix , rule , group_id , cidr_ip ) :
""" Creates a unique key for an individual group rule """
if isinstance ( rule , dict ) :
if ' proto ' in rule :
proto , from_port , to_port = [ rule . get ( x , None ) for x in ( ' proto ' , ' from_port ' , ' to_port ' ) ]
# fix for 11177
if proto not in [ ' icmp ' , ' tcp ' , ' udp ' ] and from_port == - 1 and to_port == - 1 :
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 ' ) ]
elif ' IpProtocol ' in rule :
proto , from_port , to_port = [ rule . get ( x , None ) for x in ( ' IpProtocol ' , ' FromPort ' , ' ToPort ' ) ]
if proto not in [ ' icmp ' , ' tcp ' , ' udp ' ] and from_port == - 1 and to_port == - 1 :
from_port = ' none '
to_port = ' none '
key = " %s - %s - %s - %s - %s - %s " % ( prefix , proto , from_port , to_port , group_id , cidr_ip )
return key . lower ( ) . replace ( ' -none ' , ' -None ' )
def addRulesToLookup ( rules , prefix , rules_dict ) :
for rule in rules :
for grant in rule . grants :
rules_dict [ make_rule_key ( prefix , rule , grant . group_id , grant . cidr_ip ) ] = ( rule , grant )
def add_rules_to_loopkup ( ipPermissions , group_id , prefix , dict ) :
for rule in ipPermissions :
for groupGrant in rule . get ( ' UserIdGroupPairs ' ) :
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 ) :
VALID_PARAMS = ( ' cidr_ip ' ,
VALID_PARAMS = ( ' cidr_ip ' , ' cidr_ipv6 ' ,
' group_id ' , ' group_name ' , ' group_desc ' ,
' proto ' , ' from_port ' , ' to_port ' )
if not isinstance ( rule , dict ) :
module . fail_json ( msg = ' Invalid rule parameter type [ %s ]. ' % type ( rule ) )
for k in rule :
if k not in VALID_PARAMS :
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 ' )
elif ' group_name ' in rule and ' cidr_ip ' in rule :
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 :
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_name = None
ip = None
ipv6 = None
target_group_created = False
if ' group_id ' in rule and ' cidr_ip ' in rule :
module . fail_json ( msg = " Specify group_id OR cidr_ip, not both " )
elif ' group_name ' in rule and ' cidr_ip ' in rule :
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 :
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
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_name ] = group_instance
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
else :
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 :
auto_group = ec2 . create_security_group ( group_name , rule [ ' group_desc ' ] , vpc_id = vpc_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
elif ' cidr_ip ' in rule :
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 ) :
@ -351,7 +370,7 @@ def rules_expand_ports(rules):
def rule_expand_source ( rule , 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 ] ]
source_types_all = ( ' cidr_ip ' , ' group_id' , ' group_name ' )
source_types_all = ( ' cidr_ip ' , ' cidr_ipv6' , ' group_id' , ' group_name ' )
rule_expanded = [ ]
for source in sources :
@ -366,7 +385,7 @@ def rule_expand_source(rule, source_type):
def rule_expand_sources ( rule ) :
# 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
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 ) ]
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 ( ) :
argument_spec = ec2_argument_spec ( )
argument_spec . update ( dict (
@ -392,8 +484,7 @@ def main():
rules_egress = dict ( type = ' list ' ) ,
state = dict ( default = ' present ' , type = ' str ' , choices = [ ' present ' , ' absent ' ] ) ,
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 (
@ -403,8 +494,8 @@ def main():
required_if = [ [ ' state ' , ' present ' , [ ' name ' ] ] ] ,
)
if not HAS_BOTO :
module . fail_json ( msg = ' boto required for this module' )
if not HAS_BOTO 3 :
module . fail_json ( msg = ' boto 3 required for this module' )
name = module . params [ ' name ' ]
group_id = module . params [ ' group_id ' ]
@ -420,32 +511,43 @@ def main():
module . fail_json ( msg = ' Must provide description when state is present. ' )
changed = False
ec2 = ec2_connect ( module )
# find the group if present
region , ec2_url , aws_connect_params = get_aws_connection_info ( module , boto3 = True )
if not region :
module . fail_json ( msg = " The AWS region must be specified as an "
" 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
groups = { }
groups = dict ( )
security_groups = [ ]
# do get all security groups
# find if the group is present
try :
security_groups = ec2 . get_all_security_groups ( )
except BotoServerError as e :
module . fail_json ( msg = " Error in get_all_security_groups: %s " % e . message , exception = traceback . format_exc ( ) )
for curGroup in security_groups :
groups [ curGroup . id ] = curGroup
if curGroup . name in groups :
response = client . describe_security_groups ( )
if ' SecurityGroups ' in response :
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 sg in security_groups :
curGroup = ec2 . SecurityGroup ( sg [ ' GroupId ' ] )
groups [ curGroup . id ] = ec2 . SecurityGroup ( curGroup . id )
groupName = curGroup . group_name
if groupName in groups :
# Prioritise groups from the current VPC
if vpc_id is None or curGroup . vpc_id == vpc_id :
groups [ curGroup . name ] = curGroup
groups [ groupN ame] = curGroup
else :
groups [ curGroup . name ] = curGroup
groups [ groupN ame] = curGroup
if group_id :
if curGroup . id == group_id :
group = curGroup
else :
if curGroup. n ame == name and ( vpc_id is None or curGroup . vpc_id == vpc_id ) :
if groupN ame == name and ( vpc_id is None or curGroup . vpc_id == vpc_id ) :
group = curGroup
# Ensure requested group is absent
@ -455,8 +557,9 @@ def main():
try :
if not module . check_mode :
group . delete ( )
except BotoServerError as e :
module . fail_json ( msg = " Unable to delete security group ' %s ' - %s " % ( group , e . message ) , exception = traceback . format_exc ( ) )
except botocore . exceptions . ClientError as 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 ) )
else :
group = None
changed = True
@ -469,40 +572,41 @@ def main():
if group :
# existing group
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
else :
# no match found, create it
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
# to 0.0.0.0/0 is added automatically but it's not
# reflected in the object returned by the AWS API
# 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
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 )
group = ec2 . get_all_security_groups( group_ids = ( group . id , ) ) [ 0 ]
group = ec2 . SecurityGroup( groupId )
changed = True
else :
module . fail_json ( msg = " Unsupported state requested: %s " % state )
# create a lookup for all existing rules on the group
if group :
# Manage ingress rules
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.
if rules is not None :
ip_permission = [ ]
for rule in rules :
validate_rule ( module , rule )
group_id , ip , target_group_created = get_target_from_rule ( module , ec2 , rule , name , group , groups , vpc_id )
group_id , ip , ipv6 , target_group_created = get_target_from_rule ( module , ec2 , rule , name ,
group , groups , vpc_id )
if target_group_created :
changed = True
@ -511,49 +615,63 @@ def main():
rule [ ' from_port ' ] = None
rule [ ' to_port ' ] = None
# Convert ip to list we can iterate over
if not isinstance ( ip , list ) :
ip = [ ip ]
# 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 group_id :
rule_id = make_rule_key ( ' in ' , rule , group . id , group_id )
if rule_id in groupRules :
del groupRules [ rule_id ]
else :
if not module . check_mode :
group . authorize ( rule [ ' proto ' ] , rule [ ' from_port ' ] , rule [ ' to_port ' ] , thisip , grantGroup )
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
else :
del groupRules [ ruleId ]
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
if purge_rules :
for ( rule , grant ) in groupRules . values ( ) :
grantGroup = None
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 ]
ip_permission = serialize_revoke ( grant , rule )
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
# Manage egress rules
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.
if rules_egress is not None :
for rule in rules_egress :
validate_rule ( module , rule )
group_id , ip , target_group_created = get_target_from_rule ( module , ec2 , rule , name , group , groups , vpc_id )
group_id , ip , ipv6 , target_group_created = get_target_from_rule ( module , ec2 , rule , name ,
group , groups , vpc_id )
if target_group_created :
changed = True
@ -562,45 +680,58 @@ def main():
rule [ ' from_port ' ] = None
rule [ ' to_port ' ] = None
# Convert ip to list we can iterate over
if not isinstance ( ip , list ) :
ip = [ ip ]
# 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
if group_id :
rule_id = make_rule_key ( ' out ' , rule , group . id , group_id )
if rule_id in groupRules :
del groupRules [ rule_id ]
else :
grantGroup = None
if group_id :
grantGroup = groups [ group_id ] . id
if not module . check_mode :
ec2 . authorize_security_group_egress (
group_id = group . id ,
ip_protocol = rule [ ' proto ' ] ,
from_port = rule [ ' from_port ' ] ,
to_port = rule [ ' to_port ' ] ,
src_group_id = grantGroup ,
cidr_ip = thisip )
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_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
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 :
# when no egress rules are specified,
# we add in a default allow all out rule, which was the
# 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 not module . check_mode :
ec2 . authorize_security_group_egress (
group_id = group . id ,
ip_protocol = - 1 ,
from_port = None ,
to_port = None ,
src_group_id = None ,
cidr_ip = ' 0.0.0.0/0 '
)
ip_permission = [ { ' IpProtocol ' : ' -1 ' ,
' IpRanges ' : [ { ' CidrIp ' : ' 0.0.0.0/0 ' } ]
}
]
try :
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 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
else :
# 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
if purge_rules_egress :
for ( rule , grant ) in groupRules . values ( ) :
grantGroup = None
if grant . group_id :
grantGroup = groups [ grant . group_id ] . id
if not module . check_mode :
ec2 . revoke_security_group_egress (
group_id = group . id ,
ip_protocol = rule . ip_protocol ,
from_port = rule . from_port ,
to_port = rule . to_port ,
src_group_id = grantGroup ,
cidr_ip = grant . cidr_ip )
changed = True
# we shouldn't be revoking 0.0.0.0 egress
if grant != ' 0.0.0.0/0 ' :
ip_permission = serialize_revoke ( grant , rule )
if not module . check_mode :
try :
client . revoke_security_group_egress ( GroupId = group . group_id , IpPermissions = [ ip_permission ] )
except botocore . exceptions . ClientError as e :
module . fail_json ( msg = " Unable to revoke egress for ip %s security group ' %s ' - %s " %
( grant ,
group . group_name ,
e ) ,
exception = traceback . format_exc ( ) , * * camel_dict_to_snake_dict ( e . response ) )
changed = True
if group :
module . exit_json ( changed = changed , group_id = group . id )
else :
module . exit_json ( changed = changed , group_id = None )
if __name__ == ' __main__ ' :
main ( )