diff --git a/changelogs/fragments/972-ec2_instance-stop-hibernate.yml b/changelogs/fragments/972-ec2_instance-stop-hibernate.yml new file mode 100644 index 00000000000..92ef8fd77e9 --- /dev/null +++ b/changelogs/fragments/972-ec2_instance-stop-hibernate.yml @@ -0,0 +1,2 @@ +minor_changes: +- ec2_instance - Add hibernation_options and volumes->ebs->encrypted keys to support stop-hibernate instance (https://github.com/ansible-collections/amazon.aws/pull/972). diff --git a/plugins/modules/ec2_instance.py b/plugins/modules/ec2_instance.py index 2125ab5a42a..0999f5696b3 100644 --- a/plugins/modules/ec2_instance.py +++ b/plugins/modules/ec2_instance.py @@ -244,6 +244,14 @@ - Whether to enable termination protection. - This module will not terminate an instance with termination protection active, it must be turned off first. type: bool + hibernation_options: + description: + - Indicates whether an instance is enabled for hibernation. + Refer U(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/hibernating-prerequisites.html) + for Hibernation prerequisits. + type: bool + default: False + version_added: 5.0.0 cpu_credit_specification: description: - For T series instances, choose whether to allow increased charges to buy CPU credits if the default pool is depleted. @@ -1289,6 +1297,15 @@ def build_top_level_options(params): spec['InstanceInitiatedShutdownBehavior'] = params.get('instance_initiated_shutdown_behavior') if params.get('termination_protection') is not None: spec['DisableApiTermination'] = params.get('termination_protection') + if params.get('hibernation_options') and params.get('volumes'): + for vol in params['volumes']: + if vol.get('ebs') and vol['ebs'].get('encrypted'): + spec['HibernationOptions'] = {'Configured': True} + else: + module.fail_json( + msg="Hibernation prerequisites not satisfied. Refer {0}".format( + "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/hibernating-prerequisites.html") + ) if params.get('cpu_options') is not None: spec['CpuOptions'] = {} spec['CpuOptions']['ThreadsPerCore'] = params.get('cpu_options').get('threads_per_core') @@ -2020,6 +2037,7 @@ def main(): placement_group=dict(type='str'), instance_initiated_shutdown_behavior=dict(type='str', choices=['stop', 'terminate']), termination_protection=dict(type='bool'), + hibernation_options=dict(type='bool', default=False), detailed_monitoring=dict(type='bool'), instance_ids=dict(default=[], type='list', elements='str'), network=dict(default=None, type='dict'), diff --git a/tests/integration/targets/ec2_instance_hibernation_options/aliases b/tests/integration/targets/ec2_instance_hibernation_options/aliases new file mode 100644 index 00000000000..9993c7be99a --- /dev/null +++ b/tests/integration/targets/ec2_instance_hibernation_options/aliases @@ -0,0 +1,3 @@ +cloud/aws +ec2_instance_info +ec2_instance \ No newline at end of file diff --git a/tests/integration/targets/ec2_instance_hibernation_options/defaults/main.yml b/tests/integration/targets/ec2_instance_hibernation_options/defaults/main.yml new file mode 100644 index 00000000000..1e51a336a63 --- /dev/null +++ b/tests/integration/targets/ec2_instance_hibernation_options/defaults/main.yml @@ -0,0 +1,4 @@ +--- +# defaults file for ec2_instance +ec2_instance_type: 't3.micro' +ec2_instance_tag_TestId: '{{ resource_prefix }}-instance-hibernation-options' diff --git a/tests/integration/targets/ec2_instance_hibernation_options/meta/main.yml b/tests/integration/targets/ec2_instance_hibernation_options/meta/main.yml new file mode 100644 index 00000000000..80a82ca0bac --- /dev/null +++ b/tests/integration/targets/ec2_instance_hibernation_options/meta/main.yml @@ -0,0 +1,9 @@ +# this just makes sure they're in the right place +dependencies: +- role: setup_ec2_facts +- role: setup_ec2_instance_env + vars: + ec2_instance_test_name: hibernation_options +- role: setup_botocore_pip + vars: + boto3_version: "1.20.30" diff --git a/tests/integration/targets/ec2_instance_hibernation_options/tasks/main.yml b/tests/integration/targets/ec2_instance_hibernation_options/tasks/main.yml new file mode 100644 index 00000000000..e6aace728ec --- /dev/null +++ b/tests/integration/targets/ec2_instance_hibernation_options/tasks/main.yml @@ -0,0 +1,145 @@ +- 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: Create instance with hibernation option (check mode) + ec2_instance: + name: "{{ resource_prefix }}-hibernation-options" + image_id: "{{ ec2_ami_id }}" + tags: + TestId: "{{ resource_prefix }}" + security_groups: "{{ sg.group_id }}" + vpc_subnet_id: "{{ testing_subnet_b.subnet.id }}" + hibernation_options: true + instance_type: "{{ ec2_instance_type }}" + volumes: + - device_name: /dev/sda1 + ebs: + delete_on_termination: true + encrypted: true + state: running + wait: yes + check_mode: yes + register: create_instance_check_mode_results + + - name: Check the returned value for the earlier task + assert: + that: + - create_instance_check_mode_results is changed + - create_instance_check_mode_results.spec.HibernationOptions.Configured == True + + - name: Create instance with hibernation config + ec2_instance: + name: "{{ resource_prefix }}-hibernation-options" + image_id: "{{ ec2_ami_id }}" + tags: + TestId: "{{ resource_prefix }}" + security_groups: "{{ sg.group_id }}" + vpc_subnet_id: "{{ testing_subnet_b.subnet.id }}" + hibernation_options: true + instance_type: "{{ ec2_instance_type }}" + volumes: + - device_name: /dev/sda1 + ebs: + delete_on_termination: true + encrypted: true + state: running + wait: yes + register: create_instance_results + + - set_fact: + instance_id: '{{ create_instance_results.instances[0].instance_id }}' + + - name: Check return values of the create instance task + assert: + that: + - "{{ create_instance_results.instances | length }} > 0" + - "'{{ create_instance_results.instances.0.state.name }}' == 'running'" + - "'{{ create_instance_results.spec.HibernationOptions.Configured }}'" + + - name: Gather information about the instance to get the hibernation status + ec2_instance_info: + filters: + "tag:Name": "{{ resource_prefix }}-hibernation-options" + register: instance_hibernation_status + + - name: Assert hibernation options is true + assert: + that: + - instance_hibernation_status.instances[0].hibernation_options.configured == true + + - name: Create instance with hibernation option (check mode) (idempotent) + ec2_instance: + name: "{{ resource_prefix }}-hibernation-options" + image_id: "{{ ec2_ami_id }}" + tags: + TestId: "{{ resource_prefix }}" + security_groups: "{{ sg.group_id }}" + vpc_subnet_id: "{{ testing_subnet_b.subnet.id }}" + hibernation_options: true + instance_type: "{{ ec2_instance_type }}" + volumes: + - device_name: /dev/sda1 + ebs: + delete_on_termination: true + encrypted: true + state: running + wait: yes + check_mode: yes + register: create_instance_check_mode_results + + - name: Check the returned value for the earlier task + assert: + that: + - create_instance_check_mode_results is not changed + + - name: Create instance with hibernation options configured (idempotent) + ec2_instance: + name: "{{ resource_prefix }}-hibernation-options" + image_id: "{{ ec2_ami_id }}" + tags: + TestId: "{{ resource_prefix }}" + security_groups: "{{ sg.group_id }}" + vpc_subnet_id: "{{ testing_subnet_b.subnet.id }}" + hibernation_options: true + instance_type: "{{ ec2_instance_type }}" + volumes: + - device_name: /dev/sda1 + ebs: + delete_on_termination: true + encrypted: true + state: running + wait: yes + register: create_instance_results + + - name: Check return values of the create instance task + assert: + that: + - "{{ not create_instance_results.changed }}" + - "{{ create_instance_results.instances | length }} > 0" + + - name: Create instance with hibernation options configured with unencrypted volume + ec2_instance: + name: "{{ resource_prefix }}-hibernation-options-error" + image_id: "{{ ec2_ami_id }}" + tags: + TestId: "{{ resource_prefix }}" + security_groups: "{{ sg.group_id }}" + vpc_subnet_id: "{{ testing_subnet_b.subnet.id }}" + hibernation_options: true + instance_type: "{{ ec2_instance_type }}" + volumes: + - device_name: /dev/sda1 + ebs: + delete_on_termination: true + register: create_instance_results + failed_when: "'Hibernation prerequisites not satisfied' not in create_instance_results.msg" + + - name: Terminate the instance + ec2_instance: + filters: + tag:TestId: "{{ resource_prefix }}" + state: absent \ No newline at end of file