Repository: ansible-collections/amazon.aws

Issue:
ec2_instance: Add support for controlling the Metadata options
<!--- Verify first that your feature was not already discussed on GitHub -->
<!--- Complete *all* sections as described, this form is processed automatically -->

##### SUMMARY
Add to choose 'Metadata accessible' and 'Metadata version' options in the amazon.aws.ec2 module. When provisioning a new EC2 instance, currently there is no option to choose 'Metadata accessible' and 'Metadata version' options. 
- amazon.aws.ec2 – create, terminate, start or stop an instance in ec2
  https://docs.ansible.com/ansible/latest/collections/amazon/aws/ec2_module.html
              
Please refer to below screenshot for the 'Metadata accessible' and 'Metadata version' options in the ec2 launch page of AWS.

![image](https://user-images.githubusercontent.com/58404577/124867085-cc787400-e000-11eb-961d-231ff797d30e.png)

##### ISSUE TYPE
- Feature Idea

##### COMPONENT NAME
amazon.aws.ec2 module

##### ADDITIONAL INFORMATION
<!--- Describe how the feature would be used, why it is needed and what it would solve -->

<!--- Paste example playbooks or commands between quotes below -->
```yaml

```

<!--- HINT: You can also paste gist.github.com links for larger files -->



Discussion:
Files identified in the description:
None

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
!component ec2_instance
Hi @garamirseokim,

While we haven't officially started the deprecation process for the ec2 module, it's based upon the 'old' Amazon SDK (boto) rather than the 'new' SDK (boto3/botocore) and can generally be considered deprecated. The boto module doesn't support most modern features (including controlling the metadata services) and as such support for managing them will not be added to the ec2 module.

This would however be a good feature to add to the ec2_instance module.

The ec2_instance module largely replaces the functionality of the ec2 module and I would recommend you migrate if possible. (ec2_instance doesn't support throughput yet either, however support would be possible)

Resolving patch:
diff --git a/plugins/modules/ec2_instance.py b/plugins/modules/ec2_instance.py
--- a/plugins/modules/ec2_instance.py
+++ b/plugins/modules/ec2_instance.py
@@ -276,6 +276,29 @@
     description:
       - The placement group that needs to be assigned to the instance
     type: str
+  metadata_options:
+    description:
+      - Modify the metadata options for the instance.
+      - See U(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html) for more information.
+      - The two suboptions I(http_endpoint) and I(http_tokens) are supported.
+    type: dict
+    version_added: 2.0.0
+    suboptions:
+      http_endpoint:
+        description:
+        - Enables or disables the HTTP metadata endpoint on instances.
+        - If specified a value of disabled, metadata of the instance will not be accessible.
+        choices: [enabled, disabled]
+        default: enabled
+        type: str
+      http_tokens:
+        description:
+        - Set the state of token usage for instance metadata requests.
+        - If the state is optional (v1 and v2), instance metadata can be retrieved with or without a signed token header on request.
+        - If the state is required (v2), a signed token header must be sent with any instance metadata retrieval requests.
+        choices: [optional, required]
+        default: optional
+        type: str
 
 extends_documentation_fragment:
 - amazon.aws.aws
@@ -385,6 +408,17 @@
     tags:
       Env: "eni_on"
     instance_type: t2.micro
+- name: start an instance with metadata options
+  amazon.aws.ec2_instance:
+    name: "public-metadataoptions-instance"
+    vpc_subnet_id: subnet-5calable
+    instance_type: t3.small
+    image_id: ami-123456
+    tags:
+      Environment: Testing
+    metadata_options:
+      http_endpoint: enabled
+      http_tokens: optional
 '''
 
 RETURN = '''
@@ -1195,6 +1229,12 @@ def build_top_level_options(params):
         spec['CpuOptions'] = {}
         spec['CpuOptions']['ThreadsPerCore'] = params.get('cpu_options').get('threads_per_core')
         spec['CpuOptions']['CoreCount'] = params.get('cpu_options').get('core_count')
+    if params.get('metadata_options'):
+        spec['MetadataOptions'] = {}
+        spec['MetadataOptions']['HttpEndpoint'] = params.get(
+            'metadata_options').get('http_endpoint')
+        spec['MetadataOptions']['HttpTokens'] = params.get(
+            'metadata_options').get('http_tokens')
     return spec
 
 
@@ -1737,6 +1777,9 @@ def main():
         instance_ids=dict(default=[], type='list', elements='str'),
         network=dict(default=None, type='dict'),
         volumes=dict(default=None, type='list', elements='dict'),
+        metadata_options=dict(type='dict', options=dict(
+            http_endpoint=dict(type='str', choices=['enabled', 'disabled'], default='enabled'),
+            http_tokens=dict(type='str', choices=['optional', 'required'], default='optional'))),
     )
     # running/present are synonyms
     # as are terminated/absent


Test patch:
diff --git a/tests/integration/targets/ec2_instance/inventory b/tests/integration/targets/ec2_instance/inventory
--- a/tests/integration/targets/ec2_instance/inventory
+++ b/tests/integration/targets/ec2_instance/inventory
@@ -4,6 +4,7 @@ version_fail_wrapper
 ebs_optimized
 block_devices
 cpu_options
+metadata_options
 default_vpc_tests
 external_resource_attach
 instance_no_wait
diff --git a/tests/integration/targets/ec2_instance/roles/ec2_instance/tasks/metadata_options.yml b/tests/integration/targets/ec2_instance/roles/ec2_instance/tasks/metadata_options.yml
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/ec2_instance/roles/ec2_instance/tasks/metadata_options.yml
@@ -0,0 +1,64 @@
+- block:
+  - name: "create t3.nano instance with metadata_options"
+    ec2_instance:
+      state: present
+      name: "{{ resource_prefix }}-test-t3nano-enabled-required"
+      image_id: "{{ ec2_ami_image }}"
+      tags:
+        TestId: "{{ ec2_instance_tag_TestId }}"
+      vpc_subnet_id: "{{ testing_subnet_a.subnet.id }}"
+      instance_type: t3.nano
+      metadata_options:
+          http_endpoint: enabled
+          http_tokens: required
+      wait: false
+    register: instance_creation
+
+  - name: "instance with metadata_options created with the right options"
+    assert:
+      that:
+        - instance_creation is success
+        - instance_creation is changed
+        - "'{{ instance_creation.spec.MetadataOptions.HttpEndpoint }}' == 'enabled'"
+        - "'{{ instance_creation.spec.MetadataOptions.HttpTokens }}' == 'required'"
+
+  - name: "modify metadata_options on existing instance"
+    ec2_instance:
+      state: present
+      name: "{{ resource_prefix }}-test-t3nano-enabled-required"
+      image_id: "{{ ec2_ami_image }}"
+      tags:
+        TestId: "{{ ec2_instance_tag_TestId }}"
+      vpc_subnet_id: "{{ testing_subnet_a.subnet.id }}"
+      instance_type: t3.nano
+      metadata_options:
+          http_endpoint: enabled
+          http_tokens: optional
+      wait: false
+    register: metadata_options_update
+    ignore_errors: yes
+
+  - name: "fact presented ec2 instance"
+    ec2_instance_info:
+      filters:
+        "tag:Name": "{{ resource_prefix }}-test-t3nano-enabled-required"
+    register: presented_instance_fact
+
+  - name: "modify metadata_options has no effect on existing instance"
+    assert:
+      that:
+        - metadata_options_update is success
+        - metadata_options_update is not changed
+        - "{{ presented_instance_fact.instances | length }} > 0"
+        - "'{{ presented_instance_fact.instances.0.state.name }}' in ['running','pending']"
+        - "'{{ presented_instance_fact.instances.0.metadata_options.http_endpoint }}' == 'enabled'"
+        - "'{{ presented_instance_fact.instances.0.metadata_options.http_tokens }}' == 'required'"
+
+  always:
+  - name: "Terminate metadata_options instances"
+    ec2_instance:
+      state: absent
+      filters:
+        "tag:TestId": "{{ ec2_instance_tag_TestId }}"
+      wait: yes
+    ignore_errors: yes