Skip to content

Commit b0f9ab9

Browse files
committed
ec2_ami_copy boto3 module, KMS, tagging, AMI caching (Encrypted support)
1 parent 0eb0483 commit b0f9ab9

File tree

4 files changed

+227
-5
lines changed

4 files changed

+227
-5
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
*.retry
2+
.idea/
23
configs/*
34
inventory_users
45
*.kate-swp

library/ec2_ami_copy.py

+216
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
# This file is part of Ansible
4+
#
5+
# Ansible is free software: you can redistribute it and/or modify
6+
# it under the terms of the GNU General Public License as published by
7+
# the Free Software Foundation, either version 3 of the License, or
8+
# (at your option) any later version.
9+
#
10+
# Ansible is distributed in the hope that it will be useful,
11+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
# GNU General Public License for more details.
14+
#
15+
# You should have received a copy of the GNU General Public License
16+
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
17+
18+
ANSIBLE_METADATA = {'status': ['preview'],
19+
'supported_by': 'community',
20+
'version': '1.1'}
21+
22+
DOCUMENTATION = '''
23+
---
24+
module: ec2_ami_copy
25+
short_description: copies AMI between AWS regions, return new image id
26+
description:
27+
- Copies AMI from a source region to a destination region. This module has a dependency on python-boto >= 2.5
28+
version_added: "2.0"
29+
options:
30+
source_region:
31+
description:
32+
- the source region that AMI should be copied from
33+
required: true
34+
source_image_id:
35+
description:
36+
- the id of the image in source region that should be copied
37+
required: true
38+
name:
39+
description:
40+
- The name of the new image to copy
41+
required: true
42+
default: null
43+
description:
44+
description:
45+
- An optional human-readable string describing the contents and purpose of the new AMI.
46+
required: false
47+
default: null
48+
encrypted:
49+
description:
50+
- Whether or not to encrypt the target image
51+
required: false
52+
default: null
53+
version_added: "2.2"
54+
kms_key_id:
55+
description:
56+
- KMS key id used to encrypt image. If not specified, uses default EBS Customer Master Key (CMK) for your account.
57+
required: false
58+
default: null
59+
version_added: "2.2"
60+
wait:
61+
description:
62+
- wait for the copied AMI to be in state 'available' before returning.
63+
required: false
64+
default: false
65+
tags:
66+
description:
67+
- a hash/dictionary of tags to add to the new copied AMI; '{"key":"value"}' and '{"key":"value","key":"value"}'
68+
required: false
69+
default: null
70+
71+
author: Amir Moulavi <[email protected]>, Tim C <[email protected]>
72+
extends_documentation_fragment:
73+
- aws
74+
- ec2
75+
'''
76+
77+
EXAMPLES = '''
78+
# Basic AMI Copy
79+
- ec2_ami_copy:
80+
source_region: us-east-1
81+
region: eu-west-1
82+
source_image_id: ami-xxxxxxx
83+
84+
# AMI copy wait until available
85+
- ec2_ami_copy:
86+
source_region: us-east-1
87+
region: eu-west-1
88+
source_image_id: ami-xxxxxxx
89+
wait: yes
90+
register: image_id
91+
92+
# Named AMI copy
93+
- ec2_ami_copy:
94+
source_region: us-east-1
95+
region: eu-west-1
96+
source_image_id: ami-xxxxxxx
97+
name: My-Awesome-AMI
98+
description: latest patch
99+
100+
# Tagged AMI copy
101+
- ec2_ami_copy:
102+
source_region: us-east-1
103+
region: eu-west-1
104+
source_image_id: ami-xxxxxxx
105+
tags:
106+
Name: My-Super-AMI
107+
Patch: 1.2.3
108+
109+
# Encrypted AMI copy
110+
- ec2_ami_copy:
111+
source_region: us-east-1
112+
region: eu-west-1
113+
source_image_id: ami-xxxxxxx
114+
encrypted: yes
115+
116+
# Encrypted AMI copy with specified key
117+
- ec2_ami_copy:
118+
source_region: us-east-1
119+
region: eu-west-1
120+
source_image_id: ami-xxxxxxx
121+
encrypted: yes
122+
kms_key_id: arn:aws:kms:us-east-1:XXXXXXXXXXXX:key/746de6ea-50a4-4bcb-8fbc-e3b29f2d367b
123+
'''
124+
125+
from ansible.module_utils.basic import AnsibleModule
126+
from ansible.module_utils.ec2 import (boto3_conn, ec2_argument_spec, get_aws_connection_info)
127+
128+
try:
129+
import boto
130+
import boto.ec2
131+
HAS_BOTO = True
132+
except ImportError:
133+
HAS_BOTO = False
134+
135+
try:
136+
import boto3
137+
from botocore.exceptions import ClientError, NoCredentialsError, NoRegionError
138+
HAS_BOTO3 = True
139+
except ImportError:
140+
HAS_BOTO3 = False
141+
142+
143+
144+
def copy_image(ec2, module):
145+
"""
146+
Copies an AMI
147+
148+
module : AnsibleModule object
149+
ec2: ec2 connection object
150+
"""
151+
152+
tags = module.params.get('tags')
153+
154+
params = {'SourceRegion': module.params.get('source_region'),
155+
'SourceImageId': module.params.get('source_image_id'),
156+
'Name': module.params.get('name'),
157+
'Description': module.params.get('description'),
158+
'Encrypted': module.params.get('encrypted'),
159+
# 'KmsKeyId': module.params.get('kms_key_id')
160+
}
161+
if module.params.get('kms_key_id'):
162+
params['KmsKeyId'] = module.params.get('kms_key_id')
163+
164+
try:
165+
image_id = ec2.copy_image(**params)['ImageId']
166+
if module.params.get('wait'):
167+
ec2.get_waiter('image_available').wait(ImageIds=[image_id])
168+
if module.params.get('tags'):
169+
ec2.create_tags(
170+
Resources=[image_id],
171+
Tags=[{'Key' : k, 'Value': v} for k,v in module.params.get('tags').items()]
172+
)
173+
174+
module.exit_json(changed=True, image_id=image_id)
175+
except ClientError as ce:
176+
module.fail_json(msg=ce)
177+
except NoCredentialsError:
178+
module.fail_json(msg="Unable to locate AWS credentials")
179+
except Exception as e:
180+
module.fail_json(msg=str(e))
181+
182+
183+
def main():
184+
argument_spec = ec2_argument_spec()
185+
argument_spec.update(dict(
186+
source_region=dict(required=True),
187+
source_image_id=dict(required=True),
188+
name=dict(required=True),
189+
description=dict(default=''),
190+
encrypted=dict(type='bool', required=False),
191+
kms_key_id=dict(type='str', required=False),
192+
wait=dict(type='bool', default=False, required=False),
193+
tags=dict(type='dict')))
194+
195+
module = AnsibleModule(argument_spec=argument_spec)
196+
197+
if not HAS_BOTO:
198+
module.fail_json(msg='boto required for this module')
199+
# TODO: Check botocore version
200+
region, ec2_url, aws_connect_params = get_aws_connection_info(module, boto3=True)
201+
202+
if HAS_BOTO3:
203+
204+
try:
205+
ec2 = boto3_conn(module, conn_type='client', resource='ec2', region=region, endpoint=ec2_url,
206+
**aws_connect_params)
207+
except NoRegionError:
208+
module.fail_json(msg='AWS Region is required')
209+
else:
210+
module.fail_json(msg='boto3 required for this module')
211+
212+
copy_image(ec2, module)
213+
214+
215+
if __name__ == '__main__':
216+
main()

roles/cloud-ec2/tasks/encrypt_image.yml

+6-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
register: search_crypt
1414

1515
- set_fact:
16-
enc_image: "{{ search_crypt.results[0].image_id }}"
16+
ami_image: "{{ search_crypt.results[0].ami_id }}"
1717
when: search_crypt.results
1818

1919
- name: Copy to an encrypted image
@@ -22,14 +22,16 @@
2222
aws_secret_key: "{{ aws_secret_key | default(lookup('env','AWS_SECRET_ACCESS_KEY'))}}"
2323
encrypted: yes
2424
name: algo
25+
kms_key_id: "{{ kms_key_id | default(omit) }}"
2526
region: "{{ region }}"
26-
source_image_id: "{{ image_id }}"
27+
source_image_id: "{{ ami_image }}"
2728
source_region: "{{ region }}"
2829
tags:
2930
Algo: "encrypted"
3031
wait: true
3132
register: enc_image
32-
when: enc_image is not defined
33+
when: not search_crypt.results
3334

3435
- set_fact:
35-
image_id: "{{ enc_image.image_id }}"
36+
ami_image: "{{ enc_image.image_id }}"
37+
when: not search_crypt.results

roles/cloud-ec2/tasks/main.yml

+4-1
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@
1010
region: "{{ region }}"
1111
register: ami_search
1212

13+
- set_fact:
14+
ami_image: "{{ ami_search.results[0].ami_id }}"
15+
1316
- include: encrypt_image.yml
14-
when: encrypted is defined
17+
tags: [encrypted]
1518

1619
- name: Add ssh public key
1720
ec2_key:

0 commit comments

Comments
 (0)