mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-07-23 13:20:23 -07:00
New module: AWS EC2 Launch Template (#46972)
* Add launch template integration tests
This commit is contained in:
parent
5a7f2b6b08
commit
a51eca364f
13 changed files with 1133 additions and 0 deletions
|
@ -140,6 +140,8 @@ groupings:
|
|||
- aws
|
||||
ec2_key:
|
||||
- aws
|
||||
ec2_launch_template:
|
||||
- aws
|
||||
ec2_lc:
|
||||
- aws
|
||||
ec2_lc_facts:
|
||||
|
|
649
lib/ansible/modules/cloud/amazon/ec2_launch_template.py
Normal file
649
lib/ansible/modules/cloud/amazon/ec2_launch_template.py
Normal file
|
@ -0,0 +1,649 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright (c) 2018 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': ['preview'],
|
||||
'supported_by': 'community'
|
||||
}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ec2_launch_template
|
||||
version_added: "2.8"
|
||||
short_description: Manage EC2 launch templates
|
||||
description:
|
||||
- Create, modify, and delete EC2 Launch Templates, which can be used to
|
||||
create individual instances or with Autoscaling Groups.
|
||||
- The I(ec2_instance) and I(ec2_asg) modules can, instead of specifying all
|
||||
parameters on those tasks, be passed a Launch Template which contains
|
||||
settings like instance size, disk type, subnet, and more.
|
||||
requirements:
|
||||
- botocore
|
||||
- boto3 >= 1.6.0
|
||||
extends_documentation_fragment:
|
||||
- aws
|
||||
- ec2
|
||||
author:
|
||||
- Ryan Scott Brown (@ryansb)
|
||||
options:
|
||||
template_id:
|
||||
description:
|
||||
- The ID for the launch template, can be used for all cases except creating a new Launch Template.
|
||||
aliases: [id]
|
||||
template_name:
|
||||
description:
|
||||
- The template name. This must be unique in the region-account combination you are using.
|
||||
aliases: [name]
|
||||
default_version:
|
||||
description:
|
||||
- Which version should be the default when users spin up new instances based on this template? By default, the latest version will be made the default.
|
||||
default: latest
|
||||
state:
|
||||
description:
|
||||
- Whether the launch template should exist or not. To delete only a
|
||||
specific version of a launch template, combine I(state=absent) with
|
||||
the I(version) option. By default, I(state=absent) will remove all
|
||||
versions of the template.
|
||||
choices: [present, absent]
|
||||
default: present
|
||||
block_device_mappings:
|
||||
description:
|
||||
- The block device mapping. Supplying both a snapshot ID and an encryption
|
||||
value as arguments for block-device mapping results in an error. This is
|
||||
because only blank volumes can be encrypted on start, and these are not
|
||||
created from a snapshot. If a snapshot is the basis for the volume, it
|
||||
contains data by definition and its encryption status cannot be changed
|
||||
using this action.
|
||||
suboptions:
|
||||
device_name:
|
||||
description: The device name (for example, /dev/sdh or xvdh).
|
||||
no_device:
|
||||
description: Suppresses the specified device included in the block device mapping of the AMI.
|
||||
virtual_name:
|
||||
description: >
|
||||
The virtual device name (ephemeralN). Instance store volumes are
|
||||
numbered starting from 0. An instance type with 2 available instance
|
||||
store volumes can specify mappings for ephemeral0 and ephemeral1. The
|
||||
number of available instance store volumes depends on the instance
|
||||
type. After you connect to the instance, you must mount the volume.
|
||||
ebs:
|
||||
description: Parameters used to automatically set up EBS volumes when the instance is launched.
|
||||
suboptions:
|
||||
delete_on_termintation:
|
||||
description: Indicates whether the EBS volume is deleted on instance termination.
|
||||
type: bool
|
||||
encrypted:
|
||||
description: >
|
||||
Indicates whether the EBS volume is encrypted. Encrypted volumes
|
||||
can only be attached to instances that support Amazon EBS
|
||||
encryption. If you are creating a volume from a snapshot, you
|
||||
can't specify an encryption value.
|
||||
iops:
|
||||
description:
|
||||
- The number of I/O operations per second (IOPS) that the volume
|
||||
supports. For io1, this represents the number of IOPS that are
|
||||
provisioned for the volume. For gp2, this represents the baseline
|
||||
performance of the volume and the rate at which the volume
|
||||
accumulates I/O credits for bursting. For more information about
|
||||
General Purpose SSD baseline performance, I/O credits, and
|
||||
bursting, see Amazon EBS Volume Types in the Amazon Elastic
|
||||
Compute Cloud User Guide.
|
||||
- >
|
||||
Condition: This parameter is required for requests to create io1
|
||||
volumes; it is not used in requests to create gp2, st1, sc1, or
|
||||
standard volumes.
|
||||
kms_key_id:
|
||||
description: The ARN of the AWS Key Management Service (AWS KMS) CMK used for encryption.
|
||||
snapshot_id:
|
||||
description: The ID of the snapshot to create the volume from
|
||||
volume_size:
|
||||
description:
|
||||
- The size of the volume, in GiB.
|
||||
- "Default: If you're creating the volume from a snapshot and don't specify a volume size, the default is the snapshot size."
|
||||
volume_type:
|
||||
description: The volume type
|
||||
cpu_options:
|
||||
description:
|
||||
- Choose CPU settings for the EC2 instances that will be created with this template.
|
||||
- For more information, see U(http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-optimize-cpu.html)
|
||||
suboptions:
|
||||
core_count:
|
||||
description: The number of CPU cores for the instance.
|
||||
threads_per_core:
|
||||
description: >
|
||||
The number of threads per CPU core. To disable Intel Hyper-Threading
|
||||
Technology for the instance, specify a value of 1. Otherwise, specify
|
||||
the default value of 2.
|
||||
credit_specification:
|
||||
description: The credit option for CPU usage of the instance. Valid for T2 or T3 instances only.
|
||||
suboptions:
|
||||
cpu_credits:
|
||||
description: >
|
||||
The credit option for CPU usage of a T2 or T3 instance. Valid values
|
||||
are I(standard) and I(unlimited).
|
||||
choices: [standard, unlimited]
|
||||
disable_api_termination:
|
||||
description: >
|
||||
This helps protect instances from accidental termination. If set to true,
|
||||
you can't terminate the instance using the Amazon EC2 console, CLI, or
|
||||
API. To change this attribute to false after launch, use
|
||||
I(ModifyInstanceAttribute).
|
||||
type: bool
|
||||
ebs_optimized:
|
||||
description: >
|
||||
Indicates whether the instance is optimized for Amazon EBS I/O. This
|
||||
optimization provides dedicated throughput to Amazon EBS and an optimized
|
||||
configuration stack to provide optimal Amazon EBS I/O performance. This
|
||||
optimization isn't available with all instance types. Additional usage
|
||||
charges apply when using an EBS-optimized instance.
|
||||
type: bool
|
||||
elastic_gpu_specifications:
|
||||
description: Settings for Elastic GPU attachments. See U(https://aws.amazon.com/ec2/elastic-gpus/) for details.
|
||||
suboptions:
|
||||
type:
|
||||
description: The type of Elastic GPU to attach
|
||||
iam_instance_profile:
|
||||
description: >
|
||||
The name or ARN of an IAM instance profile. Requires permissions to
|
||||
describe existing instance roles to confirm ARN is properly formed.
|
||||
image_id:
|
||||
description: >
|
||||
The AMI ID to use for new instances launched with this template. This
|
||||
value is region-dependent since AMIs are not global resources.
|
||||
instance_initiated_shutdown_behavior:
|
||||
description: >
|
||||
Indicates whether an instance stops or terminates when you initiate
|
||||
shutdown from the instance using the operating system shutdown command.
|
||||
choices: [stop, terminate]
|
||||
instance_market_options:
|
||||
description: Options for alternative instance markets, currently only the spot market is supported.
|
||||
suboptions:
|
||||
market_type:
|
||||
description: The market type. This should always be 'spot'.
|
||||
spot_options:
|
||||
description: Spot-market specific settings
|
||||
suboptions:
|
||||
block_duration_minutes:
|
||||
description: >
|
||||
The required duration for the Spot Instances (also known as Spot
|
||||
blocks), in minutes. This value must be a multiple of 60 (60,
|
||||
120, 180, 240, 300, or 360).
|
||||
instance_interruption_behavior:
|
||||
description: The behavior when a Spot Instance is interrupted. The default is I(terminate)
|
||||
choices: [hibernate, stop, terminate]
|
||||
max_price:
|
||||
description: The highest hourly price you're willing to pay for this Spot Instance.
|
||||
spot_instance_type:
|
||||
description: The request type to send.
|
||||
choices: [one-time, persistent]
|
||||
type: dict
|
||||
instance_type:
|
||||
description: >
|
||||
The instance type, such as I(c5.2xlarge). For a full list of instance types, see
|
||||
http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html
|
||||
kernel_id:
|
||||
description: >
|
||||
The ID of the kernel. We recommend that you use PV-GRUB instead of
|
||||
kernels and RAM disks. For more information, see
|
||||
U(http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/UserProvidedkernels.html)
|
||||
key_name:
|
||||
description:
|
||||
- The name of the key pair. You can create a key pair using
|
||||
I(CreateKeyPair) or I(ImportKeyPair).
|
||||
- If you do not specify a key pair, you can't connect to the instance
|
||||
unless you choose an AMI that is configured to allow users another way to
|
||||
log in.
|
||||
monitoring:
|
||||
description: Settings for instance monitoring
|
||||
suboptions:
|
||||
enabled:
|
||||
type: bool
|
||||
description: Whether to turn on detailed monitoring for new instances. This will incur extra charges.
|
||||
network_interfaces:
|
||||
description: One or more network interfaces.
|
||||
suboptions:
|
||||
associate_public_ip_address:
|
||||
description: Associates a public IPv4 address with eth0 for a new network interface.
|
||||
type: bool
|
||||
delete_on_termination:
|
||||
description: Indicates whether the network interface is deleted when the instance is terminated.
|
||||
type: bool
|
||||
description:
|
||||
description: A description for the network interface.
|
||||
device_index:
|
||||
description: The device index for the network interface attachment.
|
||||
groups:
|
||||
description: List of security group IDs to include on this instance
|
||||
ipv6_address_count:
|
||||
description: >
|
||||
The number of IPv6 addresses to assign to a network interface. Amazon
|
||||
EC2 automatically selects the IPv6 addresses from the subnet range.
|
||||
You can't use this option if specifying the I(ipv6_addresses) option.
|
||||
ipv6_addresses:
|
||||
description: >
|
||||
A list of one or more specific IPv6 addresses from the IPv6 CIDR
|
||||
block range of your subnet. You can't use this option if you're
|
||||
specifying the I(ipv6_address_count) option.
|
||||
network_interface_id:
|
||||
description: The eni ID of a network interface to attach.
|
||||
private_ip_address:
|
||||
description: The primary private IPv4 address of the network interface.
|
||||
private_ip_addresses:
|
||||
description: One or more private IPv4 addresses.
|
||||
suboptions:
|
||||
primary:
|
||||
description: >
|
||||
Indicates whether the private IPv4 address is the primary private
|
||||
IPv4 address. Only one IPv4 address can be designated as primary.
|
||||
private_ip_address:
|
||||
description: The primary private IPv4 address of the network interface.
|
||||
subnet_id:
|
||||
description: The ID of the subnet for the network interface.
|
||||
secondary_private_ip_address_count:
|
||||
description: The number of secondary private IPv4 addresses to assign to a network interface.
|
||||
placement:
|
||||
description: The placement group settings for the instance.
|
||||
suboptions:
|
||||
affinity:
|
||||
description: The affinity setting for an instance on a Dedicated Host.
|
||||
availability_zone:
|
||||
description: The Availability Zone for the instance.
|
||||
group_name:
|
||||
description: The name of the placement group for the instance.
|
||||
host_id:
|
||||
description: The ID of the Dedicated Host for the instance.
|
||||
tenancy:
|
||||
description: >
|
||||
The tenancy of the instance (if the instance is running in a VPC). An
|
||||
instance with a tenancy of dedicated runs on single-tenant hardware.
|
||||
ram_disk_id:
|
||||
description: >
|
||||
The ID of the RAM disk to launch the instance with. We recommend that you
|
||||
use PV-GRUB instead of kernels and RAM disks. For more information, see
|
||||
U(http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/UserProvidedkernels.html)
|
||||
security_group_ids:
|
||||
description: A list of security group IDs (VPC or EC2-Classic) that the new instances will be added to.
|
||||
type: list
|
||||
security_groups:
|
||||
description: A list of security group names (VPC or EC2-Classic) that the new instances will be added to.
|
||||
type: list
|
||||
tags:
|
||||
type: dict
|
||||
description:
|
||||
- A set of key-value pairs to be applied to resources when this Launch Template is used.
|
||||
- "Tag key constraints: Tag keys are case-sensitive and accept a maximum of 127 Unicode characters. May not begin with I(aws:)"
|
||||
- "Tag value constraints: Tag values are case-sensitive and accept a maximum of 255 Unicode characters."
|
||||
user_data:
|
||||
description: >
|
||||
The Base64-encoded user data to make available to the instance. For more information, see the Linux
|
||||
U(http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html) and Windows
|
||||
U(http://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/ec2-instance-metadata.html#instancedata-add-user-data)
|
||||
documentation on user-data.
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Make instance with an instance_role
|
||||
ec2_launch_template:
|
||||
name: "test-with-instance-role"
|
||||
image_id: "ami-foobarbaz"
|
||||
key_name: my_ssh_key
|
||||
instance_type: t2.micro
|
||||
iam_instance_profile: myTestProfile
|
||||
disable_api_termination: true
|
||||
|
||||
- name: Make one with a different instance type, but leave the older version as default
|
||||
ec2_launch_template:
|
||||
name: "test-with-instance-role"
|
||||
image_id: "ami-foobarbaz"
|
||||
default_version: 1
|
||||
key_name: my_ssh_key
|
||||
instance_type: c5.4xlarge
|
||||
iam_instance_profile: myTestProfile
|
||||
disable_api_termination: true
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
latest_version:
|
||||
description: Latest available version of the launch template
|
||||
returned: when state=present
|
||||
type: int
|
||||
default_version:
|
||||
description: The version that will be used if only the template name is specified. Often this is the same as the latest version, but not always.
|
||||
returned: when state=present
|
||||
type: int
|
||||
'''
|
||||
import re
|
||||
from uuid import uuid4
|
||||
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.aws.core import AnsibleAWSModule, is_boto3_error_code, get_boto3_client_method_parameters
|
||||
from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict, snake_dict_to_camel_dict
|
||||
from ansible.module_utils.ec2 import ansible_dict_to_boto3_tag_list, AWSRetry, boto3_tag_list_to_ansible_dict, ansible_dict_to_boto3_tag_list
|
||||
|
||||
try:
|
||||
from botocore.exceptions import ClientError, BotoCoreError, WaiterError
|
||||
except ImportError:
|
||||
pass # caught by AnsibleAWSModule
|
||||
|
||||
|
||||
def determine_iam_role(module, name_or_arn):
|
||||
if re.match(r'^arn:aws:iam::\d+:instance-profile/[\w+=/,.@-]+$', name_or_arn):
|
||||
return name_or_arn
|
||||
iam = module.client('iam', retry_decorator=AWSRetry.jittered_backoff())
|
||||
try:
|
||||
role = iam.get_instance_profile(InstanceProfileName=name_or_arn, aws_retry=True)
|
||||
return {'arn': role['InstanceProfile']['Arn']}
|
||||
except is_boto3_error_code('NoSuchEntity') as e:
|
||||
module.fail_json_aws(e, msg="Could not find instance_role {0}".format(name_or_arn))
|
||||
except (BotoCoreError, ClientError) as e: # pylint: disable=duplicate-except
|
||||
module.fail_json_aws(e, msg="An error occurred while searching for instance_role {0}. Please try supplying the full ARN.".format(name_or_arn))
|
||||
|
||||
|
||||
def existing_templates(module):
|
||||
ec2 = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff())
|
||||
matches = None
|
||||
try:
|
||||
if module.params.get('template_id'):
|
||||
matches = ec2.describe_launch_templates(LaunchTemplateIds=[module.params.get('template_id')])
|
||||
elif module.params.get('template_name'):
|
||||
matches = ec2.describe_launch_templates(LaunchTemplateNames=[module.params.get('template_name')])
|
||||
except is_boto3_error_code('InvalidLaunchTemplateName.NotFoundException') as e:
|
||||
# no named template was found, return nothing/empty versions
|
||||
return None, []
|
||||
except is_boto3_error_code('InvalidLaunchTemplateId.Malformed') as e: # pylint: disable=duplicate-except
|
||||
module.fail_json_aws(e, msg='Launch template with ID {0} is not a valid ID. It should start with `lt-....`'.format(
|
||||
module.params.get('launch_template_id')))
|
||||
except is_boto3_error_code('InvalidLaunchTemplateId.NotFoundException') as e: # pylint: disable=duplicate-except
|
||||
module.fail_json_aws(
|
||||
e, msg='Launch template with ID {0} could not be found, please supply a name '
|
||||
'instead so that a new template can be created'.format(module.params.get('launch_template_id')))
|
||||
except (ClientError, BotoCoreError, WaiterError) as e: # pylint: disable=duplicate-except
|
||||
module.fail_json_aws(e, msg='Could not check existing launch templates. This may be an IAM permission problem.')
|
||||
else:
|
||||
template = matches['LaunchTemplates'][0]
|
||||
template_id, template_version, template_default = template['LaunchTemplateId'], template['LatestVersionNumber'], template['DefaultVersionNumber']
|
||||
try:
|
||||
return template, ec2.describe_launch_template_versions(LaunchTemplateId=template_id)['LaunchTemplateVersions']
|
||||
except (ClientError, BotoCoreError, WaiterError) as e:
|
||||
module.fail_json_aws(e, msg='Could not find launch template versions for {0} (ID: {1}).'.format(template['LaunchTemplateName'], template_id))
|
||||
|
||||
|
||||
def params_to_launch_data(module, template_params):
|
||||
if template_params.get('tags'):
|
||||
template_params['tag_specifications'] = [
|
||||
{
|
||||
'resource_type': r_type,
|
||||
'tags': [
|
||||
{'Key': k, 'Value': v} for k, v
|
||||
in template_params['tags'].items()
|
||||
]
|
||||
}
|
||||
for r_type in ('instance', 'network-interface', 'volume')
|
||||
]
|
||||
del template_params['tags']
|
||||
if module.params.get('iam_instance_profile'):
|
||||
template_params['iam_instance_profile'] = determine_iam_role(module, module.params['iam_instance_profile'])
|
||||
params = snake_dict_to_camel_dict(
|
||||
dict((k, v) for k, v in template_params.items() if v is not None),
|
||||
capitalize_first=True,
|
||||
)
|
||||
return params
|
||||
|
||||
|
||||
def delete_template(module):
|
||||
ec2 = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff())
|
||||
template, template_versions = existing_templates(module)
|
||||
deleted_versions = []
|
||||
if template or template_versions:
|
||||
non_default_versions = [to_text(t['VersionNumber']) for t in template_versions if not t['DefaultVersion']]
|
||||
if non_default_versions:
|
||||
try:
|
||||
v_resp = ec2.delete_launch_template_versions(
|
||||
LaunchTemplateId=template['LaunchTemplateId'],
|
||||
Versions=non_default_versions,
|
||||
)
|
||||
if v_resp['UnsuccessfullyDeletedLaunchTemplateVersions']:
|
||||
module.warn('Failed to delete template versions {0} on launch template {1}'.format(
|
||||
v_resp['UnsuccessfullyDeletedLaunchTemplateVersions'],
|
||||
template['LaunchTemplateId'],
|
||||
))
|
||||
deleted_versions = [camel_dict_to_snake_dict(v) for v in v_resp['SuccessfullyDeletedLaunchTemplateVersions']]
|
||||
except (ClientError, BotoCoreError) as e:
|
||||
module.fail_json_aws(e, msg="Could not delete existing versions of the launch template {0}".format(template['LaunchTemplateId']))
|
||||
try:
|
||||
resp = ec2.delete_launch_template(
|
||||
LaunchTemplateId=template['LaunchTemplateId'],
|
||||
)
|
||||
except (ClientError, BotoCoreError) as e:
|
||||
module.fail_json_aws(e, msg="Could not delete launch template {0}".format(template['LaunchTemplateId']))
|
||||
return {
|
||||
'deleted_versions': deleted_versions,
|
||||
'deleted_template': camel_dict_to_snake_dict(resp['LaunchTemplate']),
|
||||
'changed': True,
|
||||
}
|
||||
else:
|
||||
return {'changed': False}
|
||||
|
||||
|
||||
def create_or_update(module, template_options):
|
||||
ec2 = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff())
|
||||
template, template_versions = existing_templates(module)
|
||||
out = {}
|
||||
lt_data = params_to_launch_data(module, dict((k, v) for k, v in module.params.items() if k in template_options))
|
||||
if not (template or template_versions):
|
||||
# create a full new one
|
||||
try:
|
||||
resp = ec2.create_launch_template(
|
||||
LaunchTemplateName=module.params['template_name'],
|
||||
LaunchTemplateData=lt_data,
|
||||
ClientToken=uuid4().hex,
|
||||
aws_retry=True,
|
||||
)
|
||||
except (ClientError, BotoCoreError) as e:
|
||||
module.fail_json_aws(e, msg="Couldn't create launch template")
|
||||
template, template_versions = existing_templates(module)
|
||||
out['changed'] = True
|
||||
elif template and template_versions:
|
||||
most_recent = sorted(template_versions, key=lambda x: x['VersionNumber'])[-1]
|
||||
if lt_data == most_recent['LaunchTemplateData']:
|
||||
out['changed'] = False
|
||||
return out
|
||||
try:
|
||||
resp = ec2.create_launch_template_version(
|
||||
LaunchTemplateId=template['LaunchTemplateId'],
|
||||
LaunchTemplateData=lt_data,
|
||||
ClientToken=uuid4().hex,
|
||||
aws_retry=True,
|
||||
)
|
||||
if module.params.get('default_version') in (None, ''):
|
||||
# no need to do anything, leave the existing version as default
|
||||
pass
|
||||
elif module.params.get('default_version') == 'latest':
|
||||
set_default = ec2.modify_launch_template(
|
||||
LaunchTemplateId=template['LaunchTemplateId'],
|
||||
DefaultVersion=to_text(resp['LaunchTemplateVersion']['VersionNumber']),
|
||||
ClientToken=uuid4().hex,
|
||||
aws_retry=True,
|
||||
)
|
||||
else:
|
||||
try:
|
||||
int(module.params.get('default_version'))
|
||||
except ValueError:
|
||||
module.fail_json(msg='default_version param was not a valid integer, got "{0}"'.format(module.params.get('default_version')))
|
||||
set_default = ec2.modify_launch_template(
|
||||
LaunchTemplateId=template['LaunchTemplateId'],
|
||||
DefaultVersion=to_text(int(module.params.get('default_version'))),
|
||||
ClientToken=uuid4().hex,
|
||||
aws_retry=True,
|
||||
)
|
||||
except (ClientError, BotoCoreError) as e:
|
||||
module.fail_json_aws(e, msg="Couldn't create subsequent launch template version")
|
||||
template, template_versions = existing_templates(module)
|
||||
out['changed'] = True
|
||||
return out
|
||||
|
||||
|
||||
def format_module_output(module):
|
||||
output = {}
|
||||
template, template_versions = existing_templates(module)
|
||||
template = camel_dict_to_snake_dict(template)
|
||||
template_versions = [camel_dict_to_snake_dict(v) for v in template_versions]
|
||||
for v in template_versions:
|
||||
for ts in (v['launch_template_data'].get('tag_specifications') or []):
|
||||
ts['tags'] = boto3_tag_list_to_ansible_dict(ts.pop('tags'))
|
||||
output.update(dict(template=template, versions=template_versions))
|
||||
output['default_template'] = [
|
||||
v for v in template_versions
|
||||
if v.get('default_version')
|
||||
][0]
|
||||
output['latest_template'] = [
|
||||
v for v in template_versions
|
||||
if (
|
||||
v.get('version_number') and
|
||||
int(v['version_number']) == int(template['latest_version_number'])
|
||||
)
|
||||
][0]
|
||||
return output
|
||||
|
||||
|
||||
def main():
|
||||
template_options = dict(
|
||||
block_device_mappings=dict(
|
||||
type='list',
|
||||
options=dict(
|
||||
device_name=dict(),
|
||||
ebs=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
delete_on_termination=dict(type='bool'),
|
||||
encrypted=dict(type='bool'),
|
||||
iops=dict(type='int'),
|
||||
kms_key_id=dict(),
|
||||
snapshot_id=dict(),
|
||||
volume_size=dict(type='int'),
|
||||
volume_type=dict(),
|
||||
),
|
||||
),
|
||||
no_device=dict(),
|
||||
virtual_name=dict(),
|
||||
),
|
||||
),
|
||||
cpu_options=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
core_count=dict(type='int'),
|
||||
threads_per_core=dict(type='int'),
|
||||
),
|
||||
),
|
||||
credit_specification=dict(
|
||||
dict(type='dict'),
|
||||
options=dict(
|
||||
cpu_credits=dict(),
|
||||
),
|
||||
),
|
||||
disable_api_termination=dict(type='bool'),
|
||||
ebs_optimized=dict(type='bool'),
|
||||
elastic_gpu_specifications=dict(
|
||||
options=dict(type=dict()),
|
||||
type='list',
|
||||
),
|
||||
iam_instance_profile=dict(),
|
||||
image_id=dict(),
|
||||
instance_initiated_shutdown_behavior=dict(choices=['stop', 'terminate']),
|
||||
instance_market_options=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
market_type=dict(),
|
||||
spot_options=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
block_duration_minutes=dict(type='int'),
|
||||
instance_interruption_behavior=dict(choices=['hibernate', 'stop', 'terminate']),
|
||||
max_price=dict(),
|
||||
spot_instance_type=dict(choices=['one-time', 'persistent']),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
instance_type=dict(),
|
||||
kernel_id=dict(),
|
||||
key_name=dict(),
|
||||
monitoring=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
enabled=dict(type='bool')
|
||||
),
|
||||
),
|
||||
network_interfaces=dict(
|
||||
type='list',
|
||||
options=dict(
|
||||
associate_public_ip_address=dict(type='bool'),
|
||||
delete_on_termination=dict(type='bool'),
|
||||
description=dict(),
|
||||
device_index=dict(type='int'),
|
||||
groups=dict(type='list'),
|
||||
ipv6_address_count=dict(type='int'),
|
||||
ipv6_addresses=dict(type='list'),
|
||||
network_interface_id=dict(),
|
||||
private_ip_address=dict(),
|
||||
subnet_id=dict(),
|
||||
),
|
||||
),
|
||||
placement=dict(
|
||||
options=dict(
|
||||
affinity=dict(),
|
||||
availability_zone=dict(),
|
||||
group_name=dict(),
|
||||
host_id=dict(),
|
||||
tenancy=dict(),
|
||||
),
|
||||
type='dict',
|
||||
),
|
||||
ram_disk_id=dict(),
|
||||
security_group_ids=dict(type='list'),
|
||||
security_groups=dict(type='list'),
|
||||
tags=dict(type='dict'),
|
||||
user_data=dict(),
|
||||
)
|
||||
|
||||
arg_spec = dict(
|
||||
state=dict(choices=['present', 'absent'], default='present'),
|
||||
template_name=dict(aliases=['name']),
|
||||
template_id=dict(aliases=['id']),
|
||||
default_version=dict(default='latest'),
|
||||
)
|
||||
|
||||
arg_spec.update(template_options)
|
||||
|
||||
module = AnsibleAWSModule(
|
||||
argument_spec=arg_spec,
|
||||
required_one_of=[
|
||||
('template_name', 'template_id')
|
||||
],
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
if not module.boto3_at_least('1.6.0'):
|
||||
module.fail_json(msg="ec2_launch_template requires boto3 >= 1.6.0")
|
||||
|
||||
for interface in (module.params.get('network_interfaces') or []):
|
||||
if interface.get('ipv6_addresses'):
|
||||
interface['ipv6_addresses'] = [{'ipv6_address': x} for x in interface['ipv6_addresses']]
|
||||
|
||||
if module.params.get('state') == 'present':
|
||||
out = create_or_update(module, template_options)
|
||||
out.update(format_module_output(module))
|
||||
elif module.params.get('state') == 'absent':
|
||||
out = delete_template(module)
|
||||
else:
|
||||
module.fail_json(msg='Unsupported value "{0}" for `state` parameter'.format(module.params.get('state')))
|
||||
|
||||
module.exit_json(**out)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Loading…
Add table
Add a link
Reference in a new issue