summaryrefslogtreecommitdiff
path: root/lib/ansible/modules/cloud/amazon/ec2_vpc_igw.py
blob: b34e3c7097dfd2aa5f83d066d3902fe8bfc8bf30 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
#!/usr/bin/python
# Copyright: Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function
__metaclass__ = type


ANSIBLE_METADATA = {'metadata_version': '1.1',
                    'status': ['stableinterface'],
                    'supported_by': 'certified'}


DOCUMENTATION = '''
---
module: ec2_vpc_igw
short_description: Manage an AWS VPC Internet gateway
description:
    - Manage an AWS VPC Internet gateway
version_added: "2.0"
author: Robert Estelle (@erydo)
options:
  vpc_id:
    description:
      - The VPC ID for the VPC in which to manage the Internet Gateway.
    required: true
    default: null
  tags:
    description:
      - "A dict of tags to apply to the internet gateway. Any tags currently applied to the internet gateway and not present here will be removed."
    required: false
    default: null
    aliases: [ 'resource_tags' ]
    version_added: "2.4"
  state:
    description:
      - Create or terminate the IGW
    required: false
    default: present
    choices: [ 'present', 'absent' ]
extends_documentation_fragment:
    - aws
    - ec2
'''

EXAMPLES = '''
# Note: These examples do not set authentication details, see the AWS Guide for details.

# Ensure that the VPC has an Internet Gateway.
# The Internet Gateway ID is can be accessed via {{igw.gateway_id}} for use in setting up NATs etc.
ec2_vpc_igw:
  vpc_id: vpc-abcdefgh
  state: present
register: igw

'''

RETURN = '''
changed:
  description: If any changes have been made to the Internet Gateway.
  type: bool
  returned: always
  sample:
    changed: false
gateway_id:
  description: The unique identifier for the Internet Gateway.
  type: str
  returned: I(state=present)
  sample:
    gateway_id: "igw-XXXXXXXX"
tags:
  description: The tags associated the Internet Gateway.
  type: dict
  returned: I(state=present)
  sample:
    tags:
      "Ansible": "Test"
vpc_id:
  description: The VPC ID associated with the Internet Gateway.
  type: str
  returned: I(state=present)
  sample:
    vpc_id: "vpc-XXXXXXXX"
'''

try:
    import boto.ec2
    import boto.vpc
    from boto.exception import EC2ResponseError
    HAS_BOTO = True
except ImportError:
    HAS_BOTO = False

from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.ec2 import AnsibleAWSError, connect_to_aws, ec2_argument_spec, get_aws_connection_info
from ansible.module_utils.six import string_types


class AnsibleIGWException(Exception):
    pass


def get_igw_info(igw):
    return {'gateway_id': igw.id,
            'tags': igw.tags,
            'vpc_id': igw.vpc_id
            }


def get_resource_tags(vpc_conn, resource_id):
    return dict((t.name, t.value) for t in
                vpc_conn.get_all_tags(filters={'resource-id': resource_id}))


def ensure_tags(vpc_conn, resource_id, tags, add_only, check_mode):
    try:
        cur_tags = get_resource_tags(vpc_conn, resource_id)
        if cur_tags == tags:
            return {'changed': False, 'tags': cur_tags}

        if check_mode:
            latest_check_mode_tags = cur_tags

        to_delete = dict((k, cur_tags[k]) for k in cur_tags if k not in tags)
        if to_delete and not add_only:
            if check_mode:
                # just overwriting latest_check_mode_tags instead of deleting keys
                latest_check_mode_tags = dict((k, cur_tags[k]) for k in cur_tags if k not in to_delete)
            else:
                vpc_conn.delete_tags(resource_id, to_delete)

        to_add = dict((k, tags[k]) for k in tags if k not in cur_tags or cur_tags[k] != tags[k])
        if to_add:
            if check_mode:
                latest_check_mode_tags.update(to_add)
            else:
                vpc_conn.create_tags(resource_id, to_add)

        if check_mode:
            return {'changed': True, 'tags': latest_check_mode_tags}
        latest_tags = get_resource_tags(vpc_conn, resource_id)
        return {'changed': True, 'tags': latest_tags}
    except EC2ResponseError as e:
        raise AnsibleIGWException(
            'Unable to update tags for {0}, error: {1}'.format(resource_id, e))


def get_matching_igw(vpc_conn, vpc_id):
    igws = vpc_conn.get_all_internet_gateways(filters={'attachment.vpc-id': vpc_id})
    if len(igws) > 1:
        raise AnsibleIGWException(
            'EC2 returned more than one Internet Gateway for VPC {0}, aborting'
            .format(vpc_id))
    return igws[0] if igws else None


def ensure_igw_absent(vpc_conn, vpc_id, check_mode):
    igw = get_matching_igw(vpc_conn, vpc_id)
    if igw is None:
        return {'changed': False}

    if check_mode:
        return {'changed': True}

    try:
        vpc_conn.detach_internet_gateway(igw.id, vpc_id)
        vpc_conn.delete_internet_gateway(igw.id)
    except EC2ResponseError as e:
        raise AnsibleIGWException(
            'Unable to delete Internet Gateway, error: {0}'.format(e))

    return {'changed': True}


def ensure_igw_present(vpc_conn, vpc_id, tags, check_mode):
    igw = get_matching_igw(vpc_conn, vpc_id)
    changed = False
    if igw is None:
        if check_mode:
            return {'changed': True, 'gateway_id': None}

        try:
            igw = vpc_conn.create_internet_gateway()
            vpc_conn.attach_internet_gateway(igw.id, vpc_id)
            changed = True
        except EC2ResponseError as e:
            raise AnsibleIGWException(
                'Unable to create Internet Gateway, error: {0}'.format(e))

    igw.vpc_id = vpc_id

    if tags != igw.tags:
        if check_mode:
            check_mode_tags = ensure_tags(vpc_conn, igw.id, tags, False, check_mode)
            igw_info = get_igw_info(igw)
            igw_info.get('tags', {}).update(check_mode_tags.get('tags', {}))
            return {'changed': True, 'gateway': igw_info}
        ensure_tags(vpc_conn, igw.id, tags, False, check_mode)
        igw.tags = tags
        changed = True

    igw_info = get_igw_info(igw)

    return {
        'changed': changed,
        'gateway': igw_info
    }


def main():
    argument_spec = ec2_argument_spec()
    argument_spec.update(
        dict(
            vpc_id=dict(required=True),
            state=dict(default='present', choices=['present', 'absent']),
            tags=dict(default=dict(), required=False, type='dict', aliases=['resource_tags'])
        )
    )

    module = AnsibleModule(
        argument_spec=argument_spec,
        supports_check_mode=True,
    )

    if not HAS_BOTO:
        module.fail_json(msg='boto is required for this module')

    region, ec2_url, aws_connect_params = get_aws_connection_info(module)

    if region:
        try:
            connection = connect_to_aws(boto.vpc, region, **aws_connect_params)
        except (boto.exception.NoAuthHandlerFound, AnsibleAWSError) as e:
            module.fail_json(msg=str(e))
    else:
        module.fail_json(msg="region must be specified")

    vpc_id = module.params.get('vpc_id')
    state = module.params.get('state', 'present')
    tags = module.params.get('tags')

    nonstring_tags = [k for k, v in tags.items() if not isinstance(v, string_types)]
    if nonstring_tags:
        module.fail_json(msg='One or more tags contain non-string values: {0}'.format(nonstring_tags))

    try:
        if state == 'present':
            result = ensure_igw_present(connection, vpc_id, tags, check_mode=module.check_mode)
        elif state == 'absent':
            result = ensure_igw_absent(connection, vpc_id, check_mode=module.check_mode)
    except AnsibleIGWException as e:
        module.fail_json(msg=str(e))

    module.exit_json(changed=result['changed'], **result.get('gateway', {}))


if __name__ == '__main__':
    main()