1#!/usr/bin/python 2# 3# This is a free software: you can redistribute it and/or modify 4# it under the terms of the GNU General Public License as published by 5# the Free Software Foundation, either version 3 of the License, or 6# (at your option) any later version. 7# 8# This Ansible library is distributed in the hope that it will be useful, 9# but WITHOUT ANY WARRANTY; without even the implied warranty of 10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11# GNU General Public License for more details. 12# 13# You should have received a copy of the GNU General Public License 14# along with this library. If not, see <http://www.gnu.org/licenses/>. 15 16ANSIBLE_METADATA = {'metadata_version': '1.1', 17 'status': ['preview'], 18 'supported_by': 'community'} 19 20 21DOCUMENTATION = ''' 22--- 23module: ec2_customer_gateway 24short_description: Manage an AWS customer gateway 25description: 26 - Manage an AWS customer gateway 27version_added: "2.2" 28author: Michael Baydoun (@MichaelBaydoun) 29requirements: [ botocore, boto3 ] 30notes: 31 - You cannot create more than one customer gateway with the same IP address. If you run an identical request more than one time, the 32 first request creates the customer gateway, and subsequent requests return information about the existing customer gateway. The subsequent 33 requests do not create new customer gateway resources. 34 - Return values contain customer_gateway and customer_gateways keys which are identical dicts. You should use 35 customer_gateway. See U(https://github.com/ansible/ansible-modules-extras/issues/2773) for details. 36options: 37 bgp_asn: 38 description: 39 - Border Gateway Protocol (BGP) Autonomous System Number (ASN), required when state=present. 40 ip_address: 41 description: 42 - Internet-routable IP address for customers gateway, must be a static address. 43 required: true 44 name: 45 description: 46 - Name of the customer gateway. 47 required: true 48 routing: 49 description: 50 - The type of routing. 51 choices: ['static', 'dynamic'] 52 default: dynamic 53 version_added: '2.4' 54 state: 55 description: 56 - Create or terminate the Customer Gateway. 57 default: present 58 choices: [ 'present', 'absent' ] 59extends_documentation_fragment: 60 - aws 61 - ec2 62''' 63 64EXAMPLES = ''' 65 66# Create Customer Gateway 67- ec2_customer_gateway: 68 bgp_asn: 12345 69 ip_address: 1.2.3.4 70 name: IndianapolisOffice 71 region: us-east-1 72 register: cgw 73 74# Delete Customer Gateway 75- ec2_customer_gateway: 76 ip_address: 1.2.3.4 77 name: IndianapolisOffice 78 state: absent 79 region: us-east-1 80 register: cgw 81''' 82 83RETURN = ''' 84gateway.customer_gateways: 85 description: details about the gateway that was created. 86 returned: success 87 type: complex 88 contains: 89 bgp_asn: 90 description: The Border Gateway Autonomous System Number. 91 returned: when exists and gateway is available. 92 sample: 65123 93 type: str 94 customer_gateway_id: 95 description: gateway id assigned by amazon. 96 returned: when exists and gateway is available. 97 sample: cgw-cb6386a2 98 type: str 99 ip_address: 100 description: ip address of your gateway device. 101 returned: when exists and gateway is available. 102 sample: 1.2.3.4 103 type: str 104 state: 105 description: state of gateway. 106 returned: when gateway exists and is available. 107 state: available 108 type: str 109 tags: 110 description: any tags on the gateway. 111 returned: when gateway exists and is available, and when tags exist. 112 state: available 113 type: str 114 type: 115 description: encryption type. 116 returned: when gateway exists and is available. 117 sample: ipsec.1 118 type: str 119''' 120 121try: 122 from botocore.exceptions import ClientError 123 HAS_BOTOCORE = True 124except ImportError: 125 HAS_BOTOCORE = False 126 127try: 128 import boto3 129 HAS_BOTO3 = True 130except ImportError: 131 HAS_BOTO3 = False 132 133from ansible.module_utils.basic import AnsibleModule 134from ansible.module_utils.ec2 import (boto3_conn, AWSRetry, camel_dict_to_snake_dict, 135 ec2_argument_spec, get_aws_connection_info) 136 137 138class Ec2CustomerGatewayManager: 139 140 def __init__(self, module): 141 self.module = module 142 143 try: 144 region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) 145 if not region: 146 module.fail_json(msg="Region must be specified as a parameter, in EC2_REGION or AWS_REGION environment variables or in boto configuration file") 147 self.ec2 = boto3_conn(module, conn_type='client', resource='ec2', region=region, endpoint=ec2_url, **aws_connect_kwargs) 148 except ClientError as e: 149 module.fail_json(msg=e.message) 150 151 @AWSRetry.jittered_backoff(delay=2, max_delay=30, retries=6, catch_extra_error_codes=['IncorrectState']) 152 def ensure_cgw_absent(self, gw_id): 153 response = self.ec2.delete_customer_gateway( 154 DryRun=False, 155 CustomerGatewayId=gw_id 156 ) 157 return response 158 159 def ensure_cgw_present(self, bgp_asn, ip_address): 160 if not bgp_asn: 161 bgp_asn = 65000 162 response = self.ec2.create_customer_gateway( 163 DryRun=False, 164 Type='ipsec.1', 165 PublicIp=ip_address, 166 BgpAsn=bgp_asn, 167 ) 168 return response 169 170 def tag_cgw_name(self, gw_id, name): 171 response = self.ec2.create_tags( 172 DryRun=False, 173 Resources=[ 174 gw_id, 175 ], 176 Tags=[ 177 { 178 'Key': 'Name', 179 'Value': name 180 }, 181 ] 182 ) 183 return response 184 185 def describe_gateways(self, ip_address): 186 response = self.ec2.describe_customer_gateways( 187 DryRun=False, 188 Filters=[ 189 { 190 'Name': 'state', 191 'Values': [ 192 'available', 193 ] 194 }, 195 { 196 'Name': 'ip-address', 197 'Values': [ 198 ip_address, 199 ] 200 } 201 ] 202 ) 203 return response 204 205 206def main(): 207 argument_spec = ec2_argument_spec() 208 argument_spec.update( 209 dict( 210 bgp_asn=dict(required=False, type='int'), 211 ip_address=dict(required=True), 212 name=dict(required=True), 213 routing=dict(default='dynamic', choices=['dynamic', 'static']), 214 state=dict(default='present', choices=['present', 'absent']), 215 ) 216 ) 217 218 module = AnsibleModule(argument_spec=argument_spec, 219 supports_check_mode=True, 220 required_if=[ 221 ('routing', 'dynamic', ['bgp_asn']) 222 ] 223 ) 224 225 if not HAS_BOTOCORE: 226 module.fail_json(msg='botocore is required.') 227 228 if not HAS_BOTO3: 229 module.fail_json(msg='boto3 is required.') 230 231 gw_mgr = Ec2CustomerGatewayManager(module) 232 233 name = module.params.get('name') 234 235 existing = gw_mgr.describe_gateways(module.params['ip_address']) 236 237 results = dict(changed=False) 238 if module.params['state'] == 'present': 239 if existing['CustomerGateways']: 240 existing['CustomerGateway'] = existing['CustomerGateways'][0] 241 results['gateway'] = existing 242 if existing['CustomerGateway']['Tags']: 243 tag_array = existing['CustomerGateway']['Tags'] 244 for key, value in enumerate(tag_array): 245 if value['Key'] == 'Name': 246 current_name = value['Value'] 247 if current_name != name: 248 results['name'] = gw_mgr.tag_cgw_name( 249 results['gateway']['CustomerGateway']['CustomerGatewayId'], 250 module.params['name'], 251 ) 252 results['changed'] = True 253 else: 254 if not module.check_mode: 255 results['gateway'] = gw_mgr.ensure_cgw_present( 256 module.params['bgp_asn'], 257 module.params['ip_address'], 258 ) 259 results['name'] = gw_mgr.tag_cgw_name( 260 results['gateway']['CustomerGateway']['CustomerGatewayId'], 261 module.params['name'], 262 ) 263 results['changed'] = True 264 265 elif module.params['state'] == 'absent': 266 if existing['CustomerGateways']: 267 existing['CustomerGateway'] = existing['CustomerGateways'][0] 268 results['gateway'] = existing 269 if not module.check_mode: 270 results['gateway'] = gw_mgr.ensure_cgw_absent( 271 existing['CustomerGateway']['CustomerGatewayId'] 272 ) 273 results['changed'] = True 274 275 pretty_results = camel_dict_to_snake_dict(results) 276 module.exit_json(**pretty_results) 277 278 279if __name__ == '__main__': 280 main() 281