2014-09-26 01:01:01 +00:00
#!/usr/bin/python
# -*- coding: utf-8 -*-
2015-07-04 03:57:53 +00:00
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
2014-09-26 01:01:01 +00:00
2017-08-16 03:16:38 +00:00
ANSIBLE_METADATA = { ' metadata_version ' : ' 1.1 ' ,
2017-03-14 16:07:22 +00:00
' status ' : [ ' stableinterface ' ] ,
2017-09-06 15:15:41 +00:00
' supported_by ' : ' core ' }
2017-03-14 16:07:22 +00:00
2014-09-26 01:01:01 +00:00
DOCUMENTATION = '''
- - -
module : ec2_group
2015-06-15 18:41:22 +00:00
author : " Andrew de Quincey (@adq) "
2014-09-26 01:01:01 +00:00
version_added : " 1.3 "
2017-07-17 02:03:31 +00:00
requirements : [ boto3 ]
2014-09-26 01:01:01 +00:00
short_description : maintain an ec2 VPC security group .
description :
- maintains ec2 security groups . This module has a dependency on python - boto > = 2.5
options :
name :
description :
- Name of the security group .
2017-06-26 13:07:29 +00:00
- One of and only one of I ( name ) or I ( group_id ) is required .
- Required if I ( state = present ) .
required : false
group_id :
description :
- Id of group to delete ( works only with absent ) .
- One of and only one of I ( name ) or I ( group_id ) is required .
required : false
version_added : " 2.4 "
2014-09-26 01:01:01 +00:00
description :
description :
2017-03-07 19:55:17 +00:00
- Description of the security group . Required when C ( state ) is C ( present ) .
required : false
2014-09-26 01:01:01 +00:00
vpc_id :
description :
- ID of the VPC to create the group in .
required : false
rules :
description :
2017-03-17 20:14:20 +00:00
- List of firewall inbound rules to enforce in this group ( see example ) . If none are supplied ,
no inbound rules will be enabled . Rules list may include its own name in ` group_name ` .
2017-06-12 06:55:19 +00:00
This allows idempotent loopback additions ( e . g . allow group to access itself ) .
2017-05-31 12:51:33 +00:00
Rule sources list support was added in version 2.4 . This allows to define multiple sources per
source type as well as multiple source types per rule . Prior to 2.4 an individual source is allowed .
2017-10-25 01:18:56 +00:00
In version 2.5 support for rule descriptions was added .
2014-09-26 01:01:01 +00:00
required : false
rules_egress :
description :
2017-03-17 20:14:20 +00:00
- List of firewall outbound rules to enforce in this group ( see example ) . If none are supplied ,
a default all - out rule is assumed . If an empty list is supplied , no outbound rules will be enabled .
2017-10-25 01:18:56 +00:00
Rule Egress sources list support was added in version 2.4 . In version 2.5 support for rule descriptions
was added .
2014-09-26 01:01:01 +00:00
required : false
version_added : " 1.6 "
state :
version_added : " 1.4 "
description :
- Create or delete a security group
required : false
default : ' present '
choices : [ " present " , " absent " ]
aliases : [ ]
purge_rules :
version_added : " 1.8 "
description :
- Purge existing rules on security group that are not found in rules
required : false
default : ' true '
aliases : [ ]
purge_rules_egress :
version_added : " 1.8 "
description :
2014-11-29 05:05:22 +00:00
- Purge existing rules_egress on security group that are not found in rules_egress
2014-09-26 01:01:01 +00:00
required : false
default : ' true '
aliases : [ ]
2017-08-22 15:11:38 +00:00
tags :
version_added : " 2.4 "
description :
- A dictionary of one or more tags to assign to the security group .
required : false
purge_tags :
version_added : " 2.4 "
description :
- If yes , existing tags will be purged from the resource to match exactly what is defined by I ( tags ) parameter . If the I ( tags ) parameter is not set then
tags will not be modified .
required : false
default : yes
choices : [ ' yes ' , ' no ' ]
2014-09-26 01:01:01 +00:00
2016-12-08 02:33:38 +00:00
extends_documentation_fragment :
- aws
- ec2
2014-09-26 01:01:01 +00:00
notes :
- If a rule declares a group_name and that group doesn ' t exist, it will be
automatically created . In that case , group_desc should be provided as well .
The module will refuse to create a depended - on group without a description .
'''
EXAMPLES = '''
2017-10-25 01:18:56 +00:00
- name : example using security group rule descriptions
ec2_group :
name : " {{ name }} "
description : sg with rule descriptions
vpc_id : vpc - xxxxxxxx
profile : " {{ aws_profile }} "
region : us - east - 1
rules :
- proto : tcp
ports :
- 80
cidr_ip : 0.0 .0 .0 / 0
rule_desc : allow all on port 80
2014-09-26 01:01:01 +00:00
- name : example ec2 group
2014-12-01 20:14:57 +00:00
ec2_group :
2014-09-26 01:01:01 +00:00
name : example
description : an example EC2 group
vpc_id : 12345
2017-04-13 19:11:00 +00:00
region : eu - west - 1
2014-09-26 01:01:01 +00:00
aws_secret_key : SECRET
aws_access_key : ACCESS
rules :
- proto : tcp
from_port : 80
to_port : 80
cidr_ip : 0.0 .0 .0 / 0
- proto : tcp
from_port : 22
to_port : 22
cidr_ip : 10.0 .0 .0 / 8
2015-05-22 10:34:41 +00:00
- proto : tcp
from_port : 443
to_port : 443
group_id : amazon - elb / sg - 87654321 / amazon - elb - sg
- proto : tcp
from_port : 3306
to_port : 3306
group_id : 123412341234 / sg - 87654321 / exact - name - of - sg
2014-09-26 01:01:01 +00:00
- proto : udp
from_port : 10050
to_port : 10050
cidr_ip : 10.0 .0 .0 / 8
- proto : udp
from_port : 10051
to_port : 10051
group_id : sg - 12345678
2015-09-22 13:56:13 +00:00
- proto : icmp
from_port : 8 # icmp type, -1 = any type
to_port : - 1 # icmp subtype, -1 = any subtype
cidr_ip : 10.0 .0 .0 / 8
2014-09-26 01:01:01 +00:00
- proto : all
# the containing group name may be specified here
group_name : example
2018-02-22 23:24:25 +00:00
- proto : all
# in the 'proto' attribute, if you specify -1, all, or a protocol number other than tcp, udp, icmp, or 58 (ICMPv6),
# traffic on all ports is allowed, regardless of any ports you specify
from_port : 10050 # this value is ignored
to_port : 10050 # this value is ignored
cidr_ip : 10.0 .0 .0 / 8
2014-09-26 01:01:01 +00:00
rules_egress :
- proto : tcp
from_port : 80
to_port : 80
2014-10-23 12:19:23 +00:00
cidr_ip : 0.0 .0 .0 / 0
2017-07-17 02:03:31 +00:00
cidr_ipv6 : 64 : ff9b : : / 96
2014-09-26 01:01:01 +00:00
group_name : example - other
# description to use if example-other needs to be created
group_desc : other example EC2 group
2017-01-29 16:29:54 +00:00
- name : example2 ec2 group
ec2_group :
name : example2
description : an example2 EC2 group
vpc_id : 12345
2017-04-13 19:11:00 +00:00
region : eu - west - 1
2017-01-29 16:29:54 +00:00
rules :
2017-04-04 18:09:28 +00:00
# 'ports' rule keyword was introduced in version 2.4. It accepts a single port value or a list of values including ranges (from_port-to_port).
2017-01-29 16:29:54 +00:00
- proto : tcp
ports : 22
group_name : example - vpn
- proto : tcp
ports :
- 80
- 443
- 8080 - 8099
cidr_ip : 0.0 .0 .0 / 0
2017-04-04 18:09:28 +00:00
# Rule sources list support was added in version 2.4. This allows to define multiple sources per source type as well as multiple source types per rule.
2017-01-29 16:29:54 +00:00
- proto : tcp
ports :
- 6379
- 26379
group_name :
- example - vpn
- example - redis
- proto : tcp
ports : 5665
group_name : example - vpn
cidr_ip :
- 172.16 .1 .0 / 24
- 172.16 .17 .0 / 24
2017-07-17 02:03:31 +00:00
cidr_ipv6 :
- 2607 : F8B0 : : / 32
- 64 : ff9b : : / 96
2017-01-29 16:29:54 +00:00
group_id :
- sg - edcd9784
2017-06-26 13:07:29 +00:00
- name : " Delete group by its id "
ec2_group :
group_id : sg - 33 b4ee5b
state : absent
2014-09-26 01:01:01 +00:00
'''
2017-08-01 10:53:43 +00:00
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
'''
2017-05-12 18:54:25 +00:00
import json
2017-01-29 16:29:54 +00:00
import re
from ansible . module_utils . basic import AnsibleModule
2017-07-17 02:03:31 +00:00
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
2017-08-22 15:11:38 +00:00
from ansible . module_utils . ec2 import boto3_tag_list_to_ansible_dict , ansible_dict_to_boto3_tag_list , compare_aws_tags
2017-08-01 10:53:43 +00:00
from ansible . module_utils . ec2 import AWSRetry
2017-12-20 19:57:47 +00:00
from ansible . module_utils . network . common . utils import to_ipv6_network , to_subnet
2017-07-17 02:03:31 +00:00
import traceback
2017-01-29 16:29:54 +00:00
2014-09-26 01:01:01 +00:00
try :
2017-07-17 02:03:31 +00:00
import botocore
2014-09-26 01:01:01 +00:00
except ImportError :
2017-07-17 02:03:31 +00:00
pass # caught by imported HAS_BOTO3
2017-03-26 13:33:29 +00:00
2014-09-26 01:01:01 +00:00
2017-08-01 10:53:43 +00:00
@AWSRetry.backoff ( tries = 5 , delay = 5 , backoff = 2.0 )
def get_security_groups_with_backoff ( connection , * * kwargs ) :
return connection . describe_security_groups ( * * kwargs )
2017-05-12 18:54:25 +00:00
def deduplicate_rules_args ( rules ) :
""" Returns unique rules """
if rules is None :
return None
return list ( dict ( zip ( ( json . dumps ( r , sort_keys = True ) for r in rules ) , rules ) ) . values ( ) )
2014-12-17 11:06:05 +00:00
def make_rule_key ( prefix , rule , group_id , cidr_ip ) :
2017-07-17 02:03:31 +00:00
if ' proto ' in rule :
2014-12-17 17:01:50 +00:00
proto , from_port , to_port = [ rule . get ( x , None ) for x in ( ' proto ' , ' from_port ' , ' to_port ' ) ]
2017-07-17 02:03:31 +00:00
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 '
2014-12-17 11:06:05 +00:00
key = " %s - %s - %s - %s - %s - %s " % ( prefix , proto , from_port , to_port , group_id , cidr_ip )
return key . lower ( ) . replace ( ' -none ' , ' -None ' )
2017-08-01 10:53:43 +00:00
def add_rules_to_lookup ( ipPermissions , group_id , prefix , dict ) :
2017-07-17 02:03:31 +00:00
for rule in ipPermissions :
2017-10-10 14:08:06 +00:00
for groupGrant in rule . get ( ' UserIdGroupPairs ' , [ ] ) :
2017-07-17 02:03:31 +00:00
dict [ make_rule_key ( prefix , rule , group_id , groupGrant . get ( ' GroupId ' ) ) ] = ( rule , groupGrant )
2017-10-10 14:08:06 +00:00
for ipv4Grants in rule . get ( ' IpRanges ' , [ ] ) :
2017-07-17 02:03:31 +00:00
dict [ make_rule_key ( prefix , rule , group_id , ipv4Grants . get ( ' CidrIp ' ) ) ] = ( rule , ipv4Grants )
2017-10-10 14:08:06 +00:00
for ipv6Grants in rule . get ( ' Ipv6Ranges ' , [ ] ) :
2017-07-17 02:03:31 +00:00
dict [ make_rule_key ( prefix , rule , group_id , ipv6Grants . get ( ' CidrIpv6 ' ) ) ] = ( rule , ipv6Grants )
2014-09-26 01:01:01 +00:00
2014-11-14 02:22:51 +00:00
def validate_rule ( module , rule ) :
2017-07-17 02:03:31 +00:00
VALID_PARAMS = ( ' cidr_ip ' , ' cidr_ipv6 ' ,
2014-11-14 02:22:51 +00:00
' group_id ' , ' group_name ' , ' group_desc ' ,
2017-10-25 01:18:56 +00:00
' proto ' , ' from_port ' , ' to_port ' , ' rule_desc ' )
2016-09-02 19:09:58 +00:00
if not isinstance ( rule , dict ) :
module . fail_json ( msg = ' Invalid rule parameter type [ %s ]. ' % type ( rule ) )
2014-11-14 02:22:51 +00:00
for k in rule :
if k not in VALID_PARAMS :
module . fail_json ( msg = ' Invalid rule parameter \' {} \' ' . format ( k ) )
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 ' )
2017-07-17 02:03:31 +00:00
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 " )
2014-11-14 02:22:51 +00:00
elif ' group_id ' in rule and ' group_name ' in rule :
module . fail_json ( msg = ' Specify group_id OR group_name, not both ' )
2017-08-01 10:53:43 +00:00
def get_target_from_rule ( module , client , rule , name , group , groups , vpc_id ) :
2014-09-26 01:01:01 +00:00
"""
Returns tuple of ( group_id , ip ) after validating rule params .
rule : Dict describing a rule .
name : Name of the security group being managed .
groups : Dict of all available security groups .
AWS accepts an ip range or a security group as target of a rule . This
function validate the rule specification and return either a non - None
group_id or a non - None ip range .
"""
2017-11-21 18:24:37 +00:00
FOREIGN_SECURITY_GROUP_REGEX = r ' ^( \ S+)/(sg- \ S+)/( \ S+) '
2014-09-26 01:01:01 +00:00
group_id = None
group_name = None
ip = None
2017-07-17 02:03:31 +00:00
ipv6 = None
2014-09-26 01:01:01 +00:00
target_group_created = False
2017-07-17 02:03:31 +00:00
2014-09-26 01:01:01 +00:00
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 " )
2017-07-17 02:03:31 +00:00
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 " )
2014-09-26 01:01:01 +00:00
elif ' group_id ' in rule and ' group_name ' in rule :
module . fail_json ( msg = " Specify group_id OR group_name, not both " )
2017-07-17 02:03:31 +00:00
elif ' cidr_ip ' in rule and ' cidr_ipv6 ' in rule :
module . fail_json ( msg = " Specify cidr_ip OR cidr_ipv6, not both " )
2017-08-01 10:53:43 +00:00
elif rule . get ( ' group_id ' ) and re . match ( FOREIGN_SECURITY_GROUP_REGEX , rule [ ' group_id ' ] ) :
2015-05-22 10:34:41 +00:00
# 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 ( )
2017-08-01 10:53:43 +00:00
group_instance = dict ( GroupId = group_id , GroupName = group_name )
2015-05-22 10:34:41 +00:00
groups [ group_id ] = group_instance
groups [ group_name ] = group_instance
2014-09-26 01:01:01 +00:00
elif ' group_id ' in rule :
group_id = rule [ ' group_id ' ]
elif ' group_name ' in rule :
group_name = rule [ ' group_name ' ]
2015-04-27 18:26:13 +00:00
if group_name == name :
2017-08-01 10:53:43 +00:00
group_id = group [ ' GroupId ' ]
2014-09-26 01:01:01 +00:00
groups [ group_id ] = group
groups [ group_name ] = group
2017-09-13 18:19:05 +00:00
elif group_name in groups and group . get ( ' VpcId ' ) and groups [ group_name ] . get ( ' VpcId ' ) :
# both are VPC groups, this is ok
group_id = groups [ group_name ] [ ' GroupId ' ]
elif group_name in groups and not ( group . get ( ' VpcId ' ) or groups [ group_name ] . get ( ' VpcId ' ) ) :
# both are EC2 classic, this is ok
2017-08-01 10:53:43 +00:00
group_id = groups [ group_name ] [ ' GroupId ' ]
2014-09-26 01:01:01 +00:00
else :
2017-09-13 18:19:05 +00:00
# if we got here, either the target group does not exist, or there
# is a mix of EC2 classic + VPC groups. Mixing of EC2 classic + VPC
# is bad, so we have to create a new SG because no compatible group
# exists
2014-09-26 01:01:01 +00:00
if not rule . get ( ' group_desc ' , ' ' ) . strip ( ) :
2017-07-17 02:03:31 +00:00
module . fail_json ( msg = " group %s will be automatically created by rule %s and "
" no description was provided " % ( group_name , rule ) )
2014-09-26 01:01:01 +00:00
if not module . check_mode :
2017-08-01 10:53:43 +00:00
params = dict ( GroupName = group_name , Description = rule [ ' group_desc ' ] )
if vpc_id :
params [ ' VpcId ' ] = vpc_id
auto_group = client . create_security_group ( * * params )
group_id = auto_group [ ' GroupId ' ]
2014-09-26 01:01:01 +00:00
groups [ group_id ] = auto_group
groups [ group_name ] = auto_group
target_group_created = True
elif ' cidr_ip ' in rule :
ip = rule [ ' cidr_ip ' ]
2017-07-17 02:03:31 +00:00
elif ' cidr_ipv6 ' in rule :
ipv6 = rule [ ' cidr_ipv6 ' ]
2014-09-26 01:01:01 +00:00
2017-07-17 02:03:31 +00:00
return group_id , ip , ipv6 , target_group_created
2014-09-26 01:01:01 +00:00
2017-01-29 16:29:54 +00:00
def ports_expand ( ports ) :
# takes a list of ports and returns a list of (port_from, port_to)
ports_expanded = [ ]
for port in ports :
if not isinstance ( port , str ) :
ports_expanded . append ( ( port , ) * 2 )
elif ' - ' in port :
2017-04-04 19:22:47 +00:00
ports_expanded . append ( tuple ( p . strip ( ) for p in port . split ( ' - ' , 1 ) ) )
2017-01-29 16:29:54 +00:00
else :
ports_expanded . append ( ( port . strip ( ) , ) * 2 )
return ports_expanded
def rule_expand_ports ( rule ) :
# takes a rule dict and returns a list of expanded rule dicts
if ' ports ' not in rule :
return [ rule ]
ports = rule [ ' ports ' ] if isinstance ( rule [ ' ports ' ] , list ) else [ rule [ ' ports ' ] ]
rule_expanded = [ ]
for from_to in ports_expand ( ports ) :
temp_rule = rule . copy ( )
del temp_rule [ ' ports ' ]
temp_rule [ ' from_port ' ] , temp_rule [ ' to_port ' ] = from_to
rule_expanded . append ( temp_rule )
return rule_expanded
def rules_expand_ports ( rules ) :
# takes a list of rules and expands it based on 'ports'
if not rules :
return rules
return [ rule for rule_complex in rules
for rule in rule_expand_ports ( rule_complex ) ]
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 ] ]
2017-07-17 02:03:31 +00:00
source_types_all = ( ' cidr_ip ' , ' cidr_ipv6 ' , ' group_id ' , ' group_name ' )
2017-01-29 16:29:54 +00:00
rule_expanded = [ ]
for source in sources :
temp_rule = rule . copy ( )
for s in source_types_all :
temp_rule . pop ( s , None )
temp_rule [ source_type ] = source
rule_expanded . append ( temp_rule )
return rule_expanded
def rule_expand_sources ( rule ) :
# takes a rule dict and returns a list of expanded rule discts
2017-07-17 02:03:31 +00:00
source_types = ( stype for stype in ( ' cidr_ip ' , ' cidr_ipv6 ' , ' group_id ' , ' group_name ' ) if stype in rule )
2017-01-29 16:29:54 +00:00
return [ r for stype in source_types
for r in rule_expand_source ( rule , stype ) ]
def rules_expand_sources ( rules ) :
# takes a list of rules and expands it based on 'cidr_ip', 'group_id', 'group_name'
if not rules :
return rules
return [ rule for rule_complex in rules
for rule in rule_expand_sources ( rule_complex ) ]
2017-10-25 01:18:56 +00:00
def update_rules_description ( module , client , rule_type , group_id , ip_permissions ) :
try :
if rule_type == " in " :
client . update_security_group_rule_descriptions_ingress ( GroupId = group_id , IpPermissions = [ ip_permissions ] )
if rule_type == " out " :
client . update_security_group_rule_descriptions_egress ( GroupId = group_id , IpPermissions = [ ip_permissions ] )
except botocore . exceptions . ClientError as e :
module . fail_json (
msg = " Unable to update rule description for group %s : %s " %
( group_id , e ) ,
exceptin = traceback . format_exc ( ) , * * camel_dict_to_snake_dict ( e . response ) )
2017-07-17 02:03:31 +00:00
def authorize_ip ( type , changed , client , group , groupRules ,
ip , ip_permission , module , rule , ethertype ) :
# If rule already exists, don't later delete it
2017-12-20 19:57:47 +00:00
for this_ip in ip :
split_addr = this_ip . split ( ' / ' )
if len ( split_addr ) == 2 :
# this_ip is a IPv4 or IPv6 CIDR that may or may not have host bits set
# Get the network bits.
try :
thisip = to_subnet ( split_addr [ 0 ] , split_addr [ 1 ] )
except ValueError :
thisip = to_ipv6_network ( split_addr [ 0 ] ) + " / " + split_addr [ 1 ]
if thisip != this_ip :
module . warn ( " One of your CIDR addresses ( {0} ) has host bits set. To get rid of this warning, "
" check the network mask and make sure that only network bits are set: {1} . " . format ( this_ip , thisip ) )
else :
thisip = this_ip
2017-08-01 10:53:43 +00:00
rule_id = make_rule_key ( type , rule , group [ ' GroupId ' ] , thisip )
2017-07-17 02:03:31 +00:00
if rule_id in groupRules :
2017-10-25 01:18:56 +00:00
# update the rule description
if ' rule_desc ' in rule :
desired_rule_desc = rule . get ( ' rule_desc ' ) or ' '
current_rule = groupRules [ rule_id ] [ 0 ] . get ( ' IpRanges ' ) or groupRules [ rule_id ] [ 0 ] . get ( ' Ipv6Ranges ' )
if desired_rule_desc != current_rule [ 0 ] . get ( ' Description ' , ' ' ) :
if not module . check_mode :
ip_permission = serialize_ip_grant ( rule , thisip , ethertype )
update_rules_description ( module , client , type , group [ ' GroupId ' ] , ip_permission )
changed = True
# remove the rule from groupRules to avoid purging it later
2017-07-17 02:03:31 +00:00
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 " :
2017-08-01 10:53:43 +00:00
client . authorize_security_group_ingress ( GroupId = group [ ' GroupId ' ] ,
2017-07-17 02:03:31 +00:00
IpPermissions = [ ip_permission ] )
elif type == " out " :
2017-08-01 10:53:43 +00:00
client . authorize_security_group_egress ( GroupId = group [ ' GroupId ' ] ,
2017-07-17 02:03:31 +00:00
IpPermissions = [ ip_permission ] )
except botocore . exceptions . ClientError as e :
module . fail_json ( msg = " Unable to authorize %s for ip %s security group ' %s ' - %s " %
2017-08-01 10:53:43 +00:00
( type , thisip , group [ ' GroupName ' ] , e ) ,
2017-07-17 02:03:31 +00:00
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 } ] }
2017-08-02 00:26:38 +00:00
2017-10-25 01:18:56 +00:00
if ' rule_desc ' in rule :
permission [ ' UserIdGroupPairs ' ] [ 0 ] [ ' Description ' ] = rule . get ( ' rule_desc ' ) or ' '
2017-08-22 15:11:38 +00:00
return fix_port_and_protocol ( permission )
2017-07-17 02:03:31 +00:00
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 ,
2017-08-22 15:11:38 +00:00
' UserIdGroupPairs ' : [ { ' GroupId ' : grant [ ' GroupId ' ] } ]
2017-07-17 02:03:31 +00:00
}
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 ]
}
2017-08-22 15:11:38 +00:00
return fix_port_and_protocol ( permission )
2017-07-17 02:03:31 +00:00
def serialize_ip_grant ( rule , thisip , ethertype ) :
permission = { ' IpProtocol ' : rule [ ' proto ' ] ,
' FromPort ' : rule [ ' from_port ' ] ,
' ToPort ' : rule [ ' to_port ' ] }
if ethertype == " ipv4 " :
2017-08-22 15:11:38 +00:00
permission [ ' IpRanges ' ] = [ { ' CidrIp ' : thisip } ]
2017-10-25 01:18:56 +00:00
if ' rule_desc ' in rule :
permission [ ' IpRanges ' ] [ 0 ] [ ' Description ' ] = rule . get ( ' rule_desc ' ) or ' '
2017-07-17 02:03:31 +00:00
elif ethertype == " ipv6 " :
2017-08-22 15:11:38 +00:00
permission [ ' Ipv6Ranges ' ] = [ { ' CidrIpv6 ' : thisip } ]
2017-10-25 01:18:56 +00:00
if ' rule_desc ' in rule :
permission [ ' Ipv6Ranges ' ] [ 0 ] [ ' Description ' ] = rule . get ( ' rule_desc ' ) or ' '
2017-08-02 00:26:38 +00:00
2017-08-22 15:11:38 +00:00
return fix_port_and_protocol ( permission )
2017-07-17 02:03:31 +00:00
2017-08-22 15:11:38 +00:00
def fix_port_and_protocol ( permission ) :
2017-08-02 00:26:38 +00:00
for key in [ ' FromPort ' , ' ToPort ' ] :
2017-08-22 15:11:38 +00:00
if key in permission :
if permission [ key ] is None :
del permission [ key ]
else :
permission [ key ] = int ( permission [ key ] )
permission [ ' IpProtocol ' ] = str ( permission [ ' IpProtocol ' ] )
return permission
2017-08-02 00:26:38 +00:00
2017-10-25 01:18:56 +00:00
def check_rule_desc_update_for_group_grant ( client , module , rule , group , groupRules , rule_id , group_id , rule_type , changed ) :
if ' rule_desc ' in rule :
current_rule_description = rule . get ( ' rule_desc ' ) or ' '
if current_rule_description != groupRules [ rule_id ] [ 0 ] [ ' UserIdGroupPairs ' ] [ 0 ] . get ( ' Description ' , ' ' ) :
if not module . check_mode :
ip_permission = serialize_group_grant ( group_id , rule )
update_rules_description ( module , client , rule_type , group [ ' GroupId ' ] , ip_permission )
changed = True
return changed
def has_rule_description_attr ( client ) :
return hasattr ( client , " update_security_group_rule_descriptions_egress " )
2014-09-26 01:01:01 +00:00
def main ( ) :
argument_spec = ec2_argument_spec ( )
argument_spec . update ( dict (
2017-06-26 13:07:29 +00:00
name = dict ( ) ,
group_id = dict ( ) ,
description = dict ( ) ,
vpc_id = dict ( ) ,
2017-01-29 07:28:53 +00:00
rules = dict ( type = ' list ' ) ,
rules_egress = dict ( type = ' list ' ) ,
2017-03-17 20:14:20 +00:00
state = dict ( default = ' present ' , type = ' str ' , choices = [ ' present ' , ' absent ' ] ) ,
2017-01-29 07:28:53 +00:00
purge_rules = dict ( default = True , required = False , type = ' bool ' ) ,
2017-08-22 15:11:38 +00:00
purge_rules_egress = dict ( default = True , required = False , type = ' bool ' ) ,
tags = dict ( required = False , type = ' dict ' , aliases = [ ' resource_tags ' ] ) ,
purge_tags = dict ( default = True , required = False , type = ' bool ' )
2017-01-29 07:28:53 +00:00
)
2014-09-26 01:01:01 +00:00
)
module = AnsibleModule (
argument_spec = argument_spec ,
supports_check_mode = True ,
2017-06-26 13:07:29 +00:00
required_one_of = [ [ ' name ' , ' group_id ' ] ] ,
required_if = [ [ ' state ' , ' present ' , [ ' name ' ] ] ] ,
2014-09-26 01:01:01 +00:00
)
2017-07-17 02:03:31 +00:00
if not HAS_BOTO3 :
module . fail_json ( msg = ' boto3 required for this module ' )
2015-04-01 23:16:54 +00:00
2014-09-26 01:01:01 +00:00
name = module . params [ ' name ' ]
2017-06-26 13:07:29 +00:00
group_id = module . params [ ' group_id ' ]
2014-09-26 01:01:01 +00:00
description = module . params [ ' description ' ]
vpc_id = module . params [ ' vpc_id ' ]
2017-05-12 18:54:25 +00:00
rules = deduplicate_rules_args ( rules_expand_sources ( rules_expand_ports ( module . params [ ' rules ' ] ) ) )
rules_egress = deduplicate_rules_args ( rules_expand_sources ( rules_expand_ports ( module . params [ ' rules_egress ' ] ) ) )
2014-09-26 01:01:01 +00:00
state = module . params . get ( ' state ' )
purge_rules = module . params [ ' purge_rules ' ]
purge_rules_egress = module . params [ ' purge_rules_egress ' ]
2017-08-22 15:11:38 +00:00
tags = module . params [ ' tags ' ]
purge_tags = module . params [ ' purge_tags ' ]
2014-09-26 01:01:01 +00:00
2017-03-07 19:55:17 +00:00
if state == ' present ' and not description :
module . fail_json ( msg = ' Must provide description when state is present. ' )
2014-09-26 01:01:01 +00:00
changed = False
2017-07-17 02:03:31 +00:00
region , ec2_url , aws_connect_params = get_aws_connection_info ( module , boto3 = True )
2017-11-07 18:56:17 +00:00
client = boto3_conn ( module , conn_type = ' client ' , resource = ' ec2 ' ,
endpoint = ec2_url , region = region , * * aws_connect_params )
2017-10-25 01:18:56 +00:00
if not has_rule_description_attr ( client ) :
all_rules = rules if rules else [ ] + rules_egress if rules_egress else [ ]
if any ( ' rule_desc ' in rule for rule in all_rules ) :
module . fail_json ( msg = " Using rule descriptions requires botocore version >= 1.7.2. " )
2014-09-26 01:01:01 +00:00
group = None
2017-07-17 02:03:31 +00:00
groups = dict ( )
security_groups = [ ]
# do get all security groups
# find if the group is present
2017-03-26 13:33:29 +00:00
try :
2017-08-01 10:53:43 +00:00
response = get_security_groups_with_backoff ( client )
security_groups = response . get ( ' SecurityGroups ' , [ ] )
2017-07-17 02:03:31 +00:00
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 :
2017-08-01 10:53:43 +00:00
groups [ sg [ ' GroupId ' ] ] = sg
groupName = sg [ ' GroupName ' ]
2017-07-17 02:03:31 +00:00
if groupName in groups :
2015-04-28 09:19:20 +00:00
# Prioritise groups from the current VPC
2017-09-13 18:19:05 +00:00
# even if current VPC is EC2-Classic
if groups [ groupName ] . get ( ' VpcId ' ) == vpc_id :
# Group saved already matches current VPC, change nothing
pass
elif vpc_id is None and groups [ groupName ] . get ( ' VpcId ' ) is None :
# We're in EC2 classic, and the group already saved is as well
# No VPC groups can be used alongside EC2 classic groups
pass
else :
# the current SG stored has no direct match, so we can replace it
2017-08-01 10:53:43 +00:00
groups [ groupName ] = sg
2015-04-28 09:19:20 +00:00
else :
2017-08-01 10:53:43 +00:00
groups [ groupName ] = sg
2014-09-26 01:01:01 +00:00
2017-09-13 18:19:05 +00:00
if group_id and sg [ ' GroupId ' ] == group_id :
group = sg
2017-10-10 17:15:11 +00:00
elif groupName == name and ( vpc_id is None or sg . get ( ' VpcId ' ) == vpc_id ) :
2017-09-13 18:19:05 +00:00
group = sg
2014-09-26 01:01:01 +00:00
# Ensure requested group is absent
if state == ' absent ' :
if group :
2017-03-26 13:33:29 +00:00
# found a match, delete it
2014-09-26 01:01:01 +00:00
try :
2016-03-30 18:48:20 +00:00
if not module . check_mode :
2017-08-01 10:53:43 +00:00
client . delete_security_group ( GroupId = group [ ' GroupId ' ] )
2017-07-17 02:03:31 +00:00
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 ) )
2014-09-26 01:01:01 +00:00
else :
group = None
changed = True
else :
2017-03-26 13:33:29 +00:00
# no match found, no changes required
pass
2014-09-26 01:01:01 +00:00
# Ensure requested group is present
elif state == ' present ' :
if group :
2017-03-26 13:33:29 +00:00
# existing group
2017-08-01 10:53:43 +00:00
if group [ ' Description ' ] != description :
2017-10-18 13:21:55 +00:00
module . warn ( " Group description does not match existing group. Descriptions cannot be changed without deleting "
" and re-creating the security group. Try using state=absent to delete, then rerunning this task. " )
2014-09-26 01:01:01 +00:00
# if the group doesn't exist, create it now
else :
2017-03-26 13:33:29 +00:00
# no match found, create it
2014-09-26 01:01:01 +00:00
if not module . check_mode :
2017-08-01 10:53:43 +00:00
params = dict ( GroupName = name , Description = description )
if vpc_id :
params [ ' VpcId ' ] = vpc_id
group = client . create_security_group ( * * params )
2014-09-26 01:01:01 +00:00
# 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
2017-08-01 10:53:43 +00:00
while True :
group = get_security_groups_with_backoff ( client , GroupIds = [ group [ ' GroupId ' ] ] ) [ ' SecurityGroups ' ] [ 0 ]
2017-09-13 18:19:05 +00:00
if group . get ( ' VpcId ' ) and not group . get ( ' IpPermissionsEgress ' ) :
2017-08-22 15:11:38 +00:00
pass
2017-08-01 10:53:43 +00:00
else :
break
2014-09-26 01:01:01 +00:00
changed = True
2017-08-22 15:11:38 +00:00
2018-02-21 19:48:51 +00:00
if tags is not None and group is not None :
2017-08-22 15:11:38 +00:00
current_tags = boto3_tag_list_to_ansible_dict ( group . get ( ' Tags ' , [ ] ) )
tags_need_modify , tags_to_delete = compare_aws_tags ( current_tags , tags , purge_tags )
if tags_to_delete :
2018-02-21 19:48:51 +00:00
if not module . check_mode :
try :
client . delete_tags ( Resources = [ group [ ' GroupId ' ] ] , Tags = [ { ' Key ' : tag } for tag in tags_to_delete ] )
except botocore . exceptions . ClientError as e :
module . fail_json ( msg = e . message , exception = traceback . format_exc ( ) , * * camel_dict_to_snake_dict ( e . response ) )
2017-08-22 15:11:38 +00:00
changed = True
# Add/update tags
if tags_need_modify :
2018-02-21 19:48:51 +00:00
if not module . check_mode :
try :
client . create_tags ( Resources = [ group [ ' GroupId ' ] ] , Tags = ansible_dict_to_boto3_tag_list ( tags_need_modify ) )
except botocore . exceptions . ClientError as e :
module . fail_json ( msg = e . message , exception = traceback . format_exc ( ) , * * camel_dict_to_snake_dict ( e . response ) )
2017-08-22 15:11:38 +00:00
changed = True
2014-09-26 01:01:01 +00:00
else :
module . fail_json ( msg = " Unsupported state requested: %s " % state )
# create a lookup for all existing rules on the group
2017-09-08 18:54:07 +00:00
ip_permission = [ ]
2014-09-26 01:01:01 +00:00
if group :
# Manage ingress rules
groupRules = { }
2017-08-01 10:53:43 +00:00
add_rules_to_lookup ( group [ ' IpPermissions ' ] , group [ ' GroupId ' ] , ' in ' , groupRules )
2014-09-26 01:01:01 +00:00
# Now, go through all provided rules and ensure they are there.
2014-11-13 23:03:59 +00:00
if rules is not None :
2014-09-26 01:01:01 +00:00
for rule in rules :
2014-11-14 02:22:51 +00:00
validate_rule ( module , rule )
2017-08-01 10:53:43 +00:00
group_id , ip , ipv6 , target_group_created = get_target_from_rule ( module , client , rule , name ,
2017-07-17 02:03:31 +00:00
group , groups , vpc_id )
2014-09-26 01:01:01 +00:00
if target_group_created :
changed = True
if rule [ ' proto ' ] in ( ' all ' , ' -1 ' , - 1 ) :
rule [ ' proto ' ] = - 1
rule [ ' from_port ' ] = None
rule [ ' to_port ' ] = None
2017-07-17 02:03:31 +00:00
if group_id :
2017-08-01 10:53:43 +00:00
rule_id = make_rule_key ( ' in ' , rule , group [ ' GroupId ' ] , group_id )
2017-07-17 02:03:31 +00:00
if rule_id in groupRules :
2017-10-25 01:18:56 +00:00
changed = check_rule_desc_update_for_group_grant ( client , module , rule , group , groupRules ,
rule_id , group_id , rule_type = ' in ' , changed = changed )
2017-07-17 02:03:31 +00:00
del groupRules [ rule_id ]
else :
2014-09-30 00:26:06 +00:00
if not module . check_mode :
2017-07-17 02:03:31 +00:00
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
2017-10-10 14:08:06 +00:00
ip_permission . get ( ' UserIdGroupPairs ' , [ ] ) ]
2017-07-17 02:03:31 +00:00
try :
2017-08-01 10:53:43 +00:00
client . authorize_security_group_ingress ( GroupId = group [ ' GroupId ' ] , IpPermissions = [ ips ] )
2017-07-17 02:03:31 +00:00
except botocore . exceptions . ClientError as e :
module . fail_json (
msg = " Unable to authorize ingress for group %s security group ' %s ' - %s " %
2017-08-01 10:53:43 +00:00
( group_id , group [ ' GroupName ' ] , e ) ,
2017-07-17 02:03:31 +00:00
exception = traceback . format_exc ( ) , * * camel_dict_to_snake_dict ( e . response ) )
2014-09-30 00:26:06 +00:00
changed = True
2017-07-17 02:03:31 +00:00
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 " )
2014-09-26 01:01:01 +00:00
# Finally, remove anything left in the groupRules -- these will be defunct rules
if purge_rules :
2016-12-13 13:28:21 +00:00
for ( rule , grant ) in groupRules . values ( ) :
2017-07-17 02:03:31 +00:00
ip_permission = serialize_revoke ( grant , rule )
2015-01-24 05:52:37 +00:00
if not module . check_mode :
2017-07-17 02:03:31 +00:00
try :
2017-08-01 10:53:43 +00:00
client . revoke_security_group_ingress ( GroupId = group [ ' GroupId ' ] , IpPermissions = [ ip_permission ] )
2017-07-17 02:03:31 +00:00
except botocore . exceptions . ClientError as e :
module . fail_json (
msg = " Unable to revoke ingress for security group ' %s ' - %s " %
2017-08-01 10:53:43 +00:00
( group [ ' GroupName ' ] , e ) ,
2017-07-17 02:03:31 +00:00
exception = traceback . format_exc ( ) , * * camel_dict_to_snake_dict ( e . response ) )
2015-01-24 05:52:37 +00:00
changed = True
2014-09-26 01:01:01 +00:00
# Manage egress rules
groupRules = { }
2017-08-01 10:53:43 +00:00
add_rules_to_lookup ( group [ ' IpPermissionsEgress ' ] , group [ ' GroupId ' ] , ' out ' , groupRules )
2014-09-26 01:01:01 +00:00
# Now, go through all provided rules and ensure they are there.
2014-11-13 23:03:59 +00:00
if rules_egress is not None :
2014-09-26 01:01:01 +00:00
for rule in rules_egress :
2014-11-14 02:22:51 +00:00
validate_rule ( module , rule )
2017-08-01 10:53:43 +00:00
group_id , ip , ipv6 , target_group_created = get_target_from_rule ( module , client , rule , name ,
2017-07-17 02:03:31 +00:00
group , groups , vpc_id )
2014-09-26 01:01:01 +00:00
if target_group_created :
changed = True
if rule [ ' proto ' ] in ( ' all ' , ' -1 ' , - 1 ) :
rule [ ' proto ' ] = - 1
rule [ ' from_port ' ] = None
rule [ ' to_port ' ] = None
2017-07-17 02:03:31 +00:00
if group_id :
2017-08-01 10:53:43 +00:00
rule_id = make_rule_key ( ' out ' , rule , group [ ' GroupId ' ] , group_id )
2017-07-17 02:03:31 +00:00
if rule_id in groupRules :
2017-10-25 01:18:56 +00:00
changed = check_rule_desc_update_for_group_grant ( client , module , rule , group , groupRules ,
rule_id , group_id , rule_type = ' out ' , changed = changed )
2017-07-17 02:03:31 +00:00
del groupRules [ rule_id ]
2014-09-30 00:26:06 +00:00
else :
if not module . check_mode :
2017-07-17 02:03:31 +00:00
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
2017-10-10 14:08:06 +00:00
ip_permission . get ( ' UserIdGroupPairs ' , [ ] ) ]
2017-07-17 02:03:31 +00:00
try :
2017-08-01 10:53:43 +00:00
client . authorize_security_group_egress ( GroupId = group [ ' GroupId ' ] , IpPermissions = [ ips ] )
2017-07-17 02:03:31 +00:00
except botocore . exceptions . ClientError as e :
module . fail_json (
msg = " Unable to authorize egress for group %s security group ' %s ' - %s " %
2017-08-01 10:53:43 +00:00
( group_id , group [ ' GroupName ' ] , e ) ,
2017-07-17 02:03:31 +00:00
exception = traceback . format_exc ( ) , * * camel_dict_to_snake_dict ( e . response ) )
2014-09-30 00:26:06 +00:00
changed = True
2017-07-17 02:03:31 +00:00
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 " )
2018-03-28 16:53:35 +00:00
elif ' VpcId ' in group :
2017-09-13 18:19:05 +00:00
# when no egress rules are specified and we're in a VPC,
2014-09-26 01:01:01 +00:00
# we add in a default allow all out rule, which was the
# default behavior before egress rules were added
2017-08-01 10:53:43 +00:00
default_egress_rule = ' out--1-None-None- ' + group [ ' GroupId ' ] + ' -0.0.0.0/0 '
2014-09-26 01:01:01 +00:00
if default_egress_rule not in groupRules :
2016-08-02 20:01:48 +00:00
if not module . check_mode :
2017-07-17 02:03:31 +00:00
ip_permission = [ { ' IpProtocol ' : ' -1 ' ,
' IpRanges ' : [ { ' CidrIp ' : ' 0.0.0.0/0 ' } ]
}
]
try :
2017-08-01 10:53:43 +00:00
client . authorize_security_group_egress ( GroupId = group [ ' GroupId ' ] , IpPermissions = ip_permission )
2017-07-17 02:03:31 +00:00
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 ' ,
2017-08-01 10:53:43 +00:00
group [ ' GroupName ' ] ,
2017-07-17 02:03:31 +00:00
e ) ,
exception = traceback . format_exc ( ) , * * camel_dict_to_snake_dict ( e . response ) )
2014-09-26 01:01:01 +00:00
changed = True
else :
# make sure the default egress rule is not removed
del groupRules [ default_egress_rule ]
# Finally, remove anything left in the groupRules -- these will be defunct rules
2018-01-09 18:44:13 +00:00
if purge_rules_egress and ' VpcId ' in group :
2016-12-13 13:28:21 +00:00
for ( rule , grant ) in groupRules . values ( ) :
2017-07-17 02:03:31 +00:00
# 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 :
2017-08-01 10:53:43 +00:00
client . revoke_security_group_egress ( GroupId = group [ ' GroupId ' ] , IpPermissions = [ ip_permission ] )
2017-07-17 02:03:31 +00:00
except botocore . exceptions . ClientError as e :
module . fail_json ( msg = " Unable to revoke egress for ip %s security group ' %s ' - %s " %
2017-08-01 10:53:43 +00:00
( grant , group [ ' GroupName ' ] , e ) ,
2017-07-17 02:03:31 +00:00
exception = traceback . format_exc ( ) , * * camel_dict_to_snake_dict ( e . response ) )
changed = True
2014-09-26 01:01:01 +00:00
if group :
2017-08-01 10:53:43 +00:00
security_group = get_security_groups_with_backoff ( client , GroupIds = [ group [ ' GroupId ' ] ] ) [ ' SecurityGroups ' ] [ 0 ]
security_group = camel_dict_to_snake_dict ( security_group )
2017-08-22 15:11:38 +00:00
security_group [ ' tags ' ] = boto3_tag_list_to_ansible_dict ( security_group . get ( ' tags ' , [ ] ) ,
2017-08-01 10:53:43 +00:00
tag_name_key_name = ' key ' , tag_value_key_name = ' value ' )
module . exit_json ( changed = changed , * * security_group )
2014-09-26 01:01:01 +00:00
else :
module . exit_json ( changed = changed , group_id = None )
2017-08-22 15:11:38 +00:00
2016-12-05 17:08:15 +00:00
if __name__ == ' __main__ ' :
main ( )