From 9e07cec6ee167bc1ed74cb629bf5070009a0c843 Mon Sep 17 00:00:00 2001 From: Mandar Kulkarni Date: Wed, 12 Oct 2022 13:37:18 -0700 Subject: [PATCH] ec2_ami: Add support for params BootMode, TpmSupport, UefiData (#1037) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ec2_ami: Add support for params BootMode, TpmSupport, UefiData SUMMARY Depends-On: #1066 Added support for params BootMode, TpmSupport, UefiData in ec2_ami. Fixes #944 ISSUE TYPE Feature Pull Request COMPONENT NAME ec2_ami ADDITIONAL INFORMATION Example playbook - name: abc hosts: localhost gather_facts: false tasks: - name: AMI Creation with boot_mode and tpm_support amazon.aws.ec2_ami: name: ami-create-test_legacy-bios state: present architecture: x86_64 virtualization_type: hvm root_device_name: /dev/sda1 device_mapping: - device_name: /dev/sda1 snapshot_id: snap-xxxxxxxxx wait: yes region: us-east-2 boot_mode: legacy-bios tpm_support: v2.0 tags: name: ami-create-test Reviewed-by: Gonéri Le Bouder Reviewed-by: Mandar Kulkarni Reviewed-by: Mike Graves --- ...rt-for-boot_mode-tpm_support-uefi_data.yml | 2 + plugins/modules/ec2_ami.py | 55 ++++++++++++++ .../integration/targets/ec2_ami/meta/main.yml | 3 + .../targets/ec2_ami/tasks/main.yml | 74 +++++++++++++++++++ tests/unit/plugins/modules/test_ec2_ami.py | 44 +++++++++++ 5 files changed, 178 insertions(+) create mode 100644 changelogs/fragments/1037-ec2_ami-add-support-for-boot_mode-tpm_support-uefi_data.yml create mode 100644 tests/unit/plugins/modules/test_ec2_ami.py diff --git a/changelogs/fragments/1037-ec2_ami-add-support-for-boot_mode-tpm_support-uefi_data.yml b/changelogs/fragments/1037-ec2_ami-add-support-for-boot_mode-tpm_support-uefi_data.yml new file mode 100644 index 00000000000..2a171566b2e --- /dev/null +++ b/changelogs/fragments/1037-ec2_ami-add-support-for-boot_mode-tpm_support-uefi_data.yml @@ -0,0 +1,2 @@ +minor_changes: + - ec2_ami - add support for BootMode, TpmSupport, UefiData params (https://github.com/ansible-collections/amazon.aws/pull/1037). diff --git a/plugins/modules/ec2_ami.py b/plugins/modules/ec2_ami.py index 403edd46859..a9c0342c679 100644 --- a/plugins/modules/ec2_ami.py +++ b/plugins/modules/ec2_ami.py @@ -144,6 +144,27 @@ description: - Set to simple to enable enhanced networking with the Intel 82599 Virtual Function interface for the AMI and any instances that you launch from the AMI. type: str + boot_mode: + description: + - The boot mode of the AMI. + - See the AWS documentation for more detail U(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ami-boot.html). + type: str + choices: ['legacy-bios', 'uefi'] + tpm_support: + description: + - Set to v2.0 to enable Trusted Platform Module (TPM) support. + - If the image is configured for NitroTPM support, the value is v2.0 . + - Requires I(boot_mode) to be set to 'uefi'. + - Requires an instance type that is compatible with Nitro. + - Requires minimum botocore version 1.26.0. + - See the AWS documentation for more detail U(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/nitrotpm.html). + type: str + uefi_data: + description: + - Base64 representation of the non-volatile UEFI variable store. + - Requires minimum botocore version 1.26.0. + - See the AWS documentation for more detail U(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/uefi-secure-boot.html). + type: str author: - "Evan Duffield (@scicoin-project) " - "Constantin Bugneac (@Constantin07) " @@ -216,6 +237,22 @@ - device_name: /dev/sdb no_device: true +- name: AMI Creation with boot_mode and tpm_support + amazon.aws.ec2_ami: + name: newtest + state: present + architecture: x86_64 + virtualization_type: hvm + root_device_name: /dev/sda1 + device_mapping: + - device_name: /dev/sda1 + snapshot_id: "{{ snapshot_id }}" + wait: yes + region: us-east-1 + boot_mode: uefi + uefi_data: data_file.bin + tpm_support: v2.0 + - name: Deregister/Delete AMI (keep associated snapshots) amazon.aws.ec2_ami: image_id: "{{ instance.image_id }}" @@ -441,6 +478,12 @@ def create_image(module, connection): billing_products = module.params.get('billing_products') ramdisk_id = module.params.get('ramdisk_id') sriov_net_support = module.params.get('sriov_net_support') + boot_mode = module.params.get('boot_mode') + tpm_support = module.params.get('tpm_support') + uefi_data = module.params.get('uefi_data') + + if tpm_support and boot_mode != 'uefi': + module.fail_json(msg="To specify 'tpm_support', 'boot_mode' must be 'uefi'.") if module.check_mode: image = connection.describe_images(Filters=[{'Name': 'name', 'Values': [str(name)]}]) @@ -509,6 +552,12 @@ def create_image(module, connection): params['KernelId'] = kernel_id if root_device_name: params['RootDeviceName'] = root_device_name + if boot_mode: + params['BootMode'] = boot_mode + if tpm_support: + params['TpmSupport'] = tpm_support + if uefi_data: + params['UefiData'] = uefi_data image_id = connection.register_image(aws_retry=True, **params).get('ImageId') except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: module.fail_json_aws(e, msg="Error registering image") @@ -731,6 +780,9 @@ def main(): sriov_net_support=dict(), tags=dict(type='dict', aliases=['resource_tags']), purge_tags=dict(type='bool', default=True), + boot_mode=dict(type='str', choices=['legacy-bios', 'uefi']), + tpm_support=dict(type='str'), + uefi_data=dict(type='str'), ) module = AnsibleAWSModule( @@ -746,6 +798,9 @@ def main(): if not any([module.params['image_id'], module.params['name']]): module.fail_json(msg="one of the following is required: name, image_id") + if any([module.params['tpm_support'], module.params['uefi_data']]): + module.require_botocore_at_least('1.26.0', reason='required for ec2.register_image with tpm_support or uefi_data') + connection = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) if module.params.get('state') == 'absent': diff --git a/tests/integration/targets/ec2_ami/meta/main.yml b/tests/integration/targets/ec2_ami/meta/main.yml index 1471b11f658..3dc000aba61 100644 --- a/tests/integration/targets/ec2_ami/meta/main.yml +++ b/tests/integration/targets/ec2_ami/meta/main.yml @@ -1,2 +1,5 @@ dependencies: - setup_ec2_facts + - role: setup_botocore_pip + vars: + botocore_version: '1.26.0' \ No newline at end of file diff --git a/tests/integration/targets/ec2_ami/tasks/main.yml b/tests/integration/targets/ec2_ami/tasks/main.yml index ae51014d78b..3bfbcbf1330 100644 --- a/tests/integration/targets/ec2_ami/tasks/main.yml +++ b/tests/integration/targets/ec2_ami/tasks/main.yml @@ -10,6 +10,12 @@ - amazon.aws block: + # AWS CLI is needed until there's a module to get instance uefi data + - name: Install AWS CLI + pip: + name: awscli==1.25.83 + state: present + # ============================================================ # SETUP: vpc, ec2 key pair, subnet, security group, ec2 instance, snapshot @@ -74,6 +80,21 @@ state: present register: setup_snapshot + # note: the current CI supported instance types (t2, t3, m1) do not support uefi boot mode + tpm_support + # disabling the task as aws documentation states that support for t3 will be coming soon + # - name: get instance UEFI data + # command: aws ec2 get-instance-uefi-data --instance-id {{ ec2_instance_id }} --region {{ aws_region }} + # environment: + # AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + # AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + # AWS_SESSION_TOKEN: "{{ security_token | default('') }}" + # AWS_DEFAULT_REGION: "{{ aws_region }}" + # register: instance_uefi_data_output + + # - name: Convert it to an object + # set_fact: + # instance_uefi_data: "{{ instance_uefi_data_output.stdout | from_json }}" + # ============================================================ - name: test clean failure if not providing image_id or name with state=present @@ -629,6 +650,52 @@ - not result.changed - not result.failed + # ============================================================ + + - name: create an image from the snapshot with boot_mode and tpm_support + ec2_ami: + name: '{{ ec2_ami_name }}_ami-boot-tpm' + description: '{{ ec2_ami_description }}' + state: present + boot_mode: uefi + tpm_support: v2.0 + launch_permissions: + user_ids: [] + tags: + Name: '{{ ec2_ami_name }}_ami-boot-tpm' + root_device_name: '{{ ec2_ami_root_disk }}' + device_mapping: + - device_name: '{{ ec2_ami_root_disk }}' + volume_type: gp2 + size: 8 + delete_on_termination: true + snapshot_id: '{{ setup_snapshot.snapshot_id }}' + register: result + ignore_errors: true + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - name: set image id fact for deletion later + set_fact: + ec2_ami_image_id_boot_tpm: "{{ result.image_id }}" + ec2_ami_snapshot_boot_tpm: "{{ result.block_device_mapping[ec2_ami_root_disk].snapshot_id }}" + + - name: gather facts about the image created + ec2_ami_info: + image_ids: '{{ ec2_ami_image_id_boot_tpm }}' + register: ami_facts_result_boot_tpm + ignore_errors: true + vars: + ansible_python_interpreter: "{{ botocore_virtualenv_interpreter }}" + + - name: assert that new ami has been created with desired options + assert: + that: + - "result.changed" + - "result.image_id.startswith('ami-')" + - ami_facts_result_boot_tpm.images[0].image_id | length != 0 + - ami_facts_result_boot_tpm.images[0].boot_mode == 'uefi' + - ami_facts_result_boot_tpm.images[0].tpm_support == 'v2.0' # ============================================================ @@ -641,6 +708,13 @@ debug: msg: "***** TESTING COMPLETE. COMMENCE TEARDOWN *****" + - name: delete ami + ec2_ami: + state: absent + image_id: "{{ ec2_ami_image_id_boot_tpm }}" + wait: yes + ignore_errors: yes + - name: delete ami ec2_ami: state: absent diff --git a/tests/unit/plugins/modules/test_ec2_ami.py b/tests/unit/plugins/modules/test_ec2_ami.py new file mode 100644 index 00000000000..5e8140d4a46 --- /dev/null +++ b/tests/unit/plugins/modules/test_ec2_ami.py @@ -0,0 +1,44 @@ +# 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 unittest.mock import MagicMock, Mock, patch, call + +import pytest + +from ansible_collections.amazon.aws.plugins.modules import ec2_ami + +module_name = "ansible_collections.amazon.aws.plugins.modules.ec2_ami" + + +@patch(module_name + ".get_image_by_id") +def test_create_image_uefi_data(m_get_image_by_id): + module = MagicMock() + connection = MagicMock() + + m_get_image_by_id.return_value = { + "ImageId": "ami-0c7a795306730b288", + "BootMode": "uefi", + "TpmSupport": "v2.0", + } + + module.params = { + "name": "my-image", + "boot_mode": "uefi", + "tpm_support": "v2.0", + "uefi_data": "QU1aTlVFRkk9xcN0AAAAAHj5a7fZ9+3aT2gcVRgA8Ek3NipiPST0pCiCIlTJtj20FzENCcQa", + } + + ec2_ami.create_image(module, connection) + assert connection.register_image.call_count == 1 + connection.register_image.assert_has_calls( + [ + call( + aws_retry=True, + Description=None, + Name="my-image", + BootMode="uefi", + TpmSupport="v2.0", + UefiData="QU1aTlVFRkk9xcN0AAAAAHj5a7fZ9+3aT2gcVRgA8Ek3NipiPST0pCiCIlTJtj20FzENCcQa" + ) + ] + )