summaryrefslogtreecommitdiff
path: root/cloud/amazon/ec2_key.py
blob: 5adb2e95bb501c9b5a1eb64e7395645870b4e9a1 (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
#!/usr/bin/python
# -*- coding: utf-8 -*-
# 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/>.

DOCUMENTATION = '''
---
module: ec2_key
version_added: "1.5"
short_description: maintain an ec2 key pair.
description:
    - maintains ec2 key pairs. This module has a dependency on python-boto >= 2.5
options:
  name:
    description:
      - Name of the key pair.
    required: true
  key_material:
    description:
      - Public key material.
    required: false
  state:
    description:
      - create or delete keypair
    required: false
    default: 'present'
    aliases: []
  wait:
    description:
      - Wait for the specified action to complete before returning.
    required: false
    default: false
    aliases: []
    version_added: "1.6"
  wait_timeout:
    description:
      - How long before wait gives up, in seconds
    required: false
    default: 300
    aliases: []
    version_added: "1.6"

extends_documentation_fragment:
 - aws
 - ec2
author: "Vincent Viallet (@zbal)"
'''

EXAMPLES = '''
# Note: None of these examples set aws_access_key, aws_secret_key, or region.
# It is assumed that their matching environment variables are set.

# Creates a new ec2 key pair named `example` if not present, returns generated
# private key
- name: example ec2 key
  ec2_key:
    name: example

# Creates a new ec2 key pair named `example` if not present using provided key
# material.  This could use the 'file' lookup plugin to pull this off disk.
- name: example2 ec2 key
  ec2_key:
    name: example2
    key_material: 'ssh-rsa AAAAxyz...== me@example.com'
    state: present

# Creates a new ec2 key pair named `example` if not present using provided key
# material
- name: example3 ec2 key
  ec2_key:
    name: example3
    key_material: "{{ item }}"
  with_file: /path/to/public_key.id_rsa.pub

# Removes ec2 key pair by name
- name: remove example key
  ec2_key:
    name: example
    state: absent
'''

try:
    import boto.ec2
    HAS_BOTO = True
except ImportError:
    HAS_BOTO = False

import random
import string


def main():
    argument_spec = ec2_argument_spec()
    argument_spec.update(dict(
            name=dict(required=True),
            key_material=dict(required=False),
            state = dict(default='present', choices=['present', 'absent']),
            wait = dict(type='bool', default=False),
            wait_timeout = dict(default=300),
        )
    )
    module = AnsibleModule(
        argument_spec=argument_spec,
        supports_check_mode=True,
    )

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

    name = module.params['name']
    state = module.params.get('state')
    key_material = module.params.get('key_material')
    wait = module.params.get('wait')
    wait_timeout = int(module.params.get('wait_timeout'))

    changed = False

    ec2 = ec2_connect(module)

    # find the key if present
    key = ec2.get_key_pair(name)

    # Ensure requested key is absent
    if state == 'absent':
        if key:
            '''found a match, delete it'''
            if not module.check_mode:
                try:
                    key.delete()
                    if wait:
                        start = time.time()
                        action_complete = False
                        while (time.time() - start) < wait_timeout:
                            if not ec2.get_key_pair(name):
                                action_complete = True
                                break
                            time.sleep(1)
                        if not action_complete:
                            module.fail_json(msg="timed out while waiting for the key to be removed")
                except Exception as e:
                    module.fail_json(msg="Unable to delete key pair '%s' - %s" % (key, e))
            key = None
            changed = True

    # Ensure requested key is present
    elif state == 'present':
        if key:
            # existing key found
            if key_material:
                # EC2's fingerprints are non-trivial to generate, so push this key 
                # to a temporary name and make ec2 calculate the fingerprint for us.
                #
                # http://blog.jbrowne.com/?p=23
                # https://forums.aws.amazon.com/thread.jspa?messageID=352828

                # find an unused name
                test = 'empty'
                while test:
                    randomchars = [random.choice(string.ascii_letters + string.digits) for x in range(0,10)]
                    tmpkeyname = "ansible-" + ''.join(randomchars)
                    test = ec2.get_key_pair(tmpkeyname)

                # create tmp key
                tmpkey = ec2.import_key_pair(tmpkeyname, key_material)
                # get tmp key fingerprint
                tmpfingerprint = tmpkey.fingerprint
                # delete tmp key
                tmpkey.delete()

                if key.fingerprint != tmpfingerprint:
                    if not module.check_mode:
                        key.delete()
                        key = ec2.import_key_pair(name, key_material)    

                        if wait:
                            start = time.time()
                            action_complete = False
                            while (time.time() - start) < wait_timeout:
                                if ec2.get_key_pair(name):
                                    action_complete = True
                                    break
                                time.sleep(1)
                            if not action_complete:
                                module.fail_json(msg="timed out while waiting for the key to be re-created")

                    changed = True
            pass

        # if the key doesn't exist, create it now
        else:
            '''no match found, create it'''
            if not module.check_mode:
                if key_material:
                    '''We are providing the key, need to import'''
                    key = ec2.import_key_pair(name, key_material)
                else:
                    '''
                    No material provided, let AWS handle the key creation and 
                    retrieve the private key
                    '''
                    key = ec2.create_key_pair(name)

                if wait:
                    start = time.time()
                    action_complete = False
                    while (time.time() - start) < wait_timeout:
                        if ec2.get_key_pair(name):
                            action_complete = True
                            break
                        time.sleep(1)
                    if not action_complete:
                        module.fail_json(msg="timed out while waiting for the key to be created")

            changed = True

    if key:
        data = {
            'name': key.name,
            'fingerprint': key.fingerprint
        }
        if key.material:
            data.update({'private_key': key.material})

        module.exit_json(changed=changed, key=data)
    else:
        module.exit_json(changed=changed, key=None)

# import module snippets
from ansible.module_utils.basic import *
from ansible.module_utils.ec2 import *

if __name__ == '__main__':
    main()