diff --git a/.github/workflows/sanity.yml b/.github/workflows/sanity.yml index 01bc4c9c086..c7ebf34c431 100644 --- a/.github/workflows/sanity.yml +++ b/.github/workflows/sanity.yml @@ -60,5 +60,9 @@ jobs: { "ansible-version": "devel", "python-version": "3.8" + }, + { + "ansible-version": "devel", + "python-version": "3.9" } ] diff --git a/.github/workflows/units.yml b/.github/workflows/units.yml index b55028a08df..adfa2dddd1f 100644 --- a/.github/workflows/units.yml +++ b/.github/workflows/units.yml @@ -59,6 +59,10 @@ jobs: { "ansible-version": "devel", "python-version": "3.8" + }, + { + "ansible-version": "devel", + "python-version": "3.9" } ] collection_pre_install: '' diff --git a/changelogs/fragments/ec2_instance_info-support-new-attribute.yml b/changelogs/fragments/ec2_instance_info-support-new-attribute.yml new file mode 100644 index 00000000000..5025aed21e7 --- /dev/null +++ b/changelogs/fragments/ec2_instance_info-support-new-attribute.yml @@ -0,0 +1,3 @@ +--- +minor_changes: + - ec2_instance_info - add new parameter `include_attributes` to describe instance attributes (https://github.com/ansible-collections/amazon.aws/pull/1577). diff --git a/plugins/modules/ec2_instance_info.py b/plugins/modules/ec2_instance_info.py index 484f54f24c1..6977edb9700 100644 --- a/plugins/modules/ec2_instance_info.py +++ b/plugins/modules/ec2_instance_info.py @@ -36,6 +36,30 @@ required: false aliases: ['uptime'] type: int + include_attributes: + description: + - Describes the specified attributes of the returned instances. + required: false + type: list + elements: str + choices: + - instanceType + - kernel + - ramdisk + - userData + - disableApiTermination + - instanceInitiatedShutdownBehavior + - rootDeviceName + - blockDeviceMapping + - productCodes + - sourceDestCheck + - groupSet + - ebsOptimized + - sriovNetSupport + - enclaveOptions + - disableApiStop + aliases: ['attributes'] + version_added: 6.3.0 extends_documentation_fragment: - amazon.aws.common.modules @@ -77,6 +101,13 @@ "tag:Name": "RHEL-*" instance-state-name: [ "running"] register: ec2_node_info + +- name: Gather information about a particular instance using ID and include kernel attribute + amazon.aws.ec2_instance_info: + instance_ids: + - i-12345678 + include_attributes: + - kernel """ RETURN = r""" @@ -500,6 +531,20 @@ returned: always type: dict sample: vpc-0011223344 + attributes: + description: The details of the instance attribute specified on input. + returned: when include_attribute is specified + type: dict + sample: + { + 'disable_api_termination': { + 'value': True + }, + 'ebs_optimized': { + 'value': True + } + } + version_added: 6.3.0 """ import datetime @@ -549,6 +594,12 @@ def list_ec2_instances(connection, module): for reservation in reservations["Reservations"]: instances = instances + reservation["Instances"] + # include instances attributes + attributes = module.params.get("include_attributes") + if attributes: + for instance in instances: + instance["attributes"] = describe_instance_attributes(connection, instance["InstanceId"], attributes) + # Turn the boto3 result in to ansible_friendly_snaked_names snaked_instances = [camel_dict_to_snake_dict(instance) for instance in instances] @@ -559,11 +610,39 @@ def list_ec2_instances(connection, module): module.exit_json(instances=snaked_instances) +def describe_instance_attributes(connection, instance_id, attributes): + result = {} + for attr in attributes: + response = connection.describe_instance_attribute(Attribute=attr, InstanceId=instance_id) + for key in response: + if key not in ("InstanceId", "ResponseMetadata"): + result[key] = response[key] + return result + + def main(): + instance_attributes = [ + "instanceType", + "kernel", + "ramdisk", + "userData", + "disableApiTermination", + "instanceInitiatedShutdownBehavior", + "rootDeviceName", + "blockDeviceMapping", + "productCodes", + "sourceDestCheck", + "groupSet", + "ebsOptimized", + "sriovNetSupport", + "enclaveOptions", + "disableApiStop", + ] argument_spec = dict( minimum_uptime=dict(required=False, type="int", default=None, aliases=["uptime"]), instance_ids=dict(default=[], type="list", elements="str"), filters=dict(default={}, type="dict"), + include_attributes=dict(type="list", elements="str", aliases=["attributes"], choices=instance_attributes), ) module = AnsibleAWSModule( diff --git a/tests/integration/targets/ec2_instance_info/aliases b/tests/integration/targets/ec2_instance_info/aliases new file mode 100644 index 00000000000..704e2295951 --- /dev/null +++ b/tests/integration/targets/ec2_instance_info/aliases @@ -0,0 +1,4 @@ +time=1m +cloud/aws +ec2_instance_info +ec2_instance diff --git a/tests/integration/targets/ec2_instance_info/defaults/main.yml b/tests/integration/targets/ec2_instance_info/defaults/main.yml new file mode 100644 index 00000000000..5ec6ddfc542 --- /dev/null +++ b/tests/integration/targets/ec2_instance_info/defaults/main.yml @@ -0,0 +1,7 @@ +--- +ec2_instance_type: 't2.micro' +ec2_instance_tag_TestId: '{{ resource_prefix }}-instance-info' +ec2_instance_name: "{{ resource_prefix }}-test-instance-info" +ec2_instance_user_data: | + packages: + - httpd diff --git a/tests/integration/targets/ec2_instance_info/meta/main.yml b/tests/integration/targets/ec2_instance_info/meta/main.yml new file mode 100644 index 00000000000..aefa59ca473 --- /dev/null +++ b/tests/integration/targets/ec2_instance_info/meta/main.yml @@ -0,0 +1,4 @@ +# this just makes sure they're in the right place +dependencies: +- role: setup_ec2_facts +- role: setup_ec2_instance_env diff --git a/tests/integration/targets/ec2_instance_info/tasks/main.yml b/tests/integration/targets/ec2_instance_info/tasks/main.yml new file mode 100644 index 00000000000..2e3aba8095f --- /dev/null +++ b/tests/integration/targets/ec2_instance_info/tasks/main.yml @@ -0,0 +1,76 @@ +--- +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + - name: "Make instance in the testing subnet created in the test VPC" + ec2_instance: + state: present + name: "{{ ec2_instance_name }}" + image_id: "{{ ec2_ami_id }}" + availability_zone: '{{ subnet_b_az }}' + tags: + TestId: "{{ ec2_instance_tag_TestId }}" + user_data: "{{ ec2_instance_user_data }}" + instance_type: "{{ ec2_instance_type }}" + wait: false + + - name: "Gather {{ ec2_instance_name }} info" + ec2_instance_info: + filters: + "tag:Name": "{{ ec2_instance_name }}" + include_attributes: + - instanceType + - kernel + - ramdisk + - userData + - disableApiTermination + - instanceInitiatedShutdownBehavior + - rootDeviceName + - blockDeviceMapping + - productCodes + - sourceDestCheck + - groupSet + - ebsOptimized + - sriovNetSupport + - enclaveOptions + register: _instance_info + + - name: Validate that returned value contains required attributes + assert: + that: + - _instance_info.instances | length > 0 + - '"attributes" in _instance_info.instances[0]' + # instance type + - _instance_info.instances[0].attributes.instance_type.value == ec2_instance_type + # User data + - _instance_info.instances[0].attributes.user_data.value | b64decode == ec2_instance_user_data + # kernel + - '"kernel_id" in _instance_info.instances[0].attributes' + # Ram disk + - '"ramdisk_id" in _instance_info.instances[0].attributes' + # Disable API termination + - not (_instance_info.instances[0].attributes.disable_api_termination.value | bool) + # Instance Initiated Shutdown Behavior + - '"instance_initiated_shutdown_behavior" in _instance_info.instances[0].attributes' + # Root Device Name + - _instance_info.instances[0].attributes.root_device_name.value == "/dev/sda1" + # Block Device Mapping + - '"block_device_mappings" in _instance_info.instances[0].attributes' + - _instance_info.instances[0].attributes.block_device_mappings[0].device_name == "/dev/sda1" + - '"ebs" in _instance_info.instances[0].attributes.block_device_mappings[0]' + # Product Codes + - '"product_codes" in _instance_info.instances[0].attributes' + # Source Dest Check + - _instance_info.instances[0].attributes.source_dest_check.value | bool + # GroupSet + - '"groups" in _instance_info.instances[0].attributes' + # Ebs Optimized + - not (_instance_info.instances[0].attributes.ebs_optimized.value | bool) + # Sriov Net Support + - '"sriov_net_support" in _instance_info.instances[0].attributes' + # Enclave Options + - not (_instance_info.instances[0].attributes.enclave_options.enabled | bool)