@ -22,10 +22,12 @@ description:
- This module associates AWS EC2 elastic IP addresses with instances
- This module associates AWS EC2 elastic IP addresses with instances
version_added : " 1.4 "
version_added : " 1.4 "
options :
options :
instan ce_id:
dev ice_id:
description :
description :
- The EC2 instance id
- The id of the device for the EIP . Can be an EC2 Instance id or Elastic Network Interface ( ENI ) id .
required : false
required : false
aliases : [ instance_id ]
version_added : " 2.0 "
public_ip :
public_ip :
description :
description :
- The elastic IP address to associate with the instance .
- The elastic IP address to associate with the instance .
@ -56,9 +58,15 @@ options:
required : false
required : false
default : false
default : false
version_added : " 1.6 "
version_added : " 1.6 "
release_on_disassociation :
description :
- whether or not to automatically release the EIP when it is disassociated
required : false
default : false
version_added : " 2.0 "
extends_documentation_fragment : aws
extends_documentation_fragment : aws
author : " Lorin Hochstein (@lorin) <lorin@nimbisservices.com> "
author : " Lorin Hochstein (@lorin) <lorin@nimbisservices.com> "
author : " Rick Mendes (@rickmendes) <rmendes@illumina.com> "
notes :
notes :
- This module will return C ( public_ip ) on success , which will contain the
- This module will return C ( public_ip ) on success , which will contain the
public IP address associated with the instance .
public IP address associated with the instance .
@ -66,35 +74,36 @@ notes:
the cloud instance is reachable via the new address . Use wait_for and
the cloud instance is reachable via the new address . Use wait_for and
pause to delay further playbook execution until the instance is reachable ,
pause to delay further playbook execution until the instance is reachable ,
if necessary .
if necessary .
- This module returns multiple changed statuses on disassociation or release .
It returns an overall status based on any changes occuring . It also returns
individual changed statuses for disassociation and release .
'''
'''
EXAMPLES = '''
EXAMPLES = '''
- name : associate an elastic IP with an instance
- name : associate an elastic IP with an instance
ec2_eip : instance_id = i - 1212 f003 ip = 93.184 .216 .119
ec2_eip : device_id = i - 1212 f003 ip = 93.184 .216 .119
- name : associate an elastic IP with a device
ec2_eip : device_id = eni - c8ad70f3 ip = 93.184 .216 .119
- name : disassociate an elastic IP from an instance
- name : disassociate an elastic IP from an instance
ec2_eip : instance_id = i - 1212 f003 ip = 93.184 .216 .119 state = absent
ec2_eip : device_id = i - 1212 f003 ip = 93.184 .216 .119 state = absent
- name : disassociate an elastic IP with a device
ec2_eip : device_id = eni - c8ad70f3 ip = 93.184 .216 .119 state = absent
- name : allocate a new elastic IP and associate it with an instance
- name : allocate a new elastic IP and associate it with an instance
ec2_eip : instance_id = i - 1212 f003
ec2_eip : device_id = i - 1212 f003
- name : allocate a new elastic IP without associating it to anything
- name : allocate a new elastic IP without associating it to anything
action : ec2_eip
action : ec2_eip
register : eip
register : eip
- name : output the IP
- name : output the IP
debug : msg = " Allocated IP is {{ eip.public_ip }} "
debug : msg = " Allocated IP is {{ eip.public_ip }} "
- name : another way of allocating an elastic IP without associating it to anything
- name : another way of allocating an elastic IP without associating it to anything
ec2_eip : state = ' present '
ec2_eip : state = ' present '
- name : provision new instances with ec2
- name : provision new instances with ec2
ec2 : keypair = mykey instance_type = c1 . medium image = emi - 40603 AD1 wait = yes '''
ec2 : keypair = mykey instance_type = c1 . medium image = emi - 40603 AD1 wait = yes '''
''' group=webserver count=3
''' group=webserver count=3
register : ec2
register : ec2
- name : associate new elastic IPs with each of the instances
- name : associate new elastic IPs with each of the instances
ec2_eip : " instan ce_id={{ item }} "
ec2_eip : " dev ice_id={{ item }} "
with_items : ec2 . instance_ids
with_items : ec2 . instance_ids
- name : allocate a new elastic IP inside a VPC in us - west - 2
- name : allocate a new elastic IP inside a VPC in us - west - 2
ec2_eip : region = us - west - 2 in_vpc = yes
ec2_eip : region = us - west - 2 in_vpc = yes
register : eip
register : eip
@ -112,27 +121,27 @@ except ImportError:
class EIPException ( Exception ) :
class EIPException ( Exception ) :
pass
pass
def associate_ip_and_device ( ec2 , address , device_id , check_mode , isinstance = True ) :
def associate_ip_and_instance ( ec2 , address , instance_id , check_mode ) :
if address_is_associated_with_device ( ec2 , address , device_id , isinstance ) :
if address_is_associated_with_instance ( ec2 , address , instance_id ) :
return { ' changed ' : False }
return { ' changed ' : False }
# If we're in check mode, nothing else to do
# If we're in check mode, nothing else to do
if not check_mode :
if not check_mode :
if address . domain == ' vpc ' :
if isinstance :
res = ec2 . associate_address ( instance_id ,
if address . domain == " vpc " :
allocation_id = address . allocation_id )
res = ec2 . associate_address ( device_id , allocation_id = address . allocation_id )
else :
res = ec2 . associate_address ( device_id , public_ip = address . public_ip )
else :
else :
res = ec2 . associate_address ( instance_id ,
res = ec2 . associate_address ( network_interface_id = device_id , allocation_id = address . allocation_id )
public_ip = address . public_ip )
if not res :
if not res :
raise EIPException ( ' association failed ' )
raise EIPException ( ' association failed ' )
return { ' changed ' : True }
return { ' changed ' : True }
def disassociate_ip_and_ instan ce( ec2 , address , instan ce_id, check_mod e) :
def disassociate_ip_and_ dev ice( ec2 , address , dev ice_id, check_mod e, isinstance = Tru e) :
if not address_is_associated_with_ instan ce( ec2 , address , instance_id ) :
if not address_is_associated_with_ dev ice( ec2 , address , device_id, is instance) :
return { ' changed ' : False }
return { ' changed ' : False }
# If we're in check mode, nothing else to do
# If we're in check mode, nothing else to do
@ -157,24 +166,33 @@ def _find_address_by_ip(ec2, public_ip):
raise
raise
def _find_address_by_instance_id ( ec2 , instance_id ) :
def _find_address_by_device_id ( ec2 , device_id , isinstance = True ) :
addresses = ec2 . get_all_addresses ( None , { ' instance-id ' : instance_id } )
if isinstance :
addresses = ec2 . get_all_addresses ( None , { ' instance-id ' : device_id } )
else :
addresses = ec2 . get_all_addresses ( None , { ' network-interface-id ' : device_id } )
if addresses :
if addresses :
return addresses [ 0 ]
return addresses [ 0 ]
def find_address ( ec2 , public_ip , instance_id ) :
def find_address ( ec2 , public_ip , device_id, isinstance = True ) :
""" Find an existing Elastic IP address """
""" Find an existing Elastic IP address """
if public_ip :
if public_ip :
return _find_address_by_ip ( ec2 , public_ip )
return _find_address_by_ip ( ec2 , public_ip )
elif instance_id :
elif device_id and isinstance :
return _find_address_by_instance_id ( ec2 , instance_id )
return _find_address_by_device_id ( ec2 , device_id )
elif device_id :
return _find_address_by_device_id ( ec2 , device_id , isinstance = False )
def address_is_associated_with_instance ( ec2 , address , instance_id ) :
def address_is_associated_with_device ( ec2 , address , device_id , isinstance = True ) :
""" Check if the elastic IP is currently associated with the instance """
""" Check if the elastic IP is currently associated with the device """
address = ec2 . get_all_addresses ( address . public_ip )
if address :
if address :
return address and address . instance_id == instance_id
if isinstance :
return address and address [ 0 ] . instance_id == device_id
else :
return address and address [ 0 ] . network_interface_id == device_id
return False
return False
@ -185,7 +203,7 @@ def allocate_address(ec2, domain, reuse_existing_ip_allowed):
all_addresses = ec2 . get_all_addresses ( filters = domain_filter )
all_addresses = ec2 . get_all_addresses ( filters = domain_filter )
unassociated_addresses = [ a for a in all_addresses
unassociated_addresses = [ a for a in all_addresses
if not a . instan ce_id]
if not a . dev ice_id]
if unassociated_addresses :
if unassociated_addresses :
return unassociated_addresses [ 0 ]
return unassociated_addresses [ 0 ]
@ -203,21 +221,33 @@ def release_address(ec2, address, check_mode):
return { ' changed ' : True }
return { ' changed ' : True }
def find_ instance( ec2 , instance_id ) :
def find_ device( ec2 , device_id , isinstance = True ) :
""" Attempt to find the EC2 instance and return it """
""" Attempt to find the EC2 instance and return it """
reservations = ec2 . get_all_reservations ( instance_ids = [ instance_id ] )
if isinstance :
try :
reservations = ec2 . get_all_reservations ( instance_ids = [ device_id ] )
except boto . exception . EC2ResponseError , e :
module . fail_json ( msg = str ( e ) )
if len ( reservations ) == 1 :
instances = reservations [ 0 ] . instances
if len ( instances ) == 1 :
return instances [ 0 ]
else :
try :
interfaces = ec2 . get_all_network_interfaces ( network_interface_ids = [ device_id ] )
except boto . exception . EC2ResponseError , e :
module . fail_json ( msg = str ( e ) )
if len ( reservations ) == 1 :
if len ( interfaces ) == 1 :
instances = reservations [ 0 ] . instances
return interfaces [ 0 ]
if len ( instances ) == 1 :
return instances [ 0 ]
raise EIPException ( " could not find instance " + instance_id )
raise EIPException ( " could not find instance " + device_id )
def ensure_present ( ec2 , domain , address , instance_id ,
def ensure_present ( ec2 , domain , address , dev ice_id,
reuse_existing_ip_allowed , check_mode ) :
reuse_existing_ip_allowed , check_mode , isinstance = True ):
changed = False
changed = False
# Return the EIP object since we've been given a public IP
# Return the EIP object since we've been given a public IP
@ -228,28 +258,39 @@ def ensure_present(ec2, domain, address, instance_id,
address = allocate_address ( ec2 , domain , reuse_existing_ip_allowed )
address = allocate_address ( ec2 , domain , reuse_existing_ip_allowed )
changed = True
changed = True
if instan ce_id:
if dev ice_id:
# Allocate an IP for instance since no public_ip was provided
# Allocate an IP for instance since no public_ip was provided
instance = find_instance ( ec2 , instance_id )
if isinstance :
instance = find_device ( ec2 , device_id )
# Associate address object (provided or allocated) with instance
assoc_result = associate_ip_and_device ( ec2 , address , device_id ,
check_mode )
else :
instance = find_device ( ec2 , device_id , isinstance = False )
# Associate address object (provided or allocated) with instance
assoc_result = associate_ip_and_device ( ec2 , address , device_id ,
check_mode , isinstance = False )
if instance . vpc_id :
if instance . vpc_id :
domain = ' vpc '
domain = ' vpc '
# Associate address object (provided or allocated) with instance
assoc_result = associate_ip_and_instance ( ec2 , address , instance_id ,
check_mode )
changed = changed or assoc_result [ ' changed ' ]
changed = changed or assoc_result [ ' changed ' ]
return { ' changed ' : changed , ' public_ip ' : address . public_ip }
return { ' changed ' : changed , ' public_ip ' : address . public_ip }
def ensure_absent ( ec2 , domain , address , instan ce_id, check_mod e) :
def ensure_absent ( ec2 , domain , address , dev ice_id, check_mod e, isinstance = Tru e) :
if not address :
if not address :
return { ' changed ' : False }
return { ' changed ' : False }
# disassociating address from instance
# disassociating address from instance
if instance_id :
if device_id :
return disassociate_ip_and_instance ( ec2 , address , instance_id ,
if isinstance :
check_mode )
return disassociate_ip_and_device ( ec2 , address , device_id ,
check_mode )
else :
return disassociate_ip_and_device ( ec2 , address , device_id ,
check_mode , isinstance = False )
# releasing address
# releasing address
else :
else :
return release_address ( ec2 , address , check_mode )
return release_address ( ec2 , address , check_mode )
@ -258,13 +299,14 @@ def ensure_absent(ec2, domain, address, instance_id, check_mode):
def main ( ) :
def main ( ) :
argument_spec = ec2_argument_spec ( )
argument_spec = ec2_argument_spec ( )
argument_spec . update ( dict (
argument_spec . update ( dict (
instan ce_id= dict ( required = False ) ,
dev ice_id= dict ( required = False , aliases = [ ' instance_id ' ] ) ,
public_ip = dict ( required = False , aliases = [ ' ip ' ] ) ,
public_ip = dict ( required = False , aliases = [ ' ip ' ] ) ,
state = dict ( required = False , default = ' present ' ,
state = dict ( required = False , default = ' present ' ,
choices = [ ' present ' , ' absent ' ] ) ,
choices = [ ' present ' , ' absent ' ] ) ,
in_vpc = dict ( required = False , type = ' bool ' , default = False ) ,
in_vpc = dict ( required = False , type = ' bool ' , default = False ) ,
reuse_existing_ip_allowed = dict ( required = False , type = ' bool ' ,
reuse_existing_ip_allowed = dict ( required = False , type = ' bool ' ,
default = False ) ,
default = False ) ,
release_on_disassociation = dict ( required = False , type = ' bool ' , default = False ) ,
wait_timeout = dict ( default = 300 ) ,
wait_timeout = dict ( default = 300 ) ,
) )
) )
@ -278,28 +320,52 @@ def main():
ec2 = ec2_connect ( module )
ec2 = ec2_connect ( module )
instan ce_id = module . params . get ( ' instan ce_id' )
dev ice_id = module . params . get ( ' dev ice_id' )
public_ip = module . params . get ( ' public_ip ' )
public_ip = module . params . get ( ' public_ip ' )
state = module . params . get ( ' state ' )
state = module . params . get ( ' state ' )
in_vpc = module . params . get ( ' in_vpc ' )
in_vpc = module . params . get ( ' in_vpc ' )
domain = ' vpc ' if in_vpc else None
domain = ' vpc ' if in_vpc else None
reuse_existing_ip_allowed = module . params . get ( ' reuse_existing_ip_allowed ' )
reuse_existing_ip_allowed = module . params . get ( ' reuse_existing_ip_allowed ' )
release_on_disassociation = module . params . get ( ' release_on_disassociation ' )
if device_id and device_id . startswith ( ' i- ' ) :
is_instance = True
elif device_id :
is_instance = False
try :
try :
address = find_address ( ec2 , public_ip , instance_id )
if device_id :
address = find_address ( ec2 , public_ip , device_id , isinstance = is_instance )
else :
address = False
if state == ' present ' :
if state == ' present ' :
result = ensure_present ( ec2 , domain , address , instance_id ,
if device_id :
result = ensure_present ( ec2 , domain , address , device_id ,
reuse_existing_ip_allowed ,
reuse_existing_ip_allowed ,
module . check_mode )
module . check_mode , isinstance = is_instance )
else :
address = allocate_address ( ec2 , domain , reuse_existing_ip_allowed )
result = { ' changed ' : True , ' public_ip ' : address . public_ip }
else :
else :
result = ensure_absent ( ec2 , domain , address , instance_id , module . check_mode )
if device_id :
disassociated = ensure_absent ( ec2 , domain , address , device_id , module . check_mode , isinstance = is_instance )
if release_on_disassociation and disassociated [ ' changed ' ] :
released = release_address ( ec2 , address , module . check_mode )
result = { ' changed ' : True , ' disassociated ' : disassociated , ' released ' : released }
else :
result = { ' changed ' : disassociated [ ' changed ' ] , ' disassociated ' : disassociated , ' released ' : { ' changed ' : False } }
else :
address = find_address ( ec2 , public_ip , None )
released = release_address ( ec2 , address , module . check_mode )
result = { ' changed ' : released [ ' changed ' ] , ' disassociated ' : { ' changed ' : False } , ' released ' : released }
except ( boto . exception . EC2ResponseError , EIPException ) as e :
except ( boto . exception . EC2ResponseError , EIPException ) as e :
module . fail_json ( msg = str ( e ) )
module . fail_json ( msg = str ( e ) )
module . exit_json ( * * result )
module . exit_json ( * * result )
# import module snippets
# import module snippets
from ansible . module_utils . basic import * # noqa
from ansible . module_utils . basic import * # noqa
from ansible . module_utils . ec2 import * # noqa
from ansible . module_utils . ec2 import * # noqa