diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e564b2abf5d..2a61b0a1120 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -6,7 +6,7 @@ General information about setting up your Python environment, testing modules,
Ansible coding styles, and more can be found in the [Ansible Community Guide](
https://docs.ansible.com/ansible/latest/community/index.html).
-Information about boto library usage, module utils, testing, and more can be
+Information about AWS SDK library usage, module utils, testing, and more can be
found in the [AWS Guidelines](https://docs.ansible.com/ansible/devel/dev_guide/platforms/aws_guidelines.html)
documentation.
@@ -41,7 +41,7 @@ issue, or by reporting any additional information
## Pull Requests
-All modules MUST have integration tests for new features. Upgrading to boto3 shall be considered a feature request.
+All modules MUST have integration tests for new features.
Bug fixes for modules that currently have integration tests SHOULD have tests added.
New modules should be submitted to the [community.aws](https://github.com/ansible-collections/community.aws) collection
and MUST have integration tests.
diff --git a/README.md b/README.md
index acee1044843..df65cb6ff59 100644
--- a/README.md
+++ b/README.md
@@ -23,7 +23,7 @@ Starting with the 2.0.0 releases of amazon.aws and community.aws, it is generall
Version 3.0.0 of this collection supports `boto3 >= 1.16.0` and `botocore >= 1.19.0`
-Support for the original AWS SDK `boto` has been deprecated and the module_utils library code to support it will be removed in release 4.0.0.
+All support for the original AWS SDK `boto` was removed in release 4.0.0.
## Included content
@@ -110,7 +110,7 @@ be manually installed using pip:
or:
- pip install boto boto3 botocore
+ pip install boto3 botocore
## Using this collection
diff --git a/changelogs/fragments/630-remove-boto.yml b/changelogs/fragments/630-remove-boto.yml
new file mode 100644
index 00000000000..1fd979ff7de
--- /dev/null
+++ b/changelogs/fragments/630-remove-boto.yml
@@ -0,0 +1,4 @@
+breaking_changes:
+- ec2 - The ``ec2`` module has been removed in release 4.0.0 and replaced by the ``ec2_instance`` module (https://github.com/ansible-collections/amazon.aws/pull/630).
+- module_utils - Support for the original AWS SDK aka ``boto`` has been removed, including all relevant helper functions.
+ All modules should now use the ``boto3``/``botocore`` AWS SDK (https://github.com/ansible-collections/amazon.aws/pull/630)
diff --git a/plugins/doc_fragments/aws.py b/plugins/doc_fragments/aws.py
index a9b5747c13b..8db07f9c5ef 100644
--- a/plugins/doc_fragments/aws.py
+++ b/plugins/doc_fragments/aws.py
@@ -53,7 +53,6 @@ class ModuleDocFragment(object):
aws_ca_bundle:
description:
- "The location of a CA Bundle to use when validating SSL certificates."
- - "Not used by boto 2 based modules."
- "Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally."
type: path
validate_certs:
@@ -73,7 +72,6 @@ class ModuleDocFragment(object):
description:
- A dictionary to modify the botocore configuration.
- Parameters can be found at U(https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config).
- - Only the 'user_agent' key is used for boto modules. See U(http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto) for more boto configuration.
type: dict
requirements:
- python >= 3.6
@@ -94,10 +92,6 @@ class ModuleDocFragment(object):
C(~/.aws/credentials)).
See U(https://boto3.amazonaws.com/v1/documentation/api/latest/guide/credentials.html)
for more information.
- - Modules based on the original AWS SDK (boto) may read their default
- configuration from different files.
- See U(https://boto.readthedocs.io/en/latest/boto_config_tut.html) for more
- information.
- C(AWS_REGION) or C(EC2_REGION) can be typically be used to specify the
AWS region, when required, but this can also be defined in the
configuration files.
diff --git a/plugins/module_utils/ec2.py b/plugins/module_utils/ec2.py
index ea2e21f383b..9815f8c3170 100644
--- a/plugins/module_utils/ec2.py
+++ b/plugins/module_utils/ec2.py
@@ -31,7 +31,6 @@
import os
import re
-import sys
import traceback
from ansible.module_utils._text import to_native
@@ -61,15 +60,6 @@
# Used to live here, moved into # ansible_collections.amazon.aws.plugins.module_utils.retries
from .retries import AWSRetry # pylint: disable=unused-import
-BOTO_IMP_ERR = None
-try:
- import boto
- import boto.ec2 # boto does weird import stuff
- HAS_BOTO = True
-except ImportError:
- BOTO_IMP_ERR = traceback.format_exc()
- HAS_BOTO = False
-
BOTO3_IMP_ERR = None
try:
import boto3
@@ -173,12 +163,15 @@ def ec2_argument_spec():
return spec
-def get_aws_region(module, boto3=False):
+def get_aws_region(module, boto3=None):
region = module.params.get('region')
if region:
return region
+ if not HAS_BOTO3:
+ module.fail_json(msg=missing_required_lib('boto3'), exception=BOTO3_IMP_ERR)
+
if 'AWS_REGION' in os.environ:
return os.environ['AWS_REGION']
if 'AWS_DEFAULT_REGION' in os.environ:
@@ -186,18 +179,6 @@ def get_aws_region(module, boto3=False):
if 'EC2_REGION' in os.environ:
return os.environ['EC2_REGION']
- if not boto3:
- if not HAS_BOTO:
- module.fail_json(msg=missing_required_lib('boto'), exception=BOTO_IMP_ERR)
- # boto.config.get returns None if config not found
- region = boto.config.get('Boto', 'aws_region')
- if region:
- return region
- return boto.config.get('Boto', 'ec2_region')
-
- if not HAS_BOTO3:
- module.fail_json(msg=missing_required_lib('boto3'), exception=BOTO3_IMP_ERR)
-
# here we don't need to make an additional call, will default to 'us-east-1' if the below evaluates to None.
try:
profile_name = module.params.get('profile')
@@ -206,7 +187,7 @@ def get_aws_region(module, boto3=False):
return None
-def get_aws_connection_info(module, boto3=False):
+def get_aws_connection_info(module, boto3=None):
# Check module args for credentials, then check environment vars
# access_key
@@ -248,10 +229,6 @@ def get_aws_connection_info(module, boto3=False):
access_key = os.environ['AWS_ACCESS_KEY']
elif os.environ.get('EC2_ACCESS_KEY'):
access_key = os.environ['EC2_ACCESS_KEY']
- elif HAS_BOTO and boto.config.get('Credentials', 'aws_access_key_id'):
- access_key = boto.config.get('Credentials', 'aws_access_key_id')
- elif HAS_BOTO and boto.config.get('default', 'aws_access_key_id'):
- access_key = boto.config.get('default', 'aws_access_key_id')
else:
# in case access_key came in as empty string
access_key = None
@@ -263,10 +240,6 @@ def get_aws_connection_info(module, boto3=False):
secret_key = os.environ['AWS_SECRET_KEY']
elif os.environ.get('EC2_SECRET_KEY'):
secret_key = os.environ['EC2_SECRET_KEY']
- elif HAS_BOTO and boto.config.get('Credentials', 'aws_secret_access_key'):
- secret_key = boto.config.get('Credentials', 'aws_secret_access_key')
- elif HAS_BOTO and boto.config.get('default', 'aws_secret_access_key'):
- secret_key = boto.config.get('default', 'aws_secret_access_key')
else:
# in case secret_key came in as empty string
secret_key = None
@@ -278,10 +251,6 @@ def get_aws_connection_info(module, boto3=False):
security_token = os.environ['AWS_SESSION_TOKEN']
elif os.environ.get('EC2_SECURITY_TOKEN'):
security_token = os.environ['EC2_SECURITY_TOKEN']
- elif HAS_BOTO and boto.config.get('Credentials', 'aws_security_token'):
- security_token = boto.config.get('Credentials', 'aws_security_token')
- elif HAS_BOTO and boto.config.get('default', 'aws_security_token'):
- security_token = boto.config.get('default', 'aws_security_token')
else:
# in case secret_token came in as empty string
security_token = None
@@ -290,37 +259,21 @@ def get_aws_connection_info(module, boto3=False):
if os.environ.get('AWS_CA_BUNDLE'):
ca_bundle = os.environ.get('AWS_CA_BUNDLE')
- if HAS_BOTO3 and boto3:
- boto_params = dict(aws_access_key_id=access_key,
- aws_secret_access_key=secret_key,
- aws_session_token=security_token)
-
- if profile_name:
- boto_params = dict(aws_access_key_id=None, aws_secret_access_key=None, aws_session_token=None)
- boto_params['profile_name'] = profile_name
+ boto_params = dict(aws_access_key_id=access_key,
+ aws_secret_access_key=secret_key,
+ aws_session_token=security_token)
- if validate_certs and ca_bundle:
- boto_params['verify'] = ca_bundle
- else:
- boto_params['verify'] = validate_certs
+ if profile_name:
+ boto_params = dict(aws_access_key_id=None, aws_secret_access_key=None, aws_session_token=None)
+ boto_params['profile_name'] = profile_name
+ if validate_certs and ca_bundle:
+ boto_params['verify'] = ca_bundle
else:
- boto_params = dict(aws_access_key_id=access_key,
- aws_secret_access_key=secret_key,
- security_token=security_token)
-
- # only set profile_name if passed as an argument
- if profile_name:
- boto_params['profile_name'] = profile_name
-
- boto_params['validate_certs'] = validate_certs
+ boto_params['verify'] = validate_certs
if config is not None:
- if HAS_BOTO3 and boto3:
- boto_params['aws_config'] = botocore.config.Config(**config)
- elif HAS_BOTO and not boto3:
- if 'user_agent' in config:
- sys.modules["boto.connection"].UserAgent = config['user_agent']
+ boto_params['aws_config'] = botocore.config.Config(**config)
for param, value in boto_params.items():
if isinstance(value, binary_type):
@@ -329,61 +282,6 @@ def get_aws_connection_info(module, boto3=False):
return region, ec2_url, boto_params
-def get_ec2_creds(module):
- ''' for compatibility mode with old modules that don't/can't yet
- use ec2_connect method '''
- region, ec2_url, boto_params = get_aws_connection_info(module)
- return ec2_url, boto_params['aws_access_key_id'], boto_params['aws_secret_access_key'], region
-
-
-def boto_fix_security_token_in_profile(conn, profile_name):
- ''' monkey patch for boto issue boto/boto#2100 '''
- profile = 'profile ' + profile_name
- if boto.config.has_option(profile, 'aws_security_token'):
- conn.provider.set_security_token(boto.config.get(profile, 'aws_security_token'))
- return conn
-
-
-def connect_to_aws(aws_module, region, **params):
- try:
- conn = aws_module.connect_to_region(region, **params)
- except(boto.provider.ProfileNotFoundError):
- raise AnsibleAWSError("Profile given for AWS was not found. Please fix and retry.")
- if not conn:
- if region not in [aws_module_region.name for aws_module_region in aws_module.regions()]:
- raise AnsibleAWSError("Region %s does not seem to be available for aws module %s. If the region definitely exists, you may need to upgrade "
- "boto or extend with endpoints_path" % (region, aws_module.__name__))
- else:
- raise AnsibleAWSError("Unknown problem connecting to region %s for aws module %s." % (region, aws_module.__name__))
- if params.get('profile_name'):
- conn = boto_fix_security_token_in_profile(conn, params['profile_name'])
- return conn
-
-
-def ec2_connect(module):
-
- """ Return an ec2 connection"""
-
- region, ec2_url, boto_params = get_aws_connection_info(module)
-
- # If ec2_url is present use it
- if ec2_url:
- try:
- ec2 = boto.connect_ec2_endpoint(ec2_url, **boto_params)
- except (boto.exception.NoAuthHandlerFound, AnsibleAWSError, boto.provider.ProfileNotFoundError) as e:
- module.fail_json(msg=str(e))
- # Otherwise, if we have a region specified, connect to its endpoint.
- elif region:
- try:
- ec2 = connect_to_aws(boto.ec2, region, **boto_params)
- except (boto.exception.NoAuthHandlerFound, AnsibleAWSError, boto.provider.ProfileNotFoundError) as e:
- module.fail_json(msg=str(e))
- else:
- module.fail_json(msg="Either region or ec2_url must be specified")
-
- return ec2
-
-
def ansible_dict_to_boto3_filter_list(filters_dict):
""" Convert an Ansible dict of filters to list of dicts that boto3 can use
@@ -424,7 +322,7 @@ def ansible_dict_to_boto3_filter_list(filters_dict):
return filters_list
-def get_ec2_security_group_ids_from_names(sec_group_list, ec2_connection, vpc_id=None, boto3=True):
+def get_ec2_security_group_ids_from_names(sec_group_list, ec2_connection, vpc_id=None, boto3=None):
""" Return list of security group IDs from security group names. Note that security group names are not unique
across VPCs. If a name exists across multiple VPCs and no VPC ID is supplied, all matching IDs will be returned. This
@@ -432,19 +330,11 @@ def get_ec2_security_group_ids_from_names(sec_group_list, ec2_connection, vpc_id
a try block
"""
- def get_sg_name(sg, boto3):
-
- if boto3:
- return sg['GroupName']
- else:
- return sg.name
+ def get_sg_name(sg, boto3=None):
+ return str(sg['GroupName'])
- def get_sg_id(sg, boto3):
-
- if boto3:
- return sg['GroupId']
- else:
- return sg.id
+ def get_sg_id(sg, boto3=None):
+ return str(sg['GroupId'])
sec_group_id_list = []
@@ -452,25 +342,18 @@ def get_sg_id(sg, boto3):
sec_group_list = [sec_group_list]
# Get all security groups
- if boto3:
- if vpc_id:
- filters = [
- {
- 'Name': 'vpc-id',
- 'Values': [
- vpc_id,
- ]
- }
- ]
- all_sec_groups = ec2_connection.describe_security_groups(Filters=filters)['SecurityGroups']
- else:
- all_sec_groups = ec2_connection.describe_security_groups()['SecurityGroups']
+ if vpc_id:
+ filters = [
+ {
+ 'Name': 'vpc-id',
+ 'Values': [
+ vpc_id,
+ ]
+ }
+ ]
+ all_sec_groups = ec2_connection.describe_security_groups(Filters=filters)['SecurityGroups']
else:
- if vpc_id:
- filters = {'vpc-id': vpc_id}
- all_sec_groups = ec2_connection.get_all_security_groups(filters=filters)
- else:
- all_sec_groups = ec2_connection.get_all_security_groups()
+ all_sec_groups = ec2_connection.describe_security_groups()['SecurityGroups']
unmatched = set(sec_group_list).difference(str(get_sg_name(all_sg, boto3)) for all_sg in all_sec_groups)
sec_group_name_list = list(set(sec_group_list) - set(unmatched))
@@ -482,7 +365,7 @@ def get_sg_id(sg, boto3):
if len(still_unmatched) > 0:
raise ValueError("The following group names are not valid: %s" % ', '.join(still_unmatched))
- sec_group_id_list += [str(get_sg_id(all_sg, boto3)) for all_sg in all_sec_groups if str(get_sg_name(all_sg, boto3)) in sec_group_name_list]
+ sec_group_id_list += [get_sg_id(all_sg) for all_sg in all_sec_groups if get_sg_name(all_sg) in sec_group_name_list]
return sec_group_id_list
diff --git a/plugins/modules/ec2.py b/plugins/modules/ec2.py
deleted file mode 100644
index 8cbee640619..00000000000
--- a/plugins/modules/ec2.py
+++ /dev/null
@@ -1,2041 +0,0 @@
-#!/usr/bin/python
-# This file is part of Ansible
-# 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
-
-
-DOCUMENTATION = '''
----
-module: ec2
-version_added: 1.0.0
-short_description: create, terminate, start or stop an instance in ec2
-deprecated:
- removed_in: 4.0.0
- why: The ec2 module is based upon a deprecated version of the AWS SDK.
- alternative: Use M(amazon.aws.ec2_instance).
-description:
- - Creates or terminates ec2 instances.
- - >
- Note: This module uses the older boto Python module to interact with the EC2 API.
- M(amazon.aws.ec2) will still receive bug fixes, but no new features.
- Consider using the M(amazon.aws.ec2_instance) module instead.
- If M(amazon.aws.ec2_instance) does not support a feature you need that is available in M(amazon.aws.ec2), please
- file a feature request.
-options:
- key_name:
- description:
- - Key pair to use on the instance.
- - The SSH key must already exist in AWS in order to use this argument.
- - Keys can be created / deleted using the M(amazon.aws.ec2_key) module.
- aliases: ['keypair']
- type: str
- id:
- description:
- - Identifier for this instance or set of instances, so that the module will be idempotent with respect to EC2 instances.
- - This identifier is valid for at least 24 hours after the termination of the instance, and should not be reused for another call later on.
- - For details, see the description of client token at U(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Run_Instance_Idempotency.html).
- type: str
- group:
- description:
- - Security group (or list of groups) to use with the instance.
- aliases: [ 'groups' ]
- type: list
- elements: str
- group_id:
- description:
- - Security group id (or list of ids) to use with the instance.
- type: list
- elements: str
- zone:
- description:
- - AWS availability zone in which to launch the instance.
- aliases: [ 'aws_zone', 'ec2_zone' ]
- type: str
- instance_type:
- description:
- - Instance type to use for the instance, see U(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html).
- - Required when creating a new instance.
- type: str
- aliases: ['type']
- tenancy:
- description:
- - An instance with a tenancy of C(dedicated) runs on single-tenant hardware and can only be launched into a VPC.
- - Note that to use dedicated tenancy you MUST specify a I(vpc_subnet_id) as well.
- - Dedicated tenancy is not available for EC2 "micro" instances.
- default: default
- choices: [ "default", "dedicated" ]
- type: str
- spot_price:
- description:
- - Maximum spot price to bid. If not set, a regular on-demand instance is requested.
- - A spot request is made with this maximum bid. When it is filled, the instance is started.
- type: str
- spot_type:
- description:
- - The type of spot request.
- - After being interrupted a C(persistent) spot instance will be started once there is capacity to fill the request again.
- default: "one-time"
- choices: [ "one-time", "persistent" ]
- type: str
- image:
- description:
- - I(ami) ID to use for the instance.
- - Required when I(state=present).
- type: str
- kernel:
- description:
- - Kernel eki to use for the instance.
- type: str
- ramdisk:
- description:
- - Ramdisk eri to use for the instance.
- type: str
- wait:
- description:
- - Wait for the instance to reach its desired state before returning.
- - Does not wait for SSH, see the 'wait_for_connection' example for details.
- type: bool
- default: false
- wait_timeout:
- description:
- - How long before wait gives up, in seconds.
- default: 300
- type: int
- spot_wait_timeout:
- description:
- - How long to wait for the spot instance request to be fulfilled. Affects 'Request valid until' for setting spot request lifespan.
- default: 600
- type: int
- count:
- description:
- - Number of instances to launch.
- default: 1
- type: int
- monitoring:
- description:
- - Enable detailed monitoring (CloudWatch) for the instance.
- type: bool
- default: false
- user_data:
- description:
- - Opaque blob of data which is made available to the EC2 instance.
- type: str
- instance_tags:
- description:
- - >
- A hash/dictionary of tags to add to the new instance or for
- instances to start/stop by tag. For example C({"key":"value"}) or
- C({"key":"value","key2":"value2"}).
- type: dict
- placement_group:
- description:
- - Placement group for the instance when using EC2 Clustered Compute.
- type: str
- vpc_subnet_id:
- description:
- - The subnet ID in which to launch the instance (VPC).
- type: str
- assign_public_ip:
- description:
- - When provisioning within vpc, assign a public IP address. Boto library must be 2.13.0+.
- type: bool
- private_ip:
- description:
- - The private ip address to assign the instance (from the vpc subnet).
- type: str
- instance_profile_name:
- description:
- - Name of the IAM instance profile (i.e. what the EC2 console refers to as an "IAM Role") to use. Boto library must be 2.5.0+.
- type: str
- instance_ids:
- description:
- - "list of instance ids, currently used for states: absent, running, stopped"
- aliases: ['instance_id']
- type: list
- elements: str
- source_dest_check:
- description:
- - Enable or Disable the Source/Destination checks (for NAT instances and Virtual Routers).
- When initially creating an instance the EC2 API defaults this to C(True).
- type: bool
- termination_protection:
- description:
- - Enable or Disable the Termination Protection.
- - Defaults to C(false).
- type: bool
- instance_initiated_shutdown_behavior:
- description:
- - Set whether AWS will Stop or Terminate an instance on shutdown. This parameter is ignored when using instance-store.
- images (which require termination on shutdown).
- default: 'stop'
- choices: [ "stop", "terminate" ]
- type: str
- state:
- description:
- - Create, terminate, start, stop or restart instances.
- - When I(state=absent), I(instance_ids) is required.
- - When I(state=running), I(state=stopped) or I(state=restarted) then either I(instance_ids) or I(instance_tags) is required.
- default: 'present'
- choices: ['absent', 'present', 'restarted', 'running', 'stopped']
- type: str
- volumes:
- description:
- - A list of hash/dictionaries of volumes to add to the new instance.
- type: list
- elements: dict
- suboptions:
- device_name:
- type: str
- required: true
- description:
- - A name for the device (For example C(/dev/sda)).
- delete_on_termination:
- type: bool
- default: false
- description:
- - Whether the volume should be automatically deleted when the instance is terminated.
- ephemeral:
- type: str
- description:
- - Whether the volume should be ephemeral.
- - Data on ephemeral volumes is lost when the instance is stopped.
- - Mutually exclusive with the I(snapshot) parameter.
- encrypted:
- type: bool
- default: false
- description:
- - Whether the volume should be encrypted using the 'aws/ebs' KMS CMK.
- snapshot:
- type: str
- description:
- - The ID of an EBS snapshot to copy when creating the volume.
- - Mutually exclusive with the I(ephemeral) parameter.
- volume_type:
- type: str
- description:
- - The type of volume to create.
- - See U(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html) for more information on the available volume types.
- volume_size:
- type: int
- description:
- - The size of the volume (in GiB).
- iops:
- type: int
- description:
- - The number of IOPS per second to provision for the volume.
- - Required when I(volume_type=io1).
- ebs_optimized:
- description:
- - Whether instance is using optimized EBS volumes, see U(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSOptimized.html).
- default: false
- type: bool
- exact_count:
- description:
- - An integer value which indicates how many instances that match the 'count_tag' parameter should be running.
- Instances are either created or terminated based on this value.
- type: int
- count_tag:
- description:
- - Used with I(exact_count) to determine how many nodes based on a specific tag criteria should be running.
- This can be expressed in multiple ways and is shown in the EXAMPLES section. For instance, one can request 25 servers
- that are tagged with C(class=webserver). The specified tag must already exist or be passed in as the I(instance_tags) option.
- type: raw
- network_interfaces:
- description:
- - A list of existing network interfaces to attach to the instance at launch. When specifying existing network interfaces,
- none of the I(assign_public_ip), I(private_ip), I(vpc_subnet_id), I(group), or I(group_id) parameters may be used. (Those parameters are
- for creating a new network interface at launch.)
- aliases: ['network_interface']
- type: list
- elements: str
- spot_launch_group:
- description:
- - Launch group for spot requests, see U(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/how-spot-instances-work.html#spot-launch-group).
- type: str
-author:
- - "Tim Gerla (@tgerla)"
- - "Lester Wade (@lwade)"
- - "Seth Vidal (@skvidal)"
-extends_documentation_fragment:
-- amazon.aws.aws
-- amazon.aws.ec2
-requirements:
-- python >= 2.6
-- boto
-
-'''
-
-EXAMPLES = '''
-# Note: These examples do not set authentication details, see the AWS Guide for details.
-
-# Basic provisioning example
-- amazon.aws.ec2:
- key_name: mykey
- instance_type: t2.micro
- image: ami-123456
- wait: yes
- group: webserver
- count: 3
- vpc_subnet_id: subnet-29e63245
- assign_public_ip: yes
-
-# Advanced example with tagging and CloudWatch
-- amazon.aws.ec2:
- key_name: mykey
- group: databases
- instance_type: t2.micro
- image: ami-123456
- wait: yes
- wait_timeout: 500
- count: 5
- instance_tags:
- db: postgres
- monitoring: yes
- vpc_subnet_id: subnet-29e63245
- assign_public_ip: yes
-
-# Single instance with additional IOPS volume from snapshot and volume delete on termination
-- amazon.aws.ec2:
- key_name: mykey
- group: webserver
- instance_type: c3.medium
- image: ami-123456
- wait: yes
- wait_timeout: 500
- volumes:
- - device_name: /dev/sdb
- snapshot: snap-abcdef12
- volume_type: io1
- iops: 1000
- volume_size: 100
- delete_on_termination: true
- monitoring: yes
- vpc_subnet_id: subnet-29e63245
- assign_public_ip: yes
-
-# Single instance with ssd gp2 root volume
-- amazon.aws.ec2:
- key_name: mykey
- group: webserver
- instance_type: c3.medium
- image: ami-123456
- wait: yes
- wait_timeout: 500
- volumes:
- - device_name: /dev/xvda
- volume_type: gp2
- volume_size: 8
- vpc_subnet_id: subnet-29e63245
- assign_public_ip: yes
- count_tag:
- Name: dbserver
- exact_count: 1
-
-# Multiple groups example
-- amazon.aws.ec2:
- key_name: mykey
- group: ['databases', 'internal-services', 'sshable', 'and-so-forth']
- instance_type: m1.large
- image: ami-6e649707
- wait: yes
- wait_timeout: 500
- count: 5
- instance_tags:
- db: postgres
- monitoring: yes
- vpc_subnet_id: subnet-29e63245
- assign_public_ip: yes
-
-# Multiple instances with additional volume from snapshot
-- amazon.aws.ec2:
- key_name: mykey
- group: webserver
- instance_type: m1.large
- image: ami-6e649707
- wait: yes
- wait_timeout: 500
- count: 5
- volumes:
- - device_name: /dev/sdb
- snapshot: snap-abcdef12
- volume_size: 10
- monitoring: yes
- vpc_subnet_id: subnet-29e63245
- assign_public_ip: yes
-
-# Dedicated tenancy example
-- amazon.aws.ec2:
- assign_public_ip: yes
- group_id: sg-1dc53f72
- key_name: mykey
- image: ami-6e649707
- instance_type: m1.small
- tenancy: dedicated
- vpc_subnet_id: subnet-29e63245
- wait: yes
-
-# Spot instance example
-- amazon.aws.ec2:
- spot_price: 0.24
- spot_wait_timeout: 600
- keypair: mykey
- group_id: sg-1dc53f72
- instance_type: m1.small
- image: ami-6e649707
- wait: yes
- vpc_subnet_id: subnet-29e63245
- assign_public_ip: yes
- spot_launch_group: report_generators
- instance_initiated_shutdown_behavior: terminate
-
-# Examples using pre-existing network interfaces
-- amazon.aws.ec2:
- key_name: mykey
- instance_type: t2.small
- image: ami-f005ba11
- network_interface: eni-deadbeef
-
-- amazon.aws.ec2:
- key_name: mykey
- instance_type: t2.small
- image: ami-f005ba11
- network_interfaces: ['eni-deadbeef', 'eni-5ca1ab1e']
-
-# Launch instances, runs some tasks
-# and then terminate them
-
-- name: Create a sandbox instance
- hosts: localhost
- gather_facts: False
- vars:
- keypair: my_keypair
- instance_type: m1.small
- security_group: my_securitygroup
- image: my_ami_id
- region: us-east-1
- tasks:
- - name: Launch instance
- amazon.aws.ec2:
- key_name: "{{ keypair }}"
- group: "{{ security_group }}"
- instance_type: "{{ instance_type }}"
- image: "{{ image }}"
- wait: true
- region: "{{ region }}"
- vpc_subnet_id: subnet-29e63245
- assign_public_ip: yes
- register: ec2
-
- - name: Add new instance to host group
- add_host:
- hostname: "{{ item.public_ip }}"
- groupname: launched
- loop: "{{ ec2.instances }}"
-
- - name: Wait for SSH to come up
- delegate_to: "{{ item.public_dns_name }}"
- wait_for_connection:
- delay: 60
- timeout: 320
- loop: "{{ ec2.instances }}"
-
-- name: Configure instance(s)
- hosts: launched
- become: True
- gather_facts: True
- roles:
- - my_awesome_role
- - my_awesome_test
-
-- name: Terminate instances
- hosts: localhost
- tasks:
- - name: Terminate instances that were previously launched
- amazon.aws.ec2:
- state: 'absent'
- instance_ids: '{{ ec2.instance_ids }}'
-
-# Start a few existing instances, run some tasks
-# and stop the instances
-
-- name: Start sandbox instances
- hosts: localhost
- gather_facts: false
- vars:
- instance_ids:
- - 'i-xxxxxx'
- - 'i-xxxxxx'
- - 'i-xxxxxx'
- region: us-east-1
- tasks:
- - name: Start the sandbox instances
- amazon.aws.ec2:
- instance_ids: '{{ instance_ids }}'
- region: '{{ region }}'
- state: running
- wait: True
- vpc_subnet_id: subnet-29e63245
- assign_public_ip: yes
- roles:
- - do_neat_stuff
- - do_more_neat_stuff
-
-- name: Stop sandbox instances
- hosts: localhost
- gather_facts: false
- vars:
- instance_ids:
- - 'i-xxxxxx'
- - 'i-xxxxxx'
- - 'i-xxxxxx'
- region: us-east-1
- tasks:
- - name: Stop the sandbox instances
- amazon.aws.ec2:
- instance_ids: '{{ instance_ids }}'
- region: '{{ region }}'
- state: stopped
- wait: True
- vpc_subnet_id: subnet-29e63245
- assign_public_ip: yes
-
-#
-# Start stopped instances specified by tag
-#
-- amazon.aws.ec2:
- instance_tags:
- Name: ExtraPower
- state: running
-
-#
-# Restart instances specified by tag
-#
-- amazon.aws.ec2:
- instance_tags:
- Name: ExtraPower
- state: restarted
-
-#
-# Enforce that 5 instances with a tag "foo" are running
-# (Highly recommended!)
-#
-
-- amazon.aws.ec2:
- key_name: mykey
- instance_type: c1.medium
- image: ami-40603AD1
- wait: yes
- group: webserver
- instance_tags:
- foo: bar
- exact_count: 5
- count_tag: foo
- vpc_subnet_id: subnet-29e63245
- assign_public_ip: yes
-
-#
-# Enforce that 5 running instances named "database" with a "dbtype" of "postgres"
-#
-
-- amazon.aws.ec2:
- key_name: mykey
- instance_type: c1.medium
- image: ami-40603AD1
- wait: yes
- group: webserver
- instance_tags:
- Name: database
- dbtype: postgres
- exact_count: 5
- count_tag:
- Name: database
- dbtype: postgres
- vpc_subnet_id: subnet-29e63245
- assign_public_ip: yes
-
-#
-# count_tag complex argument examples
-#
-
- # instances with tag foo
-- amazon.aws.ec2:
- count_tag:
- foo:
-
- # instances with tag foo=bar
-- amazon.aws.ec2:
- count_tag:
- foo: bar
-
- # instances with tags foo=bar & baz
-- amazon.aws.ec2:
- count_tag:
- foo: bar
- baz:
-
- # instances with tags foo & bar & baz=bang
-- amazon.aws.ec2:
- count_tag:
- - foo
- - bar
- - baz: bang
-
-'''
-
-RETURN = r'''
-changed:
- description: If the EC2 instance has changed.
- type: bool
- returned: always
- sample: true
-instances:
- description: The instances.
- type: list
- returned: always
- contains:
- ami_launch_index:
- description: The AMI launch index, which can be used to find this instance in the launch group.
- type: int
- returned: always
- sample: 0
- architecture:
- description: The architecture of the image.
- type: str
- returned: always
- sample: "x86_64"
- block_device_mapping:
- description: Any block device mapping entries for the instance.
- type: dict
- returned: always
- sample: {
- "/dev/xvda": {
- "delete_on_termination": true,
- "status": "attached",
- "volume_id": "vol-06d364586f5550b62"
- }
- }
- dns_name:
- description: The public DNS name assigned to the instance.
- type: str
- returned: always
- sample: "ec2-203-0-113-1.z-2.compute-1.amazonaws.com"
- ebs_optimized:
- description: Indicates whether the instance is optimized for Amazon EBS I/O.
- type: bool
- returned: always
- sample: false
- groups:
- description: One or more security groups.
- type: dict
- returned: always
- sample: {
- "sg-0c6562ab3d435619f": "ansible-test--88312190_setup"
- }
- hypervisor:
- description: The hypervisor type of the instance.
- type: str
- returned: always
- sample: "xen"
- image_id:
- description: The ID of the AMI used to launch the instance.
- type: str
- returned: always
- sample: "ami-0d5eff06f840b45e9"
- instance_id:
- description: The ID of the instance.
- type: str
- returned: always
- sample: "i-0250719204c428be1"
- instance_type:
- description: The instance type.
- type: str
- returned: always
- sample: "t2.micro"
- kernel:
- description: The kernel associated with this instance, if applicable.
- type: str
- returned: always
- sample: ""
- key_name:
- description: The name of the key pair, if this instance was launched with an associated key pair.
- type: str
- returned: always
- sample: "ansible-test-88312190_setup"
- launch_time:
- description: The time the instance was launched.
- type: str
- returned: always
- sample: "2021-05-09T19:30:26.000Z"
- placement:
- description: The location where the instance launched, if applicable.
- type: dict
- returned: always
- sample: {
- "availability_zone": "us-east-1a",
- "group_name": "",
- "tenancy": "default"
- }
- private_dns_name:
- description: The private DNS hostname name assigned to the instance.
- type: str
- returned: always
- sample: "ip-10-176-1-249.ec2.internal"
- private_ip:
- description: The private IPv4 address assigned to the instance.
- type: str
- returned: always
- sample: "10.176.1.249"
- public_dns_name:
- description: The public DNS name assigned to the instance.
- type: str
- returned: always
- sample: "ec2-203-0-113-1.z-2.compute-1.amazonaws.com"
- public_ip:
- description: The public IPv4 address, or the Carrier IP address assigned to the instance, if applicable.
- type: str
- returned: always
- sample: "203.0.113.1"
- ramdisk:
- description: The RAM disk associated with this instance, if applicable.
- type: str
- returned: always
- sample: ""
- root_device_name:
- description: The device name of the root device volume.
- type: str
- returned: always
- sample: "/dev/xvda"
- root_device_type:
- description: The root device type used by the AMI.
- type: str
- returned: always
- sample: "ebs"
- state:
- description: The current state of the instance.
- type: dict
- returned: always
- sample: {
- "code": 80,
- "name": "stopped"
- }
- tags:
- description: Any tags assigned to the instance.
- type: dict
- returned: always
- sample: {
- "ResourcePrefix": "ansible-test-88312190-integration_tests"
- }
- tenancy:
- description: The tenancy of the instance (if the instance is running in a VPC).
- type: str
- returned: always
- sample: "default"
- virtualization_type:
- description: The virtualization type of the instance.
- type: str
- returned: always
- sample: "hvm"
- monitoring:
- description: The monitoring for the instance.
- type: dict
- returned: always
- sample: {
- "state": "disabled"
- }
- capacity_reservation_specification:
- description: Information about the Capacity Reservation targeting option.
- type: dict
- returned: always
- sample: {
- "capacity_reservation_preference": "open"
- }
- client_token:
- description: The idempotency token you provided when you launched the instance, if applicable.
- type: str
- returned: always
- sample: ""
- cpu_options:
- description: The CPU options for the instance.
- type: dict
- returned: always
- sample: {
- "core_count": 1,
- "threads_per_core": 1
- }
- ena_support:
- description: Specifies whether enhanced networking with ENA is enabled.
- type: bool
- returned: always
- sample: true
- enclave_options:
- description: Indicates whether the instance is enabled for AWS Nitro Enclaves.
- type: dict
- returned: always
- sample: {
- "enabled": false
- }
- hibernation_options:
- description: Indicates whether the instance is enabled for hibernation.
- type: dict
- returned: always
- sample: {
- "configured": false
- }
- network_interfaces:
- description: The network interfaces for the instance.
- type: list
- returned: always
- sample: [
- {
- "attachment": {
- "attach_time": "2021-05-09T19:30:57+00:00",
- "attachment_id": "eni-attach-07341f2560be6c8fc",
- "delete_on_termination": true,
- "device_index": 0,
- "network_card_index": 0,
- "status": "attached"
- },
- "description": "",
- "groups": [
- {
- "group_id": "sg-0c6562ab3d435619f",
- "group_name": "ansible-test-88312190_setup"
- }
- ],
- "interface_type": "interface",
- "ipv6_addresses": [],
- "mac_address": "0e:0e:36:60:67:cf",
- "network_interface_id": "eni-061dee20eba3b445a",
- "owner_id": "721066863947",
- "private_dns_name": "ip-10-176-1-178.ec2.internal",
- "private_ip_address": "10.176.1.178",
- "private_ip_addresses": [
- {
- "primary": true,
- "private_dns_name": "ip-10-176-1-178.ec2.internal",
- "private_ip_address": "10.176.1.178"
- }
- ],
- "source_dest_check": true,
- "status": "in-use",
- "subnet_id": "subnet-069d3e2eab081955d",
- "vpc_id": "vpc-0b6879b6ca2e9be2b"
- }
- ]
- vpc_id:
- description: The ID of the VPC in which the instance is running.
- type: str
- returned: always
- sample: "vpc-0b6879b6ca2e9be2b"
- subnet_id:
- description: The ID of the subnet in which the instance is running.
- type: str
- returned: always
- sample: "subnet-069d3e2eab081955d"
- state_transition_reason:
- description: The reason for the most recent state transition. This might be an empty string.
- type: str
- returned: always
- sample: "User initiated (2021-05-09 19:31:28 GMT)"
- state_reason:
- description: The reason for the most recent state transition.
- type: dict
- returned: always
- sample: {
- "code": "Client.UserInitiatedShutdown",
- "message": "Client.UserInitiatedShutdown: User initiated shutdown"
- }
- security_groups:
- description: The security groups for the instance.
- type: list
- returned: always
- sample: [
- {
- "group_id": "sg-0c6562ab3d435619f",
- "group_name": "ansible-test-alinas-mbp-88312190_setup"
- }
- ]
- source_dest_check:
- description: Indicates whether source/destination checking is enabled.
- type: bool
- returned: always
- sample: true
- metadata:
- description: The metadata options for the instance.
- type: dict
- returned: always
- sample: {
- "http_endpoint": "enabled",
- "http_put_response_hop_limit": 1,
- "http_tokens": "optional",
- "state": "applied"
- }
-'''
-
-
-import time
-import datetime
-from ast import literal_eval
-
-try:
- import boto.ec2
- from boto.ec2.blockdevicemapping import BlockDeviceType
- from boto.ec2.blockdevicemapping import BlockDeviceMapping
- from boto.exception import EC2ResponseError
- from boto import connect_ec2_endpoint
- from boto import connect_vpc
-except ImportError:
- pass # Taken care of by ec2.HAS_BOTO
-
-from ansible.module_utils.six import get_function_code
-from ansible.module_utils.six import string_types
-from ansible.module_utils._text import to_bytes
-from ansible.module_utils._text import to_text
-
-from ..module_utils.core import AnsibleAWSModule
-from ..module_utils.ec2 import HAS_BOTO
-from ..module_utils.ec2 import ec2_connect
-from ..module_utils.ec2 import get_aws_connection_info
-from ..module_utils.version import LooseVersion
-
-
-def find_running_instances_by_count_tag(module, ec2, vpc, count_tag, zone=None):
-
- # get reservations for instances that match tag(s) and are in the desired state
- state = module.params.get('state')
- if state not in ['running', 'stopped']:
- state = None
- reservations = get_reservations(module, ec2, vpc, tags=count_tag, state=state, zone=zone)
-
- instances = []
- for res in reservations:
- if hasattr(res, 'instances'):
- for inst in res.instances:
- if inst.state == 'terminated' or inst.state == 'shutting-down':
- continue
- instances.append(inst)
-
- return reservations, instances
-
-
-def _set_none_to_blank(dictionary):
- result = dictionary
- for k in result:
- if isinstance(result[k], dict):
- result[k] = _set_none_to_blank(result[k])
- elif not result[k]:
- result[k] = ""
- return result
-
-
-def get_reservations(module, ec2, vpc, tags=None, state=None, zone=None):
- # TODO: filters do not work with tags that have underscores
- filters = dict()
-
- vpc_subnet_id = module.params.get('vpc_subnet_id')
- vpc_id = None
- if vpc_subnet_id:
- filters.update({"subnet-id": vpc_subnet_id})
- if vpc:
- vpc_id = vpc.get_all_subnets(subnet_ids=[vpc_subnet_id])[0].vpc_id
-
- if vpc_id:
- filters.update({"vpc-id": vpc_id})
-
- if tags is not None:
-
- if isinstance(tags, str):
- try:
- tags = literal_eval(tags)
- except Exception:
- pass
-
- # if not a string type, convert and make sure it's a text string
- if isinstance(tags, int):
- tags = to_text(tags)
-
- # if string, we only care that a tag of that name exists
- if isinstance(tags, str):
- filters.update({"tag-key": tags})
-
- # if list, append each item to filters
- if isinstance(tags, list):
- for x in tags:
- if isinstance(x, dict):
- x = _set_none_to_blank(x)
- filters.update(dict(("tag:" + tn, tv) for (tn, tv) in x.items()))
- else:
- filters.update({"tag-key": x})
-
- # if dict, add the key and value to the filter
- if isinstance(tags, dict):
- tags = _set_none_to_blank(tags)
- filters.update(dict(("tag:" + tn, tv) for (tn, tv) in tags.items()))
-
- # lets check to see if the filters dict is empty, if so then stop
- if not filters:
- module.fail_json(msg="Filters based on tag is empty => tags: %s" % (tags))
-
- if state:
- # http://stackoverflow.com/questions/437511/what-are-the-valid-instancestates-for-the-amazon-ec2-api
- filters.update({'instance-state-name': state})
-
- if zone:
- filters.update({'availability-zone': zone})
-
- if module.params.get('id'):
- filters['client-token'] = module.params['id']
-
- results = ec2.get_all_instances(filters=filters)
-
- return results
-
-
-def get_instance_info(inst):
- """
- Retrieves instance information from an instance
- ID and returns it as a dictionary
- """
- instance_info = {'id': inst.id,
- 'ami_launch_index': inst.ami_launch_index,
- 'private_ip': inst.private_ip_address,
- 'private_dns_name': inst.private_dns_name,
- 'public_ip': inst.ip_address,
- 'dns_name': inst.dns_name,
- 'public_dns_name': inst.public_dns_name,
- 'state_code': inst.state_code,
- 'architecture': inst.architecture,
- 'image_id': inst.image_id,
- 'key_name': inst.key_name,
- 'placement': inst.placement,
- 'region': inst.placement[:-1],
- 'kernel': inst.kernel,
- 'ramdisk': inst.ramdisk,
- 'launch_time': inst.launch_time,
- 'instance_type': inst.instance_type,
- 'root_device_type': inst.root_device_type,
- 'root_device_name': inst.root_device_name,
- 'state': inst.state,
- 'hypervisor': inst.hypervisor,
- 'tags': inst.tags,
- 'groups': dict((group.id, group.name) for group in inst.groups),
- }
- try:
- instance_info['virtualization_type'] = getattr(inst, 'virtualization_type')
- except AttributeError:
- instance_info['virtualization_type'] = None
-
- try:
- instance_info['ebs_optimized'] = getattr(inst, 'ebs_optimized')
- except AttributeError:
- instance_info['ebs_optimized'] = False
-
- try:
- bdm_dict = {}
- bdm = getattr(inst, 'block_device_mapping')
- for device_name in bdm.keys():
- bdm_dict[device_name] = {
- 'status': bdm[device_name].status,
- 'volume_id': bdm[device_name].volume_id,
- 'delete_on_termination': bdm[device_name].delete_on_termination
- }
- instance_info['block_device_mapping'] = bdm_dict
- except AttributeError:
- instance_info['block_device_mapping'] = False
-
- try:
- instance_info['tenancy'] = getattr(inst, 'placement_tenancy')
- except AttributeError:
- instance_info['tenancy'] = 'default'
-
- return instance_info
-
-
-def boto_supports_associate_public_ip_address(ec2):
- """
- Check if Boto library has associate_public_ip_address in the NetworkInterfaceSpecification
- class. Added in Boto 2.13.0
-
- ec2: authenticated ec2 connection object
-
- Returns:
- True if Boto library accepts associate_public_ip_address argument, else false
- """
-
- try:
- network_interface = boto.ec2.networkinterface.NetworkInterfaceSpecification()
- getattr(network_interface, "associate_public_ip_address")
- return True
- except AttributeError:
- return False
-
-
-def boto_supports_profile_name_arg(ec2):
- """
- Check if Boto library has instance_profile_name argument. instance_profile_name has been added in Boto 2.5.0
-
- ec2: authenticated ec2 connection object
-
- Returns:
- True if Boto library accept instance_profile_name argument, else false
- """
- run_instances_method = getattr(ec2, 'run_instances')
- return 'instance_profile_name' in get_function_code(run_instances_method).co_varnames
-
-
-def boto_supports_volume_encryption():
- """
- Check if Boto library supports encryption of EBS volumes (added in 2.29.0)
-
- Returns:
- True if boto library has the named param as an argument on the request_spot_instances method, else False
- """
- return hasattr(boto, 'Version') and LooseVersion(boto.Version) >= LooseVersion('2.29.0')
-
-
-def create_block_device(module, ec2, volume):
- # Not aware of a way to determine this programatically
- # http://aws.amazon.com/about-aws/whats-new/2013/10/09/ebs-provisioned-iops-maximum-iops-gb-ratio-increased-to-30-1/
- MAX_IOPS_TO_SIZE_RATIO = 30
-
- volume_type = volume.get('volume_type')
-
- if 'snapshot' not in volume and 'ephemeral' not in volume:
- if 'volume_size' not in volume:
- module.fail_json(msg='Size must be specified when creating a new volume or modifying the root volume')
- if 'snapshot' in volume:
- if volume_type == 'io1' and 'iops' not in volume:
- module.fail_json(msg='io1 volumes must have an iops value set')
- if 'iops' in volume:
- snapshot = ec2.get_all_snapshots(snapshot_ids=[volume['snapshot']])[0]
- size = volume.get('volume_size', snapshot.volume_size)
- if int(volume['iops']) > MAX_IOPS_TO_SIZE_RATIO * int(size):
- module.fail_json(msg='IOPS must be at most %d times greater than size' % MAX_IOPS_TO_SIZE_RATIO)
- if 'ephemeral' in volume:
- if 'snapshot' in volume:
- module.fail_json(msg='Cannot set both ephemeral and snapshot')
- if boto_supports_volume_encryption():
- return BlockDeviceType(snapshot_id=volume.get('snapshot'),
- ephemeral_name=volume.get('ephemeral'),
- size=volume.get('volume_size'),
- volume_type=volume_type,
- delete_on_termination=volume.get('delete_on_termination', False),
- iops=volume.get('iops'),
- encrypted=volume.get('encrypted', None))
- else:
- return BlockDeviceType(snapshot_id=volume.get('snapshot'),
- ephemeral_name=volume.get('ephemeral'),
- size=volume.get('volume_size'),
- volume_type=volume_type,
- delete_on_termination=volume.get('delete_on_termination', False),
- iops=volume.get('iops'))
-
-
-def boto_supports_param_in_spot_request(ec2, param):
- """
- Check if Boto library has a in its request_spot_instances() method. For example, the placement_group parameter wasn't added until 2.3.0.
-
- ec2: authenticated ec2 connection object
-
- Returns:
- True if boto library has the named param as an argument on the request_spot_instances method, else False
- """
- method = getattr(ec2, 'request_spot_instances')
- return param in get_function_code(method).co_varnames
-
-
-def await_spot_requests(module, ec2, spot_requests, count):
- """
- Wait for a group of spot requests to be fulfilled, or fail.
-
- module: Ansible module object
- ec2: authenticated ec2 connection object
- spot_requests: boto.ec2.spotinstancerequest.SpotInstanceRequest object returned by ec2.request_spot_instances
- count: Total number of instances to be created by the spot requests
-
- Returns:
- list of instance ID's created by the spot request(s)
- """
- spot_wait_timeout = int(module.params.get('spot_wait_timeout'))
- wait_complete = time.time() + spot_wait_timeout
-
- spot_req_inst_ids = dict()
- while time.time() < wait_complete:
- reqs = ec2.get_all_spot_instance_requests()
- for sirb in spot_requests:
- if sirb.id in spot_req_inst_ids:
- continue
- for sir in reqs:
- if sir.id != sirb.id:
- continue # this is not our spot instance
- if sir.instance_id is not None:
- spot_req_inst_ids[sirb.id] = sir.instance_id
- elif sir.state == 'open':
- continue # still waiting, nothing to do here
- elif sir.state == 'active':
- continue # Instance is created already, nothing to do here
- elif sir.state == 'failed':
- module.fail_json(msg="Spot instance request %s failed with status %s and fault %s:%s" % (
- sir.id, sir.status.code, sir.fault.code, sir.fault.message))
- elif sir.state == 'cancelled':
- module.fail_json(msg="Spot instance request %s was cancelled before it could be fulfilled." % sir.id)
- elif sir.state == 'closed':
- # instance is terminating or marked for termination
- # this may be intentional on the part of the operator,
- # or it may have been terminated by AWS due to capacity,
- # price, or group constraints in this case, we'll fail
- # the module if the reason for the state is anything
- # other than termination by user. Codes are documented at
- # https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-bid-status.html
- if sir.status.code == 'instance-terminated-by-user':
- # do nothing, since the user likely did this on purpose
- pass
- else:
- spot_msg = "Spot instance request %s was closed by AWS with the status %s and fault %s:%s"
- module.fail_json(msg=spot_msg % (sir.id, sir.status.code, sir.fault.code, sir.fault.message))
-
- if len(spot_req_inst_ids) < count:
- time.sleep(5)
- else:
- return list(spot_req_inst_ids.values())
- module.fail_json(msg="wait for spot requests timeout on %s" % time.asctime())
-
-
-def enforce_count(module, ec2, vpc):
-
- exact_count = module.params.get('exact_count')
- count_tag = module.params.get('count_tag')
- zone = module.params.get('zone')
-
- # fail here if the exact count was specified without filtering
- # on a tag, as this may lead to a undesired removal of instances
- if exact_count and count_tag is None:
- module.fail_json(msg="you must use the 'count_tag' option with exact_count")
-
- reservations, instances = find_running_instances_by_count_tag(module, ec2, vpc, count_tag, zone)
-
- changed = None
- checkmode = False
- instance_dict_array = []
- changed_instance_ids = None
-
- if len(instances) == exact_count:
- changed = False
- elif len(instances) < exact_count:
- changed = True
- to_create = exact_count - len(instances)
- if not checkmode:
- (instance_dict_array, changed_instance_ids, changed) \
- = create_instances(module, ec2, vpc, override_count=to_create)
-
- for inst in instance_dict_array:
- instances.append(inst)
- elif len(instances) > exact_count:
- changed = True
- to_remove = len(instances) - exact_count
- if not checkmode:
- all_instance_ids = sorted([x.id for x in instances])
- remove_ids = all_instance_ids[0:to_remove]
-
- instances = [x for x in instances if x.id not in remove_ids]
-
- (changed, instance_dict_array, changed_instance_ids) \
- = terminate_instances(module, ec2, remove_ids)
- terminated_list = []
- for inst in instance_dict_array:
- inst['state'] = "terminated"
- terminated_list.append(inst)
- instance_dict_array = terminated_list
-
- # ensure all instances are dictionaries
- all_instances = []
- for inst in instances:
-
- if not isinstance(inst, dict):
- warn_if_public_ip_assignment_changed(module, inst)
- inst = get_instance_info(inst)
- all_instances.append(inst)
-
- return (all_instances, instance_dict_array, changed_instance_ids, changed)
-
-
-def create_instances(module, ec2, vpc, override_count=None):
- """
- Creates new instances
-
- module : AnsibleAWSModule object
- ec2: authenticated ec2 connection object
-
- Returns:
- A list of dictionaries with instance information
- about the instances that were launched
- """
-
- key_name = module.params.get('key_name')
- id = module.params.get('id')
- group_name = module.params.get('group')
- group_id = module.params.get('group_id')
- zone = module.params.get('zone')
- instance_type = module.params.get('instance_type')
- tenancy = module.params.get('tenancy')
- spot_price = module.params.get('spot_price')
- spot_type = module.params.get('spot_type')
- image = module.params.get('image')
- if override_count:
- count = override_count
- else:
- count = module.params.get('count')
- monitoring = module.params.get('monitoring')
- kernel = module.params.get('kernel')
- ramdisk = module.params.get('ramdisk')
- wait = module.params.get('wait')
- wait_timeout = int(module.params.get('wait_timeout'))
- spot_wait_timeout = int(module.params.get('spot_wait_timeout'))
- placement_group = module.params.get('placement_group')
- user_data = module.params.get('user_data')
- instance_tags = module.params.get('instance_tags')
- vpc_subnet_id = module.params.get('vpc_subnet_id')
- assign_public_ip = module.boolean(module.params.get('assign_public_ip'))
- private_ip = module.params.get('private_ip')
- instance_profile_name = module.params.get('instance_profile_name')
- volumes = module.params.get('volumes')
- ebs_optimized = module.params.get('ebs_optimized')
- exact_count = module.params.get('exact_count')
- count_tag = module.params.get('count_tag')
- source_dest_check = module.boolean(module.params.get('source_dest_check'))
- termination_protection = module.boolean(module.params.get('termination_protection'))
- network_interfaces = module.params.get('network_interfaces')
- spot_launch_group = module.params.get('spot_launch_group')
- instance_initiated_shutdown_behavior = module.params.get('instance_initiated_shutdown_behavior')
-
- vpc_id = None
- if vpc_subnet_id:
- if not vpc:
- module.fail_json(msg="region must be specified")
- else:
- vpc_id = vpc.get_all_subnets(subnet_ids=[vpc_subnet_id])[0].vpc_id
- else:
- vpc_id = None
-
- try:
- # Here we try to lookup the group id from the security group name - if group is set.
- if group_name:
- if vpc_id:
- grp_details = ec2.get_all_security_groups(filters={'vpc_id': vpc_id})
- else:
- grp_details = ec2.get_all_security_groups()
- if isinstance(group_name, string_types):
- group_name = [group_name]
- unmatched = set(group_name).difference(str(grp.name) for grp in grp_details)
- if len(unmatched) > 0:
- module.fail_json(msg="The following group names are not valid: %s" % ', '.join(unmatched))
- group_id = [str(grp.id) for grp in grp_details if str(grp.name) in group_name]
- # Now we try to lookup the group id testing if group exists.
- elif group_id:
- # wrap the group_id in a list if it's not one already
- if isinstance(group_id, string_types):
- group_id = [group_id]
- grp_details = ec2.get_all_security_groups(group_ids=group_id)
- group_name = [grp_item.name for grp_item in grp_details]
- except boto.exception.NoAuthHandlerFound as e:
- module.fail_json_aws(e, msg='Unable to authenticate to AWS')
-
- # Lookup any instances that much our run id.
-
- running_instances = []
- count_remaining = int(count)
-
- if id is not None:
- filter_dict = {'client-token': id, 'instance-state-name': 'running'}
- previous_reservations = ec2.get_all_instances(None, filter_dict)
- for res in previous_reservations:
- for prev_instance in res.instances:
- running_instances.append(prev_instance)
- count_remaining = count_remaining - len(running_instances)
-
- # Both min_count and max_count equal count parameter. This means the launch request is explicit (we want count, or fail) in how many instances we want.
-
- if count_remaining == 0:
- changed = False
- else:
- changed = True
- try:
- params = {'image_id': image,
- 'key_name': key_name,
- 'monitoring_enabled': monitoring,
- 'placement': zone,
- 'instance_type': instance_type,
- 'kernel_id': kernel,
- 'ramdisk_id': ramdisk}
- if user_data is not None:
- params['user_data'] = to_bytes(user_data, errors='surrogate_or_strict')
-
- if ebs_optimized:
- params['ebs_optimized'] = ebs_optimized
-
- # 'tenancy' always has a default value, but it is not a valid parameter for spot instance request
- if not spot_price:
- params['tenancy'] = tenancy
-
- if boto_supports_profile_name_arg(ec2):
- params['instance_profile_name'] = instance_profile_name
- else:
- if instance_profile_name is not None:
- module.fail_json(
- msg="instance_profile_name parameter requires Boto version 2.5.0 or higher")
-
- if assign_public_ip is not None:
- if not boto_supports_associate_public_ip_address(ec2):
- module.fail_json(
- msg="assign_public_ip parameter requires Boto version 2.13.0 or higher.")
- elif not vpc_subnet_id:
- module.fail_json(
- msg="assign_public_ip only available with vpc_subnet_id")
-
- else:
- if private_ip:
- interface = boto.ec2.networkinterface.NetworkInterfaceSpecification(
- subnet_id=vpc_subnet_id,
- private_ip_address=private_ip,
- groups=group_id,
- associate_public_ip_address=assign_public_ip)
- else:
- interface = boto.ec2.networkinterface.NetworkInterfaceSpecification(
- subnet_id=vpc_subnet_id,
- groups=group_id,
- associate_public_ip_address=assign_public_ip)
- interfaces = boto.ec2.networkinterface.NetworkInterfaceCollection(interface)
- params['network_interfaces'] = interfaces
- else:
- if network_interfaces:
- if isinstance(network_interfaces, string_types):
- network_interfaces = [network_interfaces]
- interfaces = []
- for i, network_interface_id in enumerate(network_interfaces):
- interface = boto.ec2.networkinterface.NetworkInterfaceSpecification(
- network_interface_id=network_interface_id,
- device_index=i)
- interfaces.append(interface)
- params['network_interfaces'] = \
- boto.ec2.networkinterface.NetworkInterfaceCollection(*interfaces)
- else:
- params['subnet_id'] = vpc_subnet_id
- if vpc_subnet_id:
- params['security_group_ids'] = group_id
- else:
- params['security_groups'] = group_name
-
- if volumes:
- bdm = BlockDeviceMapping()
- for volume in volumes:
- if 'device_name' not in volume:
- module.fail_json(msg='Device name must be set for volume')
- # Minimum volume size is 1GiB. We'll use volume size explicitly set to 0
- # to be a signal not to create this volume
- if 'volume_size' not in volume or int(volume['volume_size']) > 0:
- bdm[volume['device_name']] = create_block_device(module, ec2, volume)
-
- params['block_device_map'] = bdm
-
- # check to see if we're using spot pricing first before starting instances
- if not spot_price:
- if assign_public_ip is not None and private_ip:
- params.update(
- dict(
- min_count=count_remaining,
- max_count=count_remaining,
- client_token=id,
- placement_group=placement_group,
- )
- )
- else:
- params.update(
- dict(
- min_count=count_remaining,
- max_count=count_remaining,
- client_token=id,
- placement_group=placement_group,
- private_ip_address=private_ip,
- )
- )
-
- # For ordinary (not spot) instances, we can select 'stop'
- # (the default) or 'terminate' here.
- params['instance_initiated_shutdown_behavior'] = instance_initiated_shutdown_behavior or 'stop'
-
- try:
- res = ec2.run_instances(**params)
- except boto.exception.EC2ResponseError as e:
- if (params['instance_initiated_shutdown_behavior'] != 'terminate' and
- "InvalidParameterCombination" == e.error_code):
- params['instance_initiated_shutdown_behavior'] = 'terminate'
- res = ec2.run_instances(**params)
- else:
- raise
-
- instids = [i.id for i in res.instances]
- while True:
- try:
- ec2.get_all_instances(instids)
- break
- except boto.exception.EC2ResponseError as e:
- if e.error_code == 'InvalidInstanceID.NotFound':
- # there's a race between start and get an instance
- continue
- else:
- module.fail_json_aws(e)
-
- # The instances returned through ec2.run_instances above can be in
- # terminated state due to idempotency. See commit 7f11c3d for a complete
- # explanation.
- terminated_instances = [
- str(instance.id) for instance in res.instances if instance.state == 'terminated'
- ]
- if terminated_instances:
- module.fail_json(msg="Instances with id(s) %s " % terminated_instances +
- "were created previously but have since been terminated - " +
- "use a (possibly different) 'instanceid' parameter")
-
- else:
- if private_ip:
- module.fail_json(
- msg='private_ip only available with on-demand (non-spot) instances')
- if boto_supports_param_in_spot_request(ec2, 'placement_group'):
- params['placement_group'] = placement_group
- elif placement_group:
- module.fail_json(
- msg="placement_group parameter requires Boto version 2.3.0 or higher.")
-
- # You can't tell spot instances to 'stop'; they will always be
- # 'terminate'd. For convenience, we'll ignore the latter value.
- if instance_initiated_shutdown_behavior and instance_initiated_shutdown_behavior != 'terminate':
- module.fail_json(
- msg="instance_initiated_shutdown_behavior=stop is not supported for spot instances.")
-
- if spot_launch_group and isinstance(spot_launch_group, string_types):
- params['launch_group'] = spot_launch_group
-
- params.update(dict(
- count=count_remaining,
- type=spot_type,
- ))
-
- # Set spot ValidUntil
- # ValidUntil -> (timestamp). The end date of the request, in
- # UTC format (for example, YYYY -MM -DD T*HH* :MM :SS Z).
- utc_valid_until = (
- datetime.datetime.utcnow()
- + datetime.timedelta(seconds=spot_wait_timeout))
- params['valid_until'] = utc_valid_until.strftime('%Y-%m-%dT%H:%M:%S.000Z')
-
- res = ec2.request_spot_instances(spot_price, **params)
-
- # Now we have to do the intermediate waiting
- if wait:
- instids = await_spot_requests(module, ec2, res, count)
- else:
- instids = []
- except boto.exception.BotoServerError as e:
- module.fail_json_aws(e, msg='Instance creation failed')
-
- # wait here until the instances are up
- num_running = 0
- wait_timeout = time.time() + wait_timeout
- res_list = ()
- while wait_timeout > time.time() and num_running < len(instids):
- try:
- res_list = ec2.get_all_instances(instids)
- except boto.exception.BotoServerError as e:
- if e.error_code == 'InvalidInstanceID.NotFound':
- time.sleep(1)
- continue
- else:
- raise
-
- num_running = 0
- for res in res_list:
- num_running += len([i for i in res.instances if i.state == 'running'])
- if len(res_list) <= 0:
- # got a bad response of some sort, possibly due to
- # stale/cached data. Wait a second and then try again
- time.sleep(1)
- continue
- if wait and num_running < len(instids):
- time.sleep(5)
- else:
- break
-
- if wait and wait_timeout <= time.time():
- # waiting took too long
- module.fail_json(msg="wait for instances running timeout on %s" % time.asctime())
-
- # We do this after the loop ends so that we end up with one list
- for res in res_list:
- running_instances.extend(res.instances)
-
- # Enabled by default by AWS
- if source_dest_check is False:
- for inst in res.instances:
- inst.modify_attribute('sourceDestCheck', False)
-
- # Disabled by default by AWS
- if termination_protection is True:
- for inst in res.instances:
- inst.modify_attribute('disableApiTermination', True)
-
- # Leave this as late as possible to try and avoid InvalidInstanceID.NotFound
- if instance_tags and instids:
- try:
- ec2.create_tags(instids, instance_tags)
- except boto.exception.EC2ResponseError as e:
- module.fail_json_aws(e, msg='Instance tagging failed')
-
- instance_dict_array = []
- created_instance_ids = []
- for inst in running_instances:
- inst.update()
- d = get_instance_info(inst)
- created_instance_ids.append(inst.id)
- instance_dict_array.append(d)
-
- return (instance_dict_array, created_instance_ids, changed)
-
-
-def terminate_instances(module, ec2, instance_ids):
- """
- Terminates a list of instances
-
- module: Ansible module object
- ec2: authenticated ec2 connection object
- termination_list: a list of instances to terminate in the form of
- [ {id: }, ..]
-
- Returns a dictionary of instance information
- about the instances terminated.
-
- If the instance to be terminated is running
- "changed" will be set to False.
-
- """
-
- # Whether to wait for termination to complete before returning
- wait = module.params.get('wait')
- wait_timeout = int(module.params.get('wait_timeout'))
-
- changed = False
- instance_dict_array = []
-
- if not isinstance(instance_ids, list) or len(instance_ids) < 1:
- module.fail_json(msg='instance_ids should be a list of instances, aborting')
-
- terminated_instance_ids = []
- for res in ec2.get_all_instances(instance_ids):
- for inst in res.instances:
- if inst.state == 'running' or inst.state == 'stopped':
- terminated_instance_ids.append(inst.id)
- instance_dict_array.append(get_instance_info(inst))
- try:
- ec2.terminate_instances([inst.id])
- except EC2ResponseError as e:
- module.fail_json_aws(e, msg='Unable to terminate instance {0}'.format(inst.id))
- changed = True
-
- # wait here until the instances are 'terminated'
- if wait:
- num_terminated = 0
- wait_timeout = time.time() + wait_timeout
- while wait_timeout > time.time() and num_terminated < len(terminated_instance_ids):
- response = ec2.get_all_instances(instance_ids=terminated_instance_ids,
- filters={'instance-state-name': 'terminated'})
- try:
- num_terminated = sum([len(res.instances) for res in response])
- except Exception as e:
- # got a bad response of some sort, possibly due to
- # stale/cached data. Wait a second and then try again
- time.sleep(1)
- continue
-
- if num_terminated < len(terminated_instance_ids):
- time.sleep(5)
-
- # waiting took too long
- if wait_timeout < time.time() and num_terminated < len(terminated_instance_ids):
- module.fail_json(msg="wait for instance termination timeout on %s" % time.asctime())
- # Lets get the current state of the instances after terminating - issue600
- instance_dict_array = []
- for res in ec2.get_all_instances(instance_ids=terminated_instance_ids, filters={'instance-state-name': 'terminated'}):
- for inst in res.instances:
- instance_dict_array.append(get_instance_info(inst))
-
- return (changed, instance_dict_array, terminated_instance_ids)
-
-
-def startstop_instances(module, ec2, instance_ids, state, instance_tags):
- """
- Starts or stops a list of existing instances
-
- module: Ansible module object
- ec2: authenticated ec2 connection object
- instance_ids: The list of instances to start in the form of
- [ {id: }, ..]
- instance_tags: A dict of tag keys and values in the form of
- {key: value, ... }
- state: Intended state ("running" or "stopped")
-
- Returns a dictionary of instance information
- about the instances started/stopped.
-
- If the instance was not able to change state,
- "changed" will be set to False.
-
- Note that if instance_ids and instance_tags are both non-empty,
- this method will process the intersection of the two
- """
-
- wait = module.params.get('wait')
- wait_timeout = int(module.params.get('wait_timeout'))
- group_id = module.params.get('group_id')
- group_name = module.params.get('group')
- changed = False
- instance_dict_array = []
-
- if not isinstance(instance_ids, list) or len(instance_ids) < 1:
- # Fail unless the user defined instance tags
- if not instance_tags:
- module.fail_json(msg='instance_ids should be a list of instances, aborting')
-
- # To make an EC2 tag filter, we need to prepend 'tag:' to each key.
- # An empty filter does no filtering, so it's safe to pass it to the
- # get_all_instances method even if the user did not specify instance_tags
- filters = {}
- if instance_tags:
- for key, value in instance_tags.items():
- filters["tag:" + key] = value
-
- filters['instance-state-name'] = ["pending", "running", "stopping", "stopped"]
-
- if module.params.get('id'):
- filters['client-token'] = module.params['id']
- # Check that our instances are not in the state we want to take
-
- # Check (and eventually change) instances attributes and instances state
- existing_instances_array = []
- for res in ec2.get_all_instances(instance_ids, filters=filters):
- for inst in res.instances:
-
- warn_if_public_ip_assignment_changed(module, inst)
-
- changed = (check_source_dest_attr(module, inst, ec2) or
- check_termination_protection(module, inst) or changed)
-
- # Check security groups and if we're using ec2-vpc; ec2-classic security groups may not be modified
- if inst.vpc_id and group_name:
- grp_details = ec2.get_all_security_groups(filters={'vpc_id': inst.vpc_id})
- if isinstance(group_name, string_types):
- group_name = [group_name]
- unmatched = set(group_name) - set(to_text(grp.name) for grp in grp_details)
- if unmatched:
- module.fail_json(msg="The following group names are not valid: %s" % ', '.join(unmatched))
- group_ids = [to_text(grp.id) for grp in grp_details if to_text(grp.name) in group_name]
- elif inst.vpc_id and group_id:
- if isinstance(group_id, string_types):
- group_id = [group_id]
- grp_details = ec2.get_all_security_groups(group_ids=group_id)
- group_ids = [grp_item.id for grp_item in grp_details]
- if inst.vpc_id and (group_name or group_id):
- if set(sg.id for sg in inst.groups) != set(group_ids):
- changed = inst.modify_attribute('groupSet', group_ids)
-
- # Check instance state
- if inst.state != state:
- instance_dict_array.append(get_instance_info(inst))
- try:
- if state == 'running':
- inst.start()
- else:
- inst.stop()
- except EC2ResponseError as e:
- module.fail_json_aws(e, 'Unable to change state for instance {0}'.format(inst.id))
- changed = True
- existing_instances_array.append(inst.id)
-
- instance_ids = list(set(existing_instances_array + (instance_ids or [])))
- # Wait for all the instances to finish starting or stopping
- wait_timeout = time.time() + wait_timeout
- while wait and wait_timeout > time.time():
- instance_dict_array = []
- matched_instances = []
- for res in ec2.get_all_instances(instance_ids):
- for i in res.instances:
- if i.state == state:
- instance_dict_array.append(get_instance_info(i))
- matched_instances.append(i)
- if len(matched_instances) < len(instance_ids):
- time.sleep(5)
- else:
- break
-
- if wait and wait_timeout <= time.time():
- # waiting took too long
- module.fail_json(msg="wait for instances running timeout on %s" % time.asctime())
-
- return (changed, instance_dict_array, instance_ids)
-
-
-def restart_instances(module, ec2, instance_ids, state, instance_tags):
- """
- Restarts a list of existing instances
-
- module: Ansible module object
- ec2: authenticated ec2 connection object
- instance_ids: The list of instances to start in the form of
- [ {id: }, ..]
- instance_tags: A dict of tag keys and values in the form of
- {key: value, ... }
- state: Intended state ("restarted")
-
- Returns a dictionary of instance information
- about the instances.
-
- If the instance was not able to change state,
- "changed" will be set to False.
-
- Wait will not apply here as this is a OS level operation.
-
- Note that if instance_ids and instance_tags are both non-empty,
- this method will process the intersection of the two.
- """
-
- changed = False
- instance_dict_array = []
-
- if not isinstance(instance_ids, list) or len(instance_ids) < 1:
- # Fail unless the user defined instance tags
- if not instance_tags:
- module.fail_json(msg='instance_ids should be a list of instances, aborting')
-
- # To make an EC2 tag filter, we need to prepend 'tag:' to each key.
- # An empty filter does no filtering, so it's safe to pass it to the
- # get_all_instances method even if the user did not specify instance_tags
- filters = {}
- if instance_tags:
- for key, value in instance_tags.items():
- filters["tag:" + key] = value
- if module.params.get('id'):
- filters['client-token'] = module.params['id']
-
- # Check that our instances are not in the state we want to take
-
- # Check (and eventually change) instances attributes and instances state
- for res in ec2.get_all_instances(instance_ids, filters=filters):
- for inst in res.instances:
-
- warn_if_public_ip_assignment_changed(module, inst)
-
- changed = (check_source_dest_attr(module, inst, ec2) or
- check_termination_protection(module, inst) or changed)
-
- # Check instance state
- if inst.state != state:
- instance_dict_array.append(get_instance_info(inst))
- try:
- inst.reboot()
- except EC2ResponseError as e:
- module.fail_json_aws(e, msg='Unable to change state for instance {0}'.format(inst.id))
- changed = True
-
- return (changed, instance_dict_array, instance_ids)
-
-
-def check_termination_protection(module, inst):
- """
- Check the instance disableApiTermination attribute.
-
- module: Ansible module object
- inst: EC2 instance object
-
- returns: True if state changed None otherwise
- """
-
- termination_protection = module.params.get('termination_protection')
-
- if (inst.get_attribute('disableApiTermination')['disableApiTermination'] != termination_protection and termination_protection is not None):
- inst.modify_attribute('disableApiTermination', termination_protection)
- return True
-
-
-def check_source_dest_attr(module, inst, ec2):
- """
- Check the instance sourceDestCheck attribute.
-
- module: Ansible module object
- inst: EC2 instance object
-
- returns: True if state changed None otherwise
- """
-
- source_dest_check = module.params.get('source_dest_check')
-
- if source_dest_check is not None:
- try:
- if inst.vpc_id is not None and inst.get_attribute('sourceDestCheck')['sourceDestCheck'] != source_dest_check:
- inst.modify_attribute('sourceDestCheck', source_dest_check)
- return True
- except boto.exception.EC2ResponseError as exc:
- # instances with more than one Elastic Network Interface will
- # fail, because they have the sourceDestCheck attribute defined
- # per-interface
- if exc.code == 'InvalidInstanceID':
- for interface in inst.interfaces:
- if interface.source_dest_check != source_dest_check:
- ec2.modify_network_interface_attribute(interface.id, "sourceDestCheck", source_dest_check)
- return True
- else:
- module.fail_json_aws(exc, msg='Failed to handle source_dest_check state for instance {0}'.format(inst.id))
-
-
-def warn_if_public_ip_assignment_changed(module, instance):
- # This is a non-modifiable attribute.
- assign_public_ip = module.params.get('assign_public_ip')
-
- # Check that public ip assignment is the same and warn if not
- public_dns_name = getattr(instance, 'public_dns_name', None)
- if (assign_public_ip or public_dns_name) and (not public_dns_name or assign_public_ip is False):
- module.warn("Unable to modify public ip assignment to {0} for instance {1}. "
- "Whether or not to assign a public IP is determined during instance creation.".format(assign_public_ip, instance.id))
-
-
-def main():
- argument_spec = dict(
- key_name=dict(aliases=['keypair']),
- id=dict(),
- group=dict(type='list', elements='str', aliases=['groups']),
- group_id=dict(type='list', elements='str'),
- zone=dict(aliases=['aws_zone', 'ec2_zone']),
- instance_type=dict(aliases=['type']),
- spot_price=dict(),
- spot_type=dict(default='one-time', choices=["one-time", "persistent"]),
- spot_launch_group=dict(),
- image=dict(),
- kernel=dict(),
- count=dict(type='int', default='1'),
- monitoring=dict(type='bool', default=False),
- ramdisk=dict(),
- wait=dict(type='bool', default=False),
- wait_timeout=dict(type='int', default=300),
- spot_wait_timeout=dict(type='int', default=600),
- placement_group=dict(),
- user_data=dict(),
- instance_tags=dict(type='dict'),
- vpc_subnet_id=dict(),
- assign_public_ip=dict(type='bool'),
- private_ip=dict(),
- instance_profile_name=dict(),
- instance_ids=dict(type='list', elements='str', aliases=['instance_id']),
- source_dest_check=dict(type='bool', default=None),
- termination_protection=dict(type='bool', default=None),
- state=dict(default='present', choices=['present', 'absent', 'running', 'restarted', 'stopped']),
- instance_initiated_shutdown_behavior=dict(default='stop', choices=['stop', 'terminate']),
- exact_count=dict(type='int', default=None),
- count_tag=dict(type='raw'),
- volumes=dict(type='list', elements='dict',),
- ebs_optimized=dict(type='bool', default=False),
- tenancy=dict(default='default', choices=['default', 'dedicated']),
- network_interfaces=dict(type='list', elements='str', aliases=['network_interface'])
- )
-
- module = AnsibleAWSModule(
- argument_spec=argument_spec,
- check_boto3=False,
- mutually_exclusive=[
- # Can be uncommented when we finish the deprecation cycle.
- # ['group', 'group_id'],
- ['exact_count', 'count'],
- ['exact_count', 'state'],
- ['exact_count', 'instance_ids'],
- ['network_interfaces', 'assign_public_ip'],
- ['network_interfaces', 'group'],
- ['network_interfaces', 'group_id'],
- ['network_interfaces', 'private_ip'],
- ['network_interfaces', 'vpc_subnet_id'],
- ],
- )
-
- module.deprecate("The 'ec2' module has been deprecated and replaced by the 'ec2_instance' module'",
- version='4.0.0', collection_name='amazon.aws')
-
- if module.params.get('group') and module.params.get('group_id'):
- module.deprecate(
- msg='Support for passing both group and group_id has been deprecated. '
- 'Currently group_id is ignored, in future passing both will result in an error',
- date='2022-06-01', collection_name='amazon.aws')
-
- if not HAS_BOTO:
- module.fail_json(msg='boto required for this module')
-
- try:
- region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module)
- if module.params.get('region') or not module.params.get('ec2_url'):
- ec2 = ec2_connect(module)
- elif module.params.get('ec2_url'):
- ec2 = connect_ec2_endpoint(ec2_url, **aws_connect_kwargs)
-
- if 'region' not in aws_connect_kwargs:
- aws_connect_kwargs['region'] = ec2.region
-
- vpc = connect_vpc(**aws_connect_kwargs)
- except boto.exception.NoAuthHandlerFound as e:
- module.fail_json_aws(e, msg='Failed to get connection')
-
- tagged_instances = []
-
- state = module.params['state']
-
- if state == 'absent':
- instance_ids = module.params['instance_ids']
- if not instance_ids:
- module.fail_json(msg='instance_ids list is required for absent state')
-
- (changed, instance_dict_array, new_instance_ids) = terminate_instances(module, ec2, instance_ids)
-
- elif state in ('running', 'stopped'):
- instance_ids = module.params.get('instance_ids')
- instance_tags = module.params.get('instance_tags')
- if not (isinstance(instance_ids, list) or isinstance(instance_tags, dict)):
- module.fail_json(msg='running list needs to be a list of instances or set of tags to run: %s' % instance_ids)
-
- (changed, instance_dict_array, new_instance_ids) = startstop_instances(module, ec2, instance_ids, state, instance_tags)
-
- elif state in ('restarted'):
- instance_ids = module.params.get('instance_ids')
- instance_tags = module.params.get('instance_tags')
- if not (isinstance(instance_ids, list) or isinstance(instance_tags, dict)):
- module.fail_json(msg='running list needs to be a list of instances or set of tags to run: %s' % instance_ids)
-
- (changed, instance_dict_array, new_instance_ids) = restart_instances(module, ec2, instance_ids, state, instance_tags)
-
- elif state == 'present':
- # Changed is always set to true when provisioning new instances
- if not module.params.get('image'):
- module.fail_json(msg='image parameter is required for new instance')
-
- if module.params.get('exact_count') is None:
- (instance_dict_array, new_instance_ids, changed) = create_instances(module, ec2, vpc)
- else:
- (tagged_instances, instance_dict_array, new_instance_ids, changed) = enforce_count(module, ec2, vpc)
-
- # Always return instances in the same order
- if new_instance_ids:
- new_instance_ids.sort()
- if instance_dict_array:
- instance_dict_array.sort(key=lambda x: x['id'])
- if tagged_instances:
- tagged_instances.sort(key=lambda x: x['id'])
-
- module.exit_json(changed=changed, instance_ids=new_instance_ids, instances=instance_dict_array, tagged_instances=tagged_instances)
-
-
-if __name__ == '__main__':
- main()
diff --git a/requirements.txt b/requirements.txt
index 1ff3ca2feb4..1a52353f680 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,5 +4,3 @@
# - tests/integration/targets/setup_botocore_pip
botocore>=1.19.0
boto3>=1.16.0
-# Final released version
-boto>=2.49.0
diff --git a/test-requirements.txt b/test-requirements.txt
index 77c76b86509..77a68ba03b5 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,6 +1,5 @@
botocore
boto3
-boto
coverage==4.5.4
placebo
diff --git a/tests/integration/requirements.txt b/tests/integration/requirements.txt
index 6e870975a35..49273391e3f 100644
--- a/tests/integration/requirements.txt
+++ b/tests/integration/requirements.txt
@@ -1,5 +1,4 @@
# Our code is based on the AWS SDKs
-boto
boto3
botocore
diff --git a/tests/integration/targets/ec2/aliases b/tests/integration/targets/ec2/aliases
deleted file mode 100644
index 4ef4b2067d0..00000000000
--- a/tests/integration/targets/ec2/aliases
+++ /dev/null
@@ -1 +0,0 @@
-cloud/aws
diff --git a/tests/integration/targets/ec2/defaults/main.yml b/tests/integration/targets/ec2/defaults/main.yml
deleted file mode 100644
index b9b4053818b..00000000000
--- a/tests/integration/targets/ec2/defaults/main.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-availability_zone: '{{ ec2_availability_zone_names[0] }}'
-
-vpc_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.0.0/16'
-subnet_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.1.0/24'
diff --git a/tests/integration/targets/ec2/meta/main.yml b/tests/integration/targets/ec2/meta/main.yml
deleted file mode 100644
index a0c556a5cb1..00000000000
--- a/tests/integration/targets/ec2/meta/main.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-dependencies:
- - prepare_tests
- - setup_ec2
- - setup_ec2_facts
diff --git a/tests/integration/targets/ec2/tasks/main.yml b/tests/integration/targets/ec2/tasks/main.yml
deleted file mode 100644
index 472289c530f..00000000000
--- a/tests/integration/targets/ec2/tasks/main.yml
+++ /dev/null
@@ -1,188 +0,0 @@
----
-- module_defaults:
- group/aws:
- aws_region: '{{ aws_region }}'
- aws_access_key: '{{ aws_access_key | default(omit) }}'
- aws_secret_key: '{{ aws_secret_key | default(omit) }}'
- security_token: '{{ security_token | default(omit) }}'
- collections:
- - community.aws
-
- block:
-
- # SETUP: vpc, ec2 key pair, subnet, security group, ec2 instance
-
- - name: create a VPC to work in
- ec2_vpc_net:
- cidr_block: '{{ vpc_cidr }}'
- state: present
- name: '{{ resource_prefix }}_setup'
- resource_tags:
- Name: '{{ resource_prefix }}_setup'
- register: setup_vpc
-
- - name: create a key pair to use for creating an ec2 instance
- ec2_key:
- name: '{{ resource_prefix }}_setup'
- state: present
- register: setup_key
-
- - name: create a subnet to use for creating an ec2 instance
- ec2_vpc_subnet:
- az: '{{ availability_zone }}'
- tags: '{{ resource_prefix }}_setup'
- vpc_id: '{{ setup_vpc.vpc.id }}'
- cidr: '{{ subnet_cidr }}'
- state: present
- resource_tags:
- Name: '{{ resource_prefix }}_setup'
- register: setup_subnet
-
- - name: create a security group to use for creating an ec2 instance
- ec2_group:
- name: '{{ resource_prefix }}_setup'
- description: 'created by Ansible integration tests'
- state: present
- vpc_id: '{{ setup_vpc.vpc.id }}'
- register: setup_sg
-
- # ============================================================
-
- - name: test first instance is started
- ec2:
- instance_type: t2.micro
- key_name: '{{ setup_key.key.name }}'
- state: present
- image: '{{ ec2_ami_id }}'
- wait: yes
- instance_tags:
- ResourcePrefix: '{{ resource_prefix }}-integration_tests'
- group_id: '{{ setup_sg.group_id }}'
- vpc_subnet_id: '{{ setup_subnet.subnet.id }}'
- register: test_instance_1
-
- - name: test second instance is started
- ec2:
- instance_type: t2.micro
- key_name: '{{ setup_key.key.name }}'
- state: present
- image: '{{ ec2_ami_id }}'
- wait: yes
- instance_tags:
- ResourcePrefix: '{{ resource_prefix }}-another_tag'
- group_id: '{{ setup_sg.group_id }}'
- vpc_subnet_id: '{{ setup_subnet.subnet.id }}'
- register: test_instance_2
-
- - name: assert instances started
- assert:
- that:
- - "test_instance_1.instances[0].state == 'running'"
- - "test_instance_2.instances[0].state == 'running'"
-
- - name: test first instance is terminated
- ec2:
- instance_ids: "{{ test_instance_1.instance_ids }}"
- state: absent
- wait: yes
- register: result
-
- - name: assert instance terminated
- assert:
- that:
- - "result.instances[0].state == 'terminated'"
-
- - name: test terminated instance is ignored when stopping
- ec2:
- instance_tags:
- ResourcePrefix: '{{ resource_prefix }}-integration_tests'
- state: stopped
- wait: yes
- register: result
-
- - name: assert resource not changed
- assert:
- that:
- - "result.changed == False"
-
- - name: test second instance not terminated
- ec2_instance_info:
- instance_ids: "{{ test_instance_2.instance_ids }}"
- register: result
-
- - name: assert second instance still running
- assert:
- that:
- - (result.instances|length) == 1
- - "result.instances[0].state.name == 'running'"
-
- - name: test second instance is stopped
- ec2:
- instance_ids: "{{ test_instance_2.instance_ids }}"
- state: stopped
- wait: yes
- register: result
-
- - name: assert second instance is stopped
- assert:
- that:
- - "result.instances[0].state == 'stopped'"
-
- # ========================================================
-
- always:
-
- # ============================================================
-
-
- # TEAR DOWN: ec2 instance, ec2 key pair, security group, vpc
- - name: Announce teardown start
- debug:
- msg: "***** TESTING COMPLETE. COMMENCE TEARDOWN *****"
-
- - name: get list of test instances
- ec2_instance_info:
- filters:
- "tag:ResourcePrefix": "{{ resource_prefix }}-*"
- register: test_instances
-
- - name: delete test instances
- ec2:
- instance_ids: "{{ test_instances.instances|map(attribute='instance_id') }}"
- state: absent
- wait: yes
- ignore_errors: yes
-
- - name: remove setup keypair
- ec2_key:
- name: '{{resource_prefix}}_setup'
- state: absent
- ignore_errors: yes
-
- - name: remove setup security group
- ec2_group:
- name: '{{ resource_prefix }}_setup'
- description: 'created by Ansible integration tests'
- state: absent
- vpc_id: '{{ setup_vpc.vpc.id }}'
- ignore_errors: yes
-
- - name: remove setup subnet
- ec2_vpc_subnet:
- az: '{{ availability_zone }}'
- tags: '{{resource_prefix}}_setup'
- vpc_id: '{{ setup_vpc.vpc.id }}'
- cidr: '{{ subnet_cidr }}'
- state: absent
- resource_tags:
- Name: '{{ resource_prefix }}_setup'
- ignore_errors: yes
-
- - name: remove setup VPC
- ec2_vpc_net:
- cidr_block: '{{ vpc_cidr }}'
- state: absent
- name: '{{ resource_prefix }}_setup'
- resource_tags:
- Name: '{{ resource_prefix }}_setup'
- ignore_errors: yes
diff --git a/tests/integration/targets/ec2_instance/main.yml b/tests/integration/targets/ec2_instance/main.yml
index 230a48c74e9..34163e478e5 100644
--- a/tests/integration/targets/ec2_instance/main.yml
+++ b/tests/integration/targets/ec2_instance/main.yml
@@ -35,6 +35,6 @@
- hosts: all
gather_facts: no
strategy: free
- serial: 5
+ serial: 6
roles:
- ec2_instance
diff --git a/tests/integration/targets/module_utils_ec2/aliases b/tests/integration/targets/module_utils_ec2/aliases
deleted file mode 100644
index 4ef4b2067d0..00000000000
--- a/tests/integration/targets/module_utils_ec2/aliases
+++ /dev/null
@@ -1 +0,0 @@
-cloud/aws
diff --git a/tests/integration/targets/module_utils_ec2/connect_to_aws.yml b/tests/integration/targets/module_utils_ec2/connect_to_aws.yml
deleted file mode 100644
index f0adfc72e59..00000000000
--- a/tests/integration/targets/module_utils_ec2/connect_to_aws.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-- hosts: all
- gather_facts: no
- collections:
- - community.aws
- - amazon.aws
- roles:
- # Test the behaviour of module_utils.connect_to_aws
- - 'connect_to_aws'
diff --git a/tests/integration/targets/module_utils_ec2/ec2_connect.yml b/tests/integration/targets/module_utils_ec2/ec2_connect.yml
deleted file mode 100644
index 75ecd297167..00000000000
--- a/tests/integration/targets/module_utils_ec2/ec2_connect.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-- hosts: all
- gather_facts: no
- collections:
- - community.aws
- - amazon.aws
- roles:
- # Test the behaviour of module_utils.ec2.ec2_connect
- - 'ec2_connect'
diff --git a/tests/integration/targets/module_utils_ec2/inventory b/tests/integration/targets/module_utils_ec2/inventory
deleted file mode 100644
index 5093e8582bc..00000000000
--- a/tests/integration/targets/module_utils_ec2/inventory
+++ /dev/null
@@ -1,6 +0,0 @@
-[tests]
-localhost
-
-[all:vars]
-ansible_connection=local
-ansible_python_interpreter="{{ ansible_playbook_python }}"
diff --git a/tests/integration/targets/module_utils_ec2/meta/main.yml b/tests/integration/targets/module_utils_ec2/meta/main.yml
deleted file mode 100644
index 1f64f1169a9..00000000000
--- a/tests/integration/targets/module_utils_ec2/meta/main.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-dependencies:
- - prepare_tests
- - setup_ec2
diff --git a/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/files/amazonroot.pem b/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/files/amazonroot.pem
deleted file mode 100644
index a6f3e92af58..00000000000
--- a/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/files/amazonroot.pem
+++ /dev/null
@@ -1,20 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF
-ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
-b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL
-MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
-b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj
-ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM
-9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw
-IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6
-VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L
-93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm
-jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
-AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA
-A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI
-U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs
-N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv
-o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU
-5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy
-rqXRfboQnoZsG4q5WTP468SQvvG5
------END CERTIFICATE-----
diff --git a/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/files/isrg-x1.pem b/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/files/isrg-x1.pem
deleted file mode 100644
index b85c8037f6b..00000000000
--- a/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/files/isrg-x1.pem
+++ /dev/null
@@ -1,31 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
-TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
-cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
-WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
-ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
-MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
-h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
-0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
-A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
-T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
-B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
-B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
-KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
-OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
-jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
-qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
-rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
-HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
-hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
-ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
-3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
-NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
-ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
-TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
-jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
-oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
-4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
-mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
-emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
------END CERTIFICATE-----
diff --git a/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/library/example_module.py b/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/library/example_module.py
deleted file mode 100644
index 543776a048b..00000000000
--- a/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/library/example_module.py
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/usr/bin/python
-# Copyright (c) 2017 Ansible Project
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-# A bare-minimum Ansible Module based on AnsibleAWSModule used for testing some
-# of the core behaviour around AWS/Boto3 connection details
-
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-
-
-try:
- import boto.ec2
-except ImportError:
- pass
-
-from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule
-from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AnsibleAWSError
-from ansible_collections.amazon.aws.plugins.module_utils.ec2 import connect_to_aws
-from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info
-
-
-def main():
- module = AnsibleAWSModule(
- argument_spec={},
- supports_check_mode=True,
- check_boto3=False,
- )
-
- region, ec2_url, aws_connect_params = get_aws_connection_info(module)
- if not region:
- module.fail_json(msg="Fail JSON: No Region")
-
- try:
- client = connect_to_aws(boto.ec2, region, **aws_connect_params)
- except boto.exception.NoAuthHandlerFound as e:
- module.fail_json_aws(e, msg='No Authentication Handler Found')
- except AnsibleAWSError as e:
- module.fail_json_aws(e, msg='Fail JSON AWS')
-
- filters = {'name': 'amzn2-ami-hvm-2.0.202006*-x86_64-gp2'}
-
- try:
- images = client.get_all_images(image_ids=[], filters=filters, owners=['amazon'], executable_by=[])
- except (boto.exception.BotoServerError, AnsibleAWSError) as e:
- module.fail_json_aws(e, msg='Fail JSON AWS')
-
- images_out = []
- for image in images:
- images_out.append(image.id)
-
- # Return something, just because we can.
- module.exit_json(
- changed=False,
- images=images_out)
-
-
-if __name__ == '__main__':
- main()
diff --git a/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/meta/main.yml b/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/meta/main.yml
deleted file mode 100644
index 77589cc2b48..00000000000
--- a/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/meta/main.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-dependencies:
- - prepare_tests
- - setup_ec2
-collections:
- - amazon.aws
diff --git a/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/credentials.yml b/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/credentials.yml
deleted file mode 100644
index 573532bb7f1..00000000000
--- a/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/credentials.yml
+++ /dev/null
@@ -1,212 +0,0 @@
----
-##################################################################################
-# Tests using standard credential parameters
-
-- name: 'Test basic operation using simple credentials (simple-parameters)'
- example_module:
- region: '{{ aws_region }}'
- access_key: '{{ aws_access_key }}'
- secret_key: '{{ aws_secret_key }}'
- security_token: '{{ security_token }}'
- register: credential_result
-
-- assert:
- that:
- - credential_result is successful
-
-- name: 'Test basic operation using simple credentials (aws-parameters)'
- example_module:
- aws_region: '{{ aws_region }}'
- aws_access_key: '{{ aws_access_key }}'
- aws_secret_key: '{{ aws_secret_key }}'
- aws_security_token: '{{ security_token }}'
- register: credential_result
-
-- assert:
- that:
- - credential_result is successful
-
-- name: 'Test basic operation using simple credentials (ec2-parameters)'
- example_module:
- ec2_region: '{{ aws_region }}'
- ec2_access_key: '{{ aws_access_key }}'
- ec2_secret_key: '{{ aws_secret_key }}'
- access_token: '{{ security_token }}'
- register: credential_result
-
-- assert:
- that:
- - credential_result is successful
-
-##################################################################################
-# Tests using standard credentials from environment variables
-
-- name: 'Test basic operation using simple credentials (aws-environment)'
- example_module:
- environment:
- AWS_REGION: '{{ aws_region }}'
- AWS_ACCESS_KEY_ID: '{{ aws_access_key }}'
- AWS_SECRET_ACCESS_KEY: '{{ aws_secret_key }}'
- AWS_SECURITY_TOKEN: '{{ security_token }}'
- register: credential_result
-
-- assert:
- that:
- - credential_result is successful
-
-- name: 'Test basic operation using simple credentials (aws2-environment)'
- example_module:
- environment:
- AWS_DEFAULT_REGION: '{{ aws_region }}'
- AWS_ACCESS_KEY: '{{ aws_access_key }}'
- AWS_SECRET_KEY: '{{ aws_secret_key }}'
- AWS_SESSION_TOKEN: '{{ security_token }}'
- register: credential_result
-
-- assert:
- that:
- - credential_result is successful
-
-- name: 'Test basic operation using simple credentials (ec2-environment)'
- example_module:
- environment:
- EC2_REGION: '{{ aws_region }}'
- EC2_ACCESS_KEY: '{{ aws_access_key }}'
- EC2_SECRET_KEY: '{{ aws_secret_key }}'
- EC2_SECURITY_TOKEN: '{{ security_token }}'
- register: credential_result
-
-- assert:
- that:
- - credential_result is successful
-
-##################################################################################
-# Tests for missing parameters
-
-- name: 'Test with missing region'
- example_module:
- region: '{{ omit }}'
- access_key: '{{ aws_access_key }}'
- secret_key: '{{ aws_secret_key }}'
- security_token: '{{ security_token }}'
- register: missing_region
- ignore_errors: True
-
-- assert:
- that:
- - missing_region is failed
- - '"Fail JSON: No Region" in missing_region.msg'
-
-- name: 'Test with missing access key'
- example_module:
- region: '{{ aws_region }}'
- access_key: '{{ omit }}'
- secret_key: '{{ aws_secret_key }}'
- security_token: '{{ security_token }}'
- register: missing_access
- ignore_errors: True
-
-- assert:
- that:
- - missing_access is failed
- - '"No handler was ready to authenticate." in missing_access.msg'
- #- '"aws_access_key_id" in missing_access.msg'
-
-- name: 'Test with missing secret key'
- example_module:
- region: '{{ aws_region }}'
- access_key: '{{ aws_access_key }}'
- secret_key: '{{ omit }}'
- security_token: '{{ security_token }}'
- register: missing_secret
- ignore_errors: True
-
-- assert:
- that:
- - missing_secret is failed
- - '"No handler was ready to authenticate." in missing_secret.msg'
- #- '"aws_secret_access_key" in missing_secret.msg'
-
-- name: 'Test with missing security token'
- example_module:
- region: '{{ aws_region }}'
- access_key: '{{ aws_access_key }}'
- secret_key: '{{ aws_secret_key }}'
- security_token: '{{ omit }}'
- register: missing_token
- ignore_errors: True
-
-- assert:
- that:
- - missing_token is failed
- # Caught when we try to do something, and passed to fail_json_aws
- - '"Fail JSON AWS" in missing_token.msg'
- - '"AWS was not able to validate the provided access credentials" in missing_token.msg'
-
-
-##################################################################################
-# Tests for bad parameters
-
-- name: 'Test with bad region'
- example_module:
- region: 'junk-example'
- access_key: '{{ aws_access_key }}'
- secret_key: '{{ aws_secret_key }}'
- security_token: '{{ security_token }}'
- register: bad_region
- ignore_errors: True
-
-- assert:
- that:
- - bad_region is failed
- - '"msg" in bad_region'
- - '"does not seem to be available" in bad_region.msg'
- - '"If the region definitely exists, you may need to upgrade boto or extend with endpoints_path" in bad_region.msg'
-
-- name: 'Test with bad access key'
- example_module:
- region: '{{ aws_region }}'
- access_key: 'junk-example'
- secret_key: '{{ aws_secret_key }}'
- security_token: '{{ security_token }}'
- register: bad_access
- ignore_errors: True
-
-- assert:
- that:
- - bad_access is failed
- # Caught when we try to do something, and passed to fail_json_aws
- - '"Fail JSON AWS" in missing_token.msg'
- - '"AWS was not able to validate the provided access credentials" in missing_token.msg'
-
-- name: 'Test with bad secret key'
- example_module:
- region: '{{ aws_region }}'
- access_key: '{{ aws_access_key }}'
- secret_key: 'junk-example'
- security_token: '{{ security_token }}'
- register: bad_secret
- ignore_errors: True
-
-- assert:
- that:
- - bad_secret is failed
- # Caught when we try to do something, and passed to fail_json_aws
- - '"Fail JSON AWS" in missing_token.msg'
- - '"AWS was not able to validate the provided access credentials" in missing_token.msg'
-
-- name: 'Test with bad security token'
- example_module:
- region: '{{ aws_region }}'
- access_key: '{{ aws_access_key }}'
- secret_key: '{{ aws_secret_key }}'
- security_token: 'junk-example'
- register: bad_token
- ignore_errors: True
-
-- assert:
- that:
- - bad_token is failed
- # Caught when we try to do something, and passed to fail_json_aws
- - '"Fail JSON AWS" in missing_token.msg'
- - '"AWS was not able to validate the provided access credentials" in missing_token.msg'
diff --git a/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/endpoints.yml b/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/endpoints.yml
deleted file mode 100644
index a2531e9603c..00000000000
--- a/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/endpoints.yml
+++ /dev/null
@@ -1,105 +0,0 @@
----
-# Note: connect_to_aws currently *ignores* aws_endpoint_url
-#
-##################################################################################
-# Tests using Endpoints
-
-- name: 'Test basic operation using standard endpoint (aws-parameters)'
- example_module:
- region: '{{ aws_region }}'
- aws_endpoint_url: 'https://ec2.{{ aws_region }}.amazonaws.com'
- aws_access_key: '{{ aws_access_key }}'
- aws_secret_key: '{{ aws_secret_key }}'
- aws_security_token: '{{ security_token }}'
- register: standard_endpoint_result
-
-- name: 'Check that we connected to the standard endpoint'
- assert:
- that:
- - standard_endpoint_result is successful
- #- '"ec2:DescribeImages" in standard_endpoint_result.resource_actions'
-
-- name: 'Test basic operation using standard endpoint (aws-parameters)'
- example_module:
- region: '{{ aws_region }}'
- endpoint_url: 'https://ec2.us-east-1.amazonaws.com'
- aws_access_key: '{{ aws_access_key }}'
- aws_secret_key: '{{ aws_secret_key }}'
- aws_security_token: '{{ security_token }}'
- register: standard_endpoint_result
-
-- name: 'Check that we connected to the standard endpoint'
- assert:
- that:
- - standard_endpoint_result is successful
- #- '"ec2:DescribeImages" in standard_endpoint_result.resource_actions'
-
-- name: 'Test basic operation using standard endpoint (aws-parameters)'
- example_module:
- region: '{{ aws_region }}'
- ec2_url: 'https://ec2.us-east-1.amazonaws.com'
- aws_access_key: '{{ aws_access_key }}'
- aws_secret_key: '{{ aws_secret_key }}'
- aws_security_token: '{{ security_token }}'
- register: standard_endpoint_result
-
-- name: 'Check that we connected to the standard endpoint'
- assert:
- that:
- - standard_endpoint_result is successful
- #- '"ec2:DescribeImages" in standard_endpoint_result.resource_actions'
-
-##################################################################################
-# Tests using environment variables
-
-- name: 'Test basic operation using standard endpoint (aws-environment)'
- example_module:
- region: '{{ aws_region }}'
- aws_access_key: '{{ aws_access_key }}'
- aws_secret_key: '{{ aws_secret_key }}'
- aws_security_token: '{{ security_token }}'
- environment:
- AWS_URL: 'https://ec2.us-east-1.amazonaws.com'
- register: standard_endpoint_result
-
-- name: 'Check that we connected to the standard endpoint'
- assert:
- that:
- - standard_endpoint_result is successful
- #- '"ec2:DescribeImages" in standard_endpoint_result.resource_actions'
-
-- name: 'Test basic operation using standard endpoint (ec2-environment)'
- example_module:
- region: '{{ aws_region }}'
- aws_access_key: '{{ aws_access_key }}'
- aws_secret_key: '{{ aws_secret_key }}'
- aws_security_token: '{{ security_token }}'
- environment:
- EC2_URL: 'https://ec2.us-east-1.amazonaws.com'
- register: standard_endpoint_result
-
-- name: 'Check that we connected to the standard endpoint'
- assert:
- that:
- - standard_endpoint_result is successful
- #- '"ec2:DescribeImages" in standard_endpoint_result.resource_actions'
-
-##################################################################################
-# Tests using a bad endpoint URL
-# - This demonstrates that endpoint_url overrode region
-
-- name: 'Test with bad endpoint URL'
- example_module:
- region: '{{ aws_region }}'
- endpoint_url: 'https://junk.{{ aws_region }}.amazonaws.com'
- access_key: '{{ aws_access_key }}'
- secret_key: '{{ aws_secret_key }}'
- security_token: '{{ security_token }}'
- register: bad_endpoint
- ignore_errors: True
-
-- assert:
- that:
- # endpoint_url is ignored by connect_to_aws
- - bad_endpoint is successful
- #- bad_endpoint is failed
diff --git a/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/main.yml b/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/main.yml
deleted file mode 100644
index 9e81b30802e..00000000000
--- a/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/main.yml
+++ /dev/null
@@ -1,12 +0,0 @@
----
-- name: 'Tests around standard credentials'
- include_tasks: 'credentials.yml'
-
-- name: 'Tests around profiles'
- include_tasks: 'profiles.yml'
-
-- name: 'Tests around endpoints'
- include_tasks: 'endpoints.yml'
-
-#- name: 'Tests around CA Bundles'
-# include_tasks: 'boto2_ec2/ca_bundle.yml'
diff --git a/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/profiles.yml b/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/profiles.yml
deleted file mode 100644
index bab095e47ad..00000000000
--- a/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/profiles.yml
+++ /dev/null
@@ -1,63 +0,0 @@
----
-# Note: unlike boto3 modules, boto2 modules can't read region from the profile
-#
-##################################################################################
-# Tests using profiles instead of directly consuming credentials
-
-- name: 'Test basic operation using profile (simple-parameters)'
- example_module:
- region: '{{ aws_region }}'
- profile: 'test_profile'
- register: profile_result
-
-- assert:
- that:
- - profile_result is successful
-
-- name: 'Test basic operation using profile (aws-parameters)'
- example_module:
- region: '{{ aws_region }}'
- aws_profile: 'test_profile'
- register: profile_result
-
-- assert:
- that:
- - profile_result is successful
-
-- name: 'Test basic operation using profile (aws-environment)'
- example_module:
- region: '{{ aws_region }}'
- environment:
- AWS_PROFILE: 'test_profile'
- register: profile_result
-
-- assert:
- that:
- - profile_result is successful
-
-- name: 'Test basic operation using profile (aws2-environment)'
- example_module:
- region: '{{ aws_region }}'
- environment:
- AWS_DEFAULT_PROFILE: 'test_profile'
- register: profile_result
-
-- assert:
- that:
- - profile_result is successful
-
-##################################################################################
-# Tests with bad profile
-
-- name: 'Test with bad profile'
- example_module:
- region: '{{ aws_region }}'
- profile: 'junk-profile'
- register: bad_profile
- ignore_errors: True
-
-- assert:
- that:
- - bad_profile is failed
- - '"msg" in bad_profile'
- - '"Profile given for AWS was not found." in bad_profile.msg'
diff --git a/tests/integration/targets/module_utils_ec2/roles/ec2_connect/files/amazonroot.pem b/tests/integration/targets/module_utils_ec2/roles/ec2_connect/files/amazonroot.pem
deleted file mode 100644
index a6f3e92af58..00000000000
--- a/tests/integration/targets/module_utils_ec2/roles/ec2_connect/files/amazonroot.pem
+++ /dev/null
@@ -1,20 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF
-ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
-b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL
-MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
-b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj
-ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM
-9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw
-IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6
-VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L
-93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm
-jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
-AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA
-A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI
-U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs
-N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv
-o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU
-5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy
-rqXRfboQnoZsG4q5WTP468SQvvG5
------END CERTIFICATE-----
diff --git a/tests/integration/targets/module_utils_ec2/roles/ec2_connect/files/isrg-x1.pem b/tests/integration/targets/module_utils_ec2/roles/ec2_connect/files/isrg-x1.pem
deleted file mode 100644
index b85c8037f6b..00000000000
--- a/tests/integration/targets/module_utils_ec2/roles/ec2_connect/files/isrg-x1.pem
+++ /dev/null
@@ -1,31 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
-TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
-cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
-WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
-ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
-MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
-h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
-0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
-A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
-T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
-B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
-B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
-KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
-OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
-jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
-qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
-rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
-HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
-hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
-ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
-3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
-NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
-ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
-TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
-jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
-oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
-4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
-mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
-emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
------END CERTIFICATE-----
diff --git a/tests/integration/targets/module_utils_ec2/roles/ec2_connect/library/example_module.py b/tests/integration/targets/module_utils_ec2/roles/ec2_connect/library/example_module.py
deleted file mode 100644
index 6bbc1a4a08c..00000000000
--- a/tests/integration/targets/module_utils_ec2/roles/ec2_connect/library/example_module.py
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/usr/bin/python
-# Copyright (c) 2017 Ansible Project
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-# A bare-minimum Ansible Module based on AnsibleAWSModule used for testing some
-# of the core behaviour around AWS/Boto3 connection details
-
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-
-
-try:
- import boto.ec2
-except ImportError:
- pass
-
-from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule
-from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ec2_connect
-
-
-def main():
- module = AnsibleAWSModule(
- argument_spec={},
- supports_check_mode=True,
- check_boto3=False,
- )
-
- try:
- client = ec2_connect(module)
- except boto.exception.NoAuthHandlerFound as e:
- module.fail_json_aws(e, msg='Failed to get connection')
-
- filters = {'name': 'amzn2-ami-hvm-2.0.202006*-x86_64-gp2'}
-
- try:
- images = client.get_all_images(image_ids=[], filters=filters, owners=['amazon'], executable_by=[])
- except boto.exception.BotoServerError as e:
- module.fail_json_aws(e, msg='Fail JSON AWS')
-
- images_out = []
- for image in images:
- images_out.append(image.id)
-
- # Return something, just because we can.
- module.exit_json(
- changed=False,
- images=images_out)
-
-
-if __name__ == '__main__':
- main()
diff --git a/tests/integration/targets/module_utils_ec2/roles/ec2_connect/meta/main.yml b/tests/integration/targets/module_utils_ec2/roles/ec2_connect/meta/main.yml
deleted file mode 100644
index 77589cc2b48..00000000000
--- a/tests/integration/targets/module_utils_ec2/roles/ec2_connect/meta/main.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-dependencies:
- - prepare_tests
- - setup_ec2
-collections:
- - amazon.aws
diff --git a/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/credentials.yml b/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/credentials.yml
deleted file mode 100644
index 1843a497d18..00000000000
--- a/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/credentials.yml
+++ /dev/null
@@ -1,212 +0,0 @@
----
-##################################################################################
-# Tests using standard credential parameters
-
-- name: 'Test basic operation using simple credentials (simple-parameters)'
- example_module:
- region: '{{ aws_region }}'
- access_key: '{{ aws_access_key }}'
- secret_key: '{{ aws_secret_key }}'
- security_token: '{{ security_token }}'
- register: credential_result
-
-- assert:
- that:
- - credential_result is successful
-
-- name: 'Test basic operation using simple credentials (aws-parameters)'
- example_module:
- aws_region: '{{ aws_region }}'
- aws_access_key: '{{ aws_access_key }}'
- aws_secret_key: '{{ aws_secret_key }}'
- aws_security_token: '{{ security_token }}'
- register: credential_result
-
-- assert:
- that:
- - credential_result is successful
-
-- name: 'Test basic operation using simple credentials (ec2-parameters)'
- example_module:
- ec2_region: '{{ aws_region }}'
- ec2_access_key: '{{ aws_access_key }}'
- ec2_secret_key: '{{ aws_secret_key }}'
- access_token: '{{ security_token }}'
- register: credential_result
-
-- assert:
- that:
- - credential_result is successful
-
-##################################################################################
-# Tests using standard credentials from environment variables
-
-- name: 'Test basic operation using simple credentials (aws-environment)'
- example_module:
- environment:
- AWS_REGION: '{{ aws_region }}'
- AWS_ACCESS_KEY_ID: '{{ aws_access_key }}'
- AWS_SECRET_ACCESS_KEY: '{{ aws_secret_key }}'
- AWS_SECURITY_TOKEN: '{{ security_token }}'
- register: credential_result
-
-- assert:
- that:
- - credential_result is successful
-
-- name: 'Test basic operation using simple credentials (aws2-environment)'
- example_module:
- environment:
- AWS_DEFAULT_REGION: '{{ aws_region }}'
- AWS_ACCESS_KEY: '{{ aws_access_key }}'
- AWS_SECRET_KEY: '{{ aws_secret_key }}'
- AWS_SESSION_TOKEN: '{{ security_token }}'
- register: credential_result
-
-- assert:
- that:
- - credential_result is successful
-
-- name: 'Test basic operation using simple credentials (ec2-environment)'
- example_module:
- environment:
- EC2_REGION: '{{ aws_region }}'
- EC2_ACCESS_KEY: '{{ aws_access_key }}'
- EC2_SECRET_KEY: '{{ aws_secret_key }}'
- EC2_SECURITY_TOKEN: '{{ security_token }}'
- register: credential_result
-
-- assert:
- that:
- - credential_result is successful
-
-##################################################################################
-# Tests for missing parameters
-
-- name: 'Test with missing region'
- example_module:
- region: '{{ omit }}'
- access_key: '{{ aws_access_key }}'
- secret_key: '{{ aws_secret_key }}'
- security_token: '{{ security_token }}'
- register: missing_region
- ignore_errors: True
-
-- assert:
- that:
- - missing_region is failed
- - '"Either region or ec2_url must be specified" in missing_region.msg'
-
-- name: 'Test with missing access key'
- example_module:
- region: '{{ aws_region }}'
- access_key: '{{ omit }}'
- secret_key: '{{ aws_secret_key }}'
- security_token: '{{ security_token }}'
- register: missing_access
- ignore_errors: True
-
-- assert:
- that:
- - missing_access is failed
- - '"No handler was ready to authenticate." in missing_access.msg'
- #- '"aws_access_key_id" in missing_access.msg'
-
-- name: 'Test with missing secret key'
- example_module:
- region: '{{ aws_region }}'
- access_key: '{{ aws_access_key }}'
- secret_key: '{{ omit }}'
- security_token: '{{ security_token }}'
- register: missing_secret
- ignore_errors: True
-
-- assert:
- that:
- - missing_secret is failed
- - '"No handler was ready to authenticate." in missing_secret.msg'
- #- '"aws_secret_access_key" in missing_secret.msg'
-
-- name: 'Test with missing security token'
- example_module:
- region: '{{ aws_region }}'
- access_key: '{{ aws_access_key }}'
- secret_key: '{{ aws_secret_key }}'
- security_token: '{{ omit }}'
- register: missing_token
- ignore_errors: True
-
-- assert:
- that:
- - missing_token is failed
- # Caught when we try to do something, and passed to fail_json_aws
- - '"Fail JSON AWS" in missing_token.msg'
- - '"AWS was not able to validate the provided access credentials" in missing_token.msg'
-
-
-##################################################################################
-# Tests for bad parameters
-
-- name: 'Test with bad region'
- example_module:
- region: 'junk-example'
- access_key: '{{ aws_access_key }}'
- secret_key: '{{ aws_secret_key }}'
- security_token: '{{ security_token }}'
- register: bad_region
- ignore_errors: True
-
-- assert:
- that:
- - bad_region is failed
- - '"msg" in bad_region'
- - '"does not seem to be available" in bad_region.msg'
- - '"If the region definitely exists, you may need to upgrade boto or extend with endpoints_path" in bad_region.msg'
-
-- name: 'Test with bad access key'
- example_module:
- region: '{{ aws_region }}'
- access_key: 'junk-example'
- secret_key: '{{ aws_secret_key }}'
- security_token: '{{ security_token }}'
- register: bad_access
- ignore_errors: True
-
-- assert:
- that:
- - bad_access is failed
- # Caught when we try to do something, and passed to fail_json_aws
- - '"Fail JSON AWS" in missing_token.msg'
- - '"AWS was not able to validate the provided access credentials" in missing_token.msg'
-
-- name: 'Test with bad secret key'
- example_module:
- region: '{{ aws_region }}'
- access_key: '{{ aws_access_key }}'
- secret_key: 'junk-example'
- security_token: '{{ security_token }}'
- register: bad_secret
- ignore_errors: True
-
-- assert:
- that:
- - bad_secret is failed
- # Caught when we try to do something, and passed to fail_json_aws
- - '"Fail JSON AWS" in missing_token.msg'
- - '"AWS was not able to validate the provided access credentials" in missing_token.msg'
-
-- name: 'Test with bad security token'
- example_module:
- region: '{{ aws_region }}'
- access_key: '{{ aws_access_key }}'
- secret_key: '{{ aws_secret_key }}'
- security_token: 'junk-example'
- register: bad_token
- ignore_errors: True
-
-- assert:
- that:
- - bad_token is failed
- # Caught when we try to do something, and passed to fail_json_aws
- - '"Fail JSON AWS" in missing_token.msg'
- - '"AWS was not able to validate the provided access credentials" in missing_token.msg'
diff --git a/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/endpoints.yml b/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/endpoints.yml
deleted file mode 100644
index a8a6ba20ca7..00000000000
--- a/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/endpoints.yml
+++ /dev/null
@@ -1,119 +0,0 @@
----
-# Note 1: With boto3 we can use the FIPS endpoints as a minimal proxy for testing that
-# we're using something different. With boto2 the authentication fails.
-#
-##################################################################################
-# Tests using Endpoints
-
-- name: 'Test basic operation using standard endpoint (aws-parameters)'
- example_module:
- region: '{{ aws_region }}'
- aws_endpoint_url: 'https://ec2.{{ aws_region }}.amazonaws.com'
- aws_access_key: '{{ aws_access_key }}'
- aws_secret_key: '{{ aws_secret_key }}'
- aws_security_token: '{{ security_token }}'
- register: standard_endpoint_result
-
-- name: 'Check that we connected to the standard endpoint'
- assert:
- that:
- - standard_endpoint_result is successful
- #- '"ec2:DescribeImages" in standard_endpoint_result.resource_actions'
-
-- name: 'Test basic operation using standard endpoint - no region (aws-parameters)'
- example_module:
- region: '{{ omit }}'
- aws_endpoint_url: 'https://ec2.{{ aws_region }}.amazonaws.com'
- aws_access_key: '{{ aws_access_key }}'
- aws_secret_key: '{{ aws_secret_key }}'
- aws_security_token: '{{ security_token }}'
- register: standard_endpoint_result
-
-- name: 'Check that we connected to the standard endpoint'
- assert:
- that:
- - standard_endpoint_result is successful
- #- '"ec2:DescribeImages" in standard_endpoint_result.resource_actions'
-
-- name: 'Test basic operation using standard endpoint (aws-parameters)'
- example_module:
- region: '{{ aws_region }}'
- endpoint_url: 'https://ec2.{{ aws_region }}.amazonaws.com'
- aws_access_key: '{{ aws_access_key }}'
- aws_secret_key: '{{ aws_secret_key }}'
- aws_security_token: '{{ security_token }}'
- register: standard_endpoint_result
-
-- name: 'Check that we connected to the standard endpoint'
- assert:
- that:
- - standard_endpoint_result is successful
- #- '"ec2:DescribeImages" in standard_endpoint_result.resource_actions'
-
-- name: 'Test basic operation using standard endpoint (aws-parameters)'
- example_module:
- region: '{{ aws_region }}'
- ec2_url: 'https://ec2.{{ aws_region }}.amazonaws.com'
- aws_access_key: '{{ aws_access_key }}'
- aws_secret_key: '{{ aws_secret_key }}'
- aws_security_token: '{{ security_token }}'
- register: standard_endpoint_result
-
-- name: 'Check that we connected to the standard endpoint'
- assert:
- that:
- - standard_endpoint_result is successful
- #- '"ec2:DescribeImages" in standard_endpoint_result.resource_actions'
-
-##################################################################################
-# Tests using environment variables
-
-- name: 'Test basic operation using standard endpoint (aws-environment)'
- example_module:
- region: '{{ aws_region }}'
- aws_access_key: '{{ aws_access_key }}'
- aws_secret_key: '{{ aws_secret_key }}'
- aws_security_token: '{{ security_token }}'
- environment:
- AWS_URL: 'https://ec2.{{ aws_region }}.amazonaws.com'
- register: standard_endpoint_result
-
-- name: 'Check that we connected to the standard endpoint'
- assert:
- that:
- - standard_endpoint_result is successful
- #- '"ec2:DescribeImages" in standard_endpoint_result.resource_actions'
-
-- name: 'Test basic operation using standard endpoint (ec2-environment)'
- example_module:
- region: '{{ aws_region }}'
- aws_access_key: '{{ aws_access_key }}'
- aws_secret_key: '{{ aws_secret_key }}'
- aws_security_token: '{{ security_token }}'
- environment:
- EC2_URL: 'https://ec2.{{ aws_region }}.amazonaws.com'
- register: standard_endpoint_result
-
-- name: 'Check that we connected to the standard endpoint'
- assert:
- that:
- - standard_endpoint_result is successful
- #- '"ec2:DescribeImages" in standard_endpoint_result.resource_actions'
-
-##################################################################################
-# Tests using a bad endpoint URL
-# - This demonstrates that endpoint_url overrode region
-
-- name: 'Test with bad endpoint URL'
- example_module:
- region: '{{ aws_region }}'
- endpoint_url: 'https://junk.{{ aws_region }}.amazonaws.com'
- access_key: '{{ aws_access_key }}'
- secret_key: '{{ aws_secret_key }}'
- security_token: '{{ security_token }}'
- register: bad_endpoint
- ignore_errors: True
-
-- assert:
- that:
- - bad_endpoint is failed
diff --git a/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/main.yml b/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/main.yml
deleted file mode 100644
index 9e81b30802e..00000000000
--- a/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/main.yml
+++ /dev/null
@@ -1,12 +0,0 @@
----
-- name: 'Tests around standard credentials'
- include_tasks: 'credentials.yml'
-
-- name: 'Tests around profiles'
- include_tasks: 'profiles.yml'
-
-- name: 'Tests around endpoints'
- include_tasks: 'endpoints.yml'
-
-#- name: 'Tests around CA Bundles'
-# include_tasks: 'boto2_ec2/ca_bundle.yml'
diff --git a/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/profiles.yml b/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/profiles.yml
deleted file mode 100644
index bab095e47ad..00000000000
--- a/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/profiles.yml
+++ /dev/null
@@ -1,63 +0,0 @@
----
-# Note: unlike boto3 modules, boto2 modules can't read region from the profile
-#
-##################################################################################
-# Tests using profiles instead of directly consuming credentials
-
-- name: 'Test basic operation using profile (simple-parameters)'
- example_module:
- region: '{{ aws_region }}'
- profile: 'test_profile'
- register: profile_result
-
-- assert:
- that:
- - profile_result is successful
-
-- name: 'Test basic operation using profile (aws-parameters)'
- example_module:
- region: '{{ aws_region }}'
- aws_profile: 'test_profile'
- register: profile_result
-
-- assert:
- that:
- - profile_result is successful
-
-- name: 'Test basic operation using profile (aws-environment)'
- example_module:
- region: '{{ aws_region }}'
- environment:
- AWS_PROFILE: 'test_profile'
- register: profile_result
-
-- assert:
- that:
- - profile_result is successful
-
-- name: 'Test basic operation using profile (aws2-environment)'
- example_module:
- region: '{{ aws_region }}'
- environment:
- AWS_DEFAULT_PROFILE: 'test_profile'
- register: profile_result
-
-- assert:
- that:
- - profile_result is successful
-
-##################################################################################
-# Tests with bad profile
-
-- name: 'Test with bad profile'
- example_module:
- region: '{{ aws_region }}'
- profile: 'junk-profile'
- register: bad_profile
- ignore_errors: True
-
-- assert:
- that:
- - bad_profile is failed
- - '"msg" in bad_profile'
- - '"Profile given for AWS was not found." in bad_profile.msg'
diff --git a/tests/integration/targets/module_utils_ec2/runme.sh b/tests/integration/targets/module_utils_ec2/runme.sh
deleted file mode 100755
index cbf09a5422d..00000000000
--- a/tests/integration/targets/module_utils_ec2/runme.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/env bash
-
-set -eux
-
-ANSIBLE_ROLES_PATH="../"
-# Boto3
-AWS_CONFIG_FILE="$( pwd )/boto3_config"
-# Boto2
-BOTO_CONFIG="$( pwd )/boto3_config"
-
-export ANSIBLE_ROLES_PATH
-export AWS_CONFIG_FILE
-export BOTO_CONFIG
-
-ansible-playbook setup.yml -i localhost "$@"
-ansible-playbook ec2_connect.yml -i inventory "$@" -e "@session_credentials.yml"
-ansible-playbook connect_to_aws.yml -i inventory "$@" -e "@session_credentials.yml"
diff --git a/tests/integration/targets/module_utils_ec2/setup.yml b/tests/integration/targets/module_utils_ec2/setup.yml
deleted file mode 100644
index 9b219eb20f9..00000000000
--- a/tests/integration/targets/module_utils_ec2/setup.yml
+++ /dev/null
@@ -1,40 +0,0 @@
----
-- hosts: localhost
- connection: local
- gather_facts: no
- tasks:
- # ===========================================================
- # While CI uses a dedicated session, the easiest way to run
- # tests outside of CI is with a simple access/secret key pair.
- #
- # For consistency, use sts_session_token to grab session
- # credentials if we're not already using a session
- # Note: this can't be done within a session, hence the slightly
- # strange dance
- - name: 'Get a session token if we are using a basic key'
- when:
- - security_token is not defined
- block:
- - name: 'Get a session token'
- sts_session_token:
- region: '{{ aws_region }}'
- aws_access_key: '{{ aws_access_key }}'
- aws_secret_key: '{{ aws_secret_key }}'
- register: session_token
- no_log: true
- - name: 'Override initial tokens'
- set_fact:
- session_access_key: '{{ session_token.sts_creds.access_key }}'
- session_secret_key: '{{ session_token.sts_creds.secret_key }}'
- session_security_token: '{{ session_token.sts_creds.session_token }}'
- no_log: true
-
- - name: 'Write out credentials'
- template:
- dest: './session_credentials.yml'
- src: 'session_credentials.yml.j2'
-
- - name: 'Write out boto config file'
- template:
- dest: './boto3_config'
- src: 'boto_config.j2'
diff --git a/tests/integration/targets/module_utils_ec2/templates/boto_config.j2 b/tests/integration/targets/module_utils_ec2/templates/boto_config.j2
deleted file mode 100644
index f8668f057fc..00000000000
--- a/tests/integration/targets/module_utils_ec2/templates/boto_config.j2
+++ /dev/null
@@ -1,5 +0,0 @@
-[profile test_profile]
-region = {{ aws_region }}
-aws_access_key_id = {{ session_access_key | default(aws_access_key) }}
-aws_secret_access_key = {{ session_secret_key | default(aws_secret_key) }}
-aws_security_token = {{ session_security_token | default(security_token) }}
diff --git a/tests/integration/targets/module_utils_ec2/templates/session_credentials.yml.j2 b/tests/integration/targets/module_utils_ec2/templates/session_credentials.yml.j2
deleted file mode 100644
index bb030439392..00000000000
--- a/tests/integration/targets/module_utils_ec2/templates/session_credentials.yml.j2
+++ /dev/null
@@ -1,3 +0,0 @@
-aws_access_key: {{ session_access_key | default(aws_access_key) }}
-aws_secret_key: {{ session_secret_key | default(aws_secret_key) }}
-security_token: {{ session_security_token | default(security_token) }}
diff --git a/tests/sanity/ignore-2.9.txt b/tests/sanity/ignore-2.9.txt
index 9950c296ab7..e8ec7663805 100644
--- a/tests/sanity/ignore-2.9.txt
+++ b/tests/sanity/ignore-2.9.txt
@@ -1,8 +1,5 @@
plugins/module_utils/ec2.py pylint:ansible-deprecated-no-version # We use dates for deprecations, Ansible 2.9 only supports this for compatability
plugins/modules/aws_az_info.py pylint:ansible-deprecated-no-version # We use dates for deprecations, Ansible 2.9 only supports this for compatability
-plugins/modules/ec2.py pylint:ansible-deprecated-no-version # We use dates for deprecations, Ansible 2.9 only supports this for compatability
-plugins/modules/ec2.py validate-modules:deprecation-mismatch # Ansible 2.9 docs don't support deprecation properly
-plugins/modules/ec2.py validate-modules:invalid-documentation # Ansible 2.9 docs don't support deprecation properly
plugins/modules/ec2_tag.py pylint:ansible-deprecated-no-version # We use dates for deprecations, Ansible 2.9 only supports this for compatability
plugins/modules/ec2_vol.py pylint:ansible-deprecated-no-version # We use dates for deprecations, Ansible 2.9 only supports this for compatability
plugins/modules/ec2_vpc_dhcp_option.py pylint:ansible-deprecated-no-version # We use dates for deprecations, Ansible 2.9 only supports this for compatability
diff --git a/tests/unit/requirements.txt b/tests/unit/requirements.txt
index 704c73a25b2..49f392832ab 100644
--- a/tests/unit/requirements.txt
+++ b/tests/unit/requirements.txt
@@ -1,6 +1,5 @@
# Our code is based on the AWS SDKs
botocore
boto3
-boto
placebo