1#!/usr/local/bin/python3.8
2# -*- coding: utf-8 -*-
3# Copyright: Ansible Project
4# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
5
6from __future__ import absolute_import, division, print_function
7__metaclass__ = type
8
9
10DOCUMENTATION = '''
11---
12module: ec2_metadata_facts
13version_added: 1.0.0
14short_description: gathers facts (instance metadata) about remote hosts within EC2
15author:
16    - Silviu Dicu (@silviud)
17    - Vinay Dandekar (@roadmapper)
18description:
19    - This module fetches data from the instance metadata endpoint in EC2 as per
20      U(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html).
21    - The module must be called from within the EC2 instance itself.
22    - The module is configured to utilize the session oriented Instance Metadata Service v2 (IMDSv2)
23      U(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html).
24    - If the HttpEndpoint parameter
25      U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_ModifyInstanceMetadataOptions.html#API_ModifyInstanceMetadataOptions_RequestParameters)
26      is set to disabled for the EC2 instance, the module will return an error while retrieving a session token.
27notes:
28    - Parameters to filter on ec2_metadata_facts may be added later.
29'''
30
31EXAMPLES = '''
32# Gather EC2 metadata facts
33- amazon.aws.ec2_metadata_facts:
34
35- debug:
36    msg: "This instance is a t1.micro"
37  when: ansible_ec2_instance_type == "t1.micro"
38'''
39
40RETURN = '''
41ansible_facts:
42    description: Dictionary of new facts representing discovered properties of the EC2 instance.
43    returned: changed
44    type: complex
45    contains:
46        ansible_ec2_ami_id:
47            description: The AMI ID used to launch the instance.
48            type: str
49            sample: "ami-XXXXXXXX"
50        ansible_ec2_ami_launch_index:
51            description:
52                - If you started more than one instance at the same time, this value indicates the order in which the instance was launched.
53                - The value of the first instance launched is 0.
54            type: str
55            sample: "0"
56        ansible_ec2_ami_manifest_path:
57            description:
58                - The path to the AMI manifest file in Amazon S3.
59                - If you used an Amazon EBS-backed AMI to launch the instance, the returned result is unknown.
60            type: str
61            sample: "(unknown)"
62        ansible_ec2_ancestor_ami_ids:
63            description:
64                - The AMI IDs of any instances that were rebundled to create this AMI.
65                - This value will only exist if the AMI manifest file contained an ancestor-amis key.
66            type: str
67            sample: "(unknown)"
68        ansible_ec2_block_device_mapping_ami:
69            description: The virtual device that contains the root/boot file system.
70            type: str
71            sample: "/dev/sda1"
72        ansible_ec2_block_device_mapping_ebsN:
73            description:
74                - The virtual devices associated with Amazon EBS volumes, if any are present.
75                - Amazon EBS volumes are only available in metadata if they were present at launch time or when the instance was last started.
76                - The N indicates the index of the Amazon EBS volume (such as ebs1 or ebs2).
77            type: str
78            sample: "/dev/xvdb"
79        ansible_ec2_block_device_mapping_ephemeralN:
80            description: The virtual devices associated with ephemeral devices, if any are present. The N indicates the index of the ephemeral volume.
81            type: str
82            sample: "/dev/xvdc"
83        ansible_ec2_block_device_mapping_root:
84            description:
85                - The virtual devices or partitions associated with the root devices, or partitions on the virtual device,
86                  where the root (/ or C) file system is associated with the given instance.
87            type: str
88            sample: "/dev/sda1"
89        ansible_ec2_block_device_mapping_swap:
90            description: The virtual devices associated with swap. Not always present.
91            type: str
92            sample: "/dev/sda2"
93        ansible_ec2_fws_instance_monitoring:
94            description: "Value showing whether the customer has enabled detailed one-minute monitoring in CloudWatch."
95            type: str
96            sample: "enabled"
97        ansible_ec2_hostname:
98            description:
99                - The private IPv4 DNS hostname of the instance.
100                - In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0).
101            type: str
102            sample: "ip-10-0-0-1.ec2.internal"
103        ansible_ec2_iam_info:
104            description:
105                - If there is an IAM role associated with the instance, contains information about the last time the instance profile was updated,
106                  including the instance's LastUpdated date, InstanceProfileArn, and InstanceProfileId. Otherwise, not present.
107            type: complex
108            sample: ""
109            contains:
110                LastUpdated:
111                    description: The last time which InstanceProfile is associated with the Instance changed.
112                    type: str
113                InstanceProfileArn:
114                    description: The ARN of the InstanceProfile associated with the Instance.
115                    type: str
116                InstanceProfileId:
117                    description: The Id of the InstanceProfile associated with the Instance.
118                    type: str
119        ansible_ec2_iam_info_instanceprofilearn:
120            description: The IAM instance profile ARN.
121            type: str
122            sample: "arn:aws:iam::<account id>:instance-profile/<role name>"
123        ansible_ec2_iam_info_instanceprofileid:
124            description: IAM instance profile ID.
125            type: str
126            sample: ""
127        ansible_ec2_iam_info_lastupdated:
128            description: IAM info last updated time.
129            type: str
130            sample: "2017-05-12T02:42:27Z"
131        ansible_ec2_iam_instance_profile_role:
132            description: IAM instance role.
133            type: str
134            sample: "role_name"
135        ansible_ec2_iam_security_credentials_<role name>:
136            description:
137                - If there is an IAM role associated with the instance, role-name is the name of the role,
138                  and role-name contains the temporary security credentials associated with the role. Otherwise, not present.
139            type: str
140            sample: ""
141        ansible_ec2_iam_security_credentials_<role name>_accesskeyid:
142            description: IAM role access key ID.
143            type: str
144            sample: ""
145        ansible_ec2_iam_security_credentials_<role name>_code:
146            description: IAM code.
147            type: str
148            sample: "Success"
149        ansible_ec2_iam_security_credentials_<role name>_expiration:
150            description: IAM role credentials expiration time.
151            type: str
152            sample: "2017-05-12T09:11:41Z"
153        ansible_ec2_iam_security_credentials_<role name>_lastupdated:
154            description: IAM role last updated time.
155            type: str
156            sample: "2017-05-12T02:40:44Z"
157        ansible_ec2_iam_security_credentials_<role name>_secretaccesskey:
158            description: IAM role secret access key.
159            type: str
160            sample: ""
161        ansible_ec2_iam_security_credentials_<role name>_token:
162            description: IAM role token.
163            type: str
164            sample: ""
165        ansible_ec2_iam_security_credentials_<role name>_type:
166            description: IAM role type.
167            type: str
168            sample: "AWS-HMAC"
169        ansible_ec2_instance_action:
170            description: Notifies the instance that it should reboot in preparation for bundling.
171            type: str
172            sample: "none"
173        ansible_ec2_instance_id:
174            description: The ID of this instance.
175            type: str
176            sample: "i-XXXXXXXXXXXXXXXXX"
177        ansible_ec2_instance_identity_document:
178            description: JSON containing instance attributes, such as instance-id, private IP address, etc.
179            type: str
180            sample: ""
181        ansible_ec2_instance_identity_document_accountid:
182            description: ""
183            type: str
184            sample: "012345678901"
185        ansible_ec2_instance_identity_document_architecture:
186            description: Instance system architecture.
187            type: str
188            sample: "x86_64"
189        ansible_ec2_instance_identity_document_availabilityzone:
190            description: The Availability Zone in which the instance launched.
191            type: str
192            sample: "us-east-1a"
193        ansible_ec2_instance_identity_document_billingproducts:
194            description: Billing products for this instance.
195            type: str
196            sample: ""
197        ansible_ec2_instance_identity_document_devpayproductcodes:
198            description: Product codes for the launched AMI.
199            type: str
200            sample: ""
201        ansible_ec2_instance_identity_document_imageid:
202            description: The AMI ID used to launch the instance.
203            type: str
204            sample: "ami-01234567"
205        ansible_ec2_instance_identity_document_instanceid:
206            description: The ID of this instance.
207            type: str
208            sample: "i-0123456789abcdef0"
209        ansible_ec2_instance_identity_document_instancetype:
210            description: The type of instance.
211            type: str
212            sample: "m4.large"
213        ansible_ec2_instance_identity_document_kernelid:
214            description: The ID of the kernel launched with this instance, if applicable.
215            type: str
216            sample: ""
217        ansible_ec2_instance_identity_document_pendingtime:
218            description: The instance pending time.
219            type: str
220            sample: "2017-05-11T20:51:20Z"
221        ansible_ec2_instance_identity_document_privateip:
222            description:
223                - The private IPv4 address of the instance.
224                - In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0).
225            type: str
226            sample: "10.0.0.1"
227        ansible_ec2_instance_identity_document_ramdiskid:
228            description: The ID of the RAM disk specified at launch time, if applicable.
229            type: str
230            sample: ""
231        ansible_ec2_instance_identity_document_region:
232            description: The Region in which the instance launched.
233            type: str
234            sample: "us-east-1"
235        ansible_ec2_instance_identity_document_version:
236            description: Identity document version.
237            type: str
238            sample: "2010-08-31"
239        ansible_ec2_instance_identity_pkcs7:
240            description: Used to verify the document's authenticity and content against the signature.
241            type: str
242            sample: ""
243        ansible_ec2_instance_identity_rsa2048:
244            description: Used to verify the document's authenticity and content against the signature.
245            type: str
246            sample: ""
247        ansible_ec2_instance_identity_signature:
248            description: Data that can be used by other parties to verify its origin and authenticity.
249            type: str
250            sample: ""
251        ansible_ec2_instance_life_cycle:
252            description: The purchasing option of the instance.
253            type: str
254            sample: "on-demand"
255        ansible_ec2_instance_type:
256            description: The type of the instance.
257            type: str
258            sample: "m4.large"
259        ansible_ec2_local_hostname:
260            description:
261                - The private IPv4 DNS hostname of the instance.
262                - In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0).
263            type: str
264            sample: "ip-10-0-0-1.ec2.internal"
265        ansible_ec2_local_ipv4:
266            description:
267                - The private IPv4 address of the instance.
268                - In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0).
269            type: str
270            sample: "10.0.0.1"
271        ansible_ec2_mac:
272            description:
273                - The instance's media access control (MAC) address.
274                - In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0).
275            type: str
276            sample: "00:11:22:33:44:55"
277        ansible_ec2_metrics_vhostmd:
278            description: Metrics; no longer available.
279            type: str
280            sample: ""
281        ansible_ec2_network_interfaces_macs_<mac address>_device_number:
282            description:
283                - The unique device number associated with that interface. The device number corresponds to the device name;
284                  for example, a device-number of 2 is for the eth2 device.
285                - This category corresponds to the DeviceIndex and device-index fields that are used by the Amazon EC2 API and the EC2 commands for the AWS CLI.
286            type: str
287            sample: "0"
288        ansible_ec2_network_interfaces_macs_<mac address>_interface_id:
289            description: The elastic network interface ID.
290            type: str
291            sample: "eni-12345678"
292        ansible_ec2_network_interfaces_macs_<mac address>_ipv4_associations_<ip address>:
293            description: The private IPv4 addresses that are associated with each public-ip address and assigned to that interface.
294            type: str
295            sample: ""
296        ansible_ec2_network_interfaces_macs_<mac address>_ipv6s:
297            description: The IPv6 addresses associated with the interface. Returned only for instances launched into a VPC.
298            type: str
299            sample: ""
300        ansible_ec2_network_interfaces_macs_<mac address>_local_hostname:
301            description: The interface's local hostname.
302            type: str
303            sample: ""
304        ansible_ec2_network_interfaces_macs_<mac address>_local_ipv4s:
305            description: The private IPv4 addresses associated with the interface.
306            type: str
307            sample: ""
308        ansible_ec2_network_interfaces_macs_<mac address>_mac:
309            description: The instance's MAC address.
310            type: str
311            sample: "00:11:22:33:44:55"
312        ansible_ec2_network_interfaces_macs_<mac address>_owner_id:
313            description:
314                - The ID of the owner of the network interface.
315                - In multiple-interface environments, an interface can be attached by a third party, such as Elastic Load Balancing.
316                - Traffic on an interface is always billed to the interface owner.
317            type: str
318            sample: "01234567890"
319        ansible_ec2_network_interfaces_macs_<mac address>_public_hostname:
320            description:
321                - The interface's public DNS (IPv4). If the instance is in a VPC,
322                  this category is only returned if the enableDnsHostnames attribute is set to true.
323            type: str
324            sample: "ec2-1-2-3-4.compute-1.amazonaws.com"
325        ansible_ec2_network_interfaces_macs_<mac address>_public_ipv4s:
326            description: The Elastic IP addresses associated with the interface. There may be multiple IPv4 addresses on an instance.
327            type: str
328            sample: "1.2.3.4"
329        ansible_ec2_network_interfaces_macs_<mac address>_security_group_ids:
330            description: The IDs of the security groups to which the network interface belongs. Returned only for instances launched into a VPC.
331            type: str
332            sample: "sg-01234567,sg-01234568"
333        ansible_ec2_network_interfaces_macs_<mac address>_security_groups:
334            description: Security groups to which the network interface belongs. Returned only for instances launched into a VPC.
335            type: str
336            sample: "secgroup1,secgroup2"
337        ansible_ec2_network_interfaces_macs_<mac address>_subnet_id:
338            description: The ID of the subnet in which the interface resides. Returned only for instances launched into a VPC.
339            type: str
340            sample: "subnet-01234567"
341        ansible_ec2_network_interfaces_macs_<mac address>_subnet_ipv4_cidr_block:
342            description: The IPv4 CIDR block of the subnet in which the interface resides. Returned only for instances launched into a VPC.
343            type: str
344            sample: "10.0.1.0/24"
345        ansible_ec2_network_interfaces_macs_<mac address>_subnet_ipv6_cidr_blocks:
346            description: The IPv6 CIDR block of the subnet in which the interface resides. Returned only for instances launched into a VPC.
347            type: str
348            sample: ""
349        ansible_ec2_network_interfaces_macs_<mac address>_vpc_id:
350            description: The ID of the VPC in which the interface resides. Returned only for instances launched into a VPC.
351            type: str
352            sample: "vpc-0123456"
353        ansible_ec2_network_interfaces_macs_<mac address>_vpc_ipv4_cidr_block:
354            description: The IPv4 CIDR block of the VPC in which the interface resides. Returned only for instances launched into a VPC.
355            type: str
356            sample: "10.0.0.0/16"
357        ansible_ec2_network_interfaces_macs_<mac address>_vpc_ipv4_cidr_blocks:
358            description: The IPv4 CIDR block of the VPC in which the interface resides. Returned only for instances launched into a VPC.
359            type: str
360            sample: "10.0.0.0/16"
361        ansible_ec2_network_interfaces_macs_<mac address>_vpc_ipv6_cidr_blocks:
362            description: The IPv6 CIDR block of the VPC in which the interface resides. Returned only for instances launched into a VPC.
363            type: str
364            sample: ""
365        ansible_ec2_placement_availability_zone:
366            description: The Availability Zone in which the instance launched.
367            type: str
368            sample: "us-east-1a"
369        ansible_ec2_placement_region:
370            description: The Region in which the instance launched.
371            type: str
372            sample: "us-east-1"
373        ansible_ec2_product_codes:
374            description: Product codes associated with the instance, if any.
375            type: str
376            sample: "aw0evgkw8e5c1q413zgy5pjce"
377        ansible_ec2_profile:
378            description: EC2 instance hardware profile.
379            type: str
380            sample: "default-hvm"
381        ansible_ec2_public_hostname:
382            description:
383                - The instance's public DNS. If the instance is in a VPC, this category is only returned if the enableDnsHostnames attribute is set to true.
384            type: str
385            sample: "ec2-1-2-3-4.compute-1.amazonaws.com"
386        ansible_ec2_public_ipv4:
387            description: The public IPv4 address. If an Elastic IP address is associated with the instance, the value returned is the Elastic IP address.
388            type: str
389            sample: "1.2.3.4"
390        ansible_ec2_public_key:
391            description: Public key. Only available if supplied at instance launch time.
392            type: str
393            sample: ""
394        ansible_ec2_ramdisk_id:
395            description: The ID of the RAM disk specified at launch time, if applicable.
396            type: str
397            sample: ""
398        ansible_ec2_reservation_id:
399            description: The ID of the reservation.
400            type: str
401            sample: "r-0123456789abcdef0"
402        ansible_ec2_security_groups:
403            description:
404                - The names of the security groups applied to the instance. After launch, you can only change the security groups of instances running in a VPC.
405                - Such changes are reflected here and in network/interfaces/macs/mac/security-groups.
406            type: str
407            sample: "securitygroup1,securitygroup2"
408        ansible_ec2_services_domain:
409            description: The domain for AWS resources for the region; for example, amazonaws.com for us-east-1.
410            type: str
411            sample: "amazonaws.com"
412        ansible_ec2_services_partition:
413            description:
414                - The partition that the resource is in. For standard AWS regions, the partition is aws.
415                - If you have resources in other partitions, the partition is aws-partitionname.
416                - For example, the partition for resources in the China (Beijing) region is aws-cn.
417            type: str
418            sample: "aws"
419        ansible_ec2_spot_termination_time:
420            description:
421                - The approximate time, in UTC, that the operating system for your Spot instance will receive the shutdown signal.
422                - This item is present and contains a time value only if the Spot instance has been marked for termination by Amazon EC2.
423                - The termination-time item is not set to a time if you terminated the Spot instance yourself.
424            type: str
425            sample: "2015-01-05T18:02:00Z"
426        ansible_ec2_user_data:
427            description: The instance user data.
428            type: str
429            sample: "#!/bin/bash"
430'''
431
432import json
433import re
434import socket
435import time
436
437from ansible.module_utils.basic import AnsibleModule
438from ansible.module_utils._text import to_text
439from ansible.module_utils.urls import fetch_url
440from ansible.module_utils.six.moves.urllib.parse import quote
441
442socket.setdefaulttimeout(5)
443
444
445class Ec2Metadata(object):
446    ec2_metadata_token_uri = 'http://169.254.169.254/latest/api/token'
447    ec2_metadata_uri = 'http://169.254.169.254/latest/meta-data/'
448    ec2_sshdata_uri = 'http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key'
449    ec2_userdata_uri = 'http://169.254.169.254/latest/user-data/'
450    ec2_dynamicdata_uri = 'http://169.254.169.254/latest/dynamic/'
451
452    def __init__(self, module, ec2_metadata_token_uri=None, ec2_metadata_uri=None, ec2_sshdata_uri=None, ec2_userdata_uri=None, ec2_dynamicdata_uri=None):
453        self.module = module
454        self.uri_token = ec2_metadata_token_uri or self.ec2_metadata_token_uri
455        self.uri_meta = ec2_metadata_uri or self.ec2_metadata_uri
456        self.uri_user = ec2_userdata_uri or self.ec2_userdata_uri
457        self.uri_ssh = ec2_sshdata_uri or self.ec2_sshdata_uri
458        self.uri_dynamic = ec2_dynamicdata_uri or self.ec2_dynamicdata_uri
459        self._data = {}
460        self._token = None
461        self._prefix = 'ansible_ec2_%s'
462
463    def _fetch(self, url):
464        encoded_url = quote(url, safe='%/:=&?~#+!$,;\'@()*[]')
465        headers = {}
466        if self._token:
467            headers = {'X-aws-ec2-metadata-token': self._token}
468        response, info = fetch_url(self.module, encoded_url, headers=headers, force=True)
469
470        if info.get('status') in (401, 403):
471            self.module.fail_json(msg='Failed to retrieve metadata from AWS: {0}'.format(info['msg']), response=info)
472        elif info.get('status') not in (200, 404):
473            time.sleep(3)
474            # request went bad, retry once then raise
475            self.module.warn('Retrying query to metadata service. First attempt failed: {0}'.format(info['msg']))
476            response, info = fetch_url(self.module, encoded_url, headers=headers, force=True)
477            if info.get('status') not in (200, 404):
478                # fail out now
479                self.module.fail_json(msg='Failed to retrieve metadata from AWS: {0}'.format(info['msg']), response=info)
480        if response:
481            data = response.read()
482        else:
483            data = None
484        return to_text(data)
485
486    def _mangle_fields(self, fields, uri, filter_patterns=None):
487        filter_patterns = ['public-keys-0'] if filter_patterns is None else filter_patterns
488
489        new_fields = {}
490        for key, value in fields.items():
491            split_fields = key[len(uri):].split('/')
492            # Parse out the IAM role name (which is _not_ the same as the instance profile name)
493            if len(split_fields) == 3 and split_fields[0:2] == ['iam', 'security-credentials'] and ':' not in split_fields[2]:
494                new_fields[self._prefix % "iam-instance-profile-role"] = split_fields[2]
495            if len(split_fields) > 1 and split_fields[1]:
496                new_key = "-".join(split_fields)
497                new_fields[self._prefix % new_key] = value
498            else:
499                new_key = "".join(split_fields)
500                new_fields[self._prefix % new_key] = value
501        for pattern in filter_patterns:
502            for key in dict(new_fields):
503                match = re.search(pattern, key)
504                if match:
505                    new_fields.pop(key)
506        return new_fields
507
508    def fetch(self, uri, recurse=True):
509        raw_subfields = self._fetch(uri)
510        if not raw_subfields:
511            return
512        subfields = raw_subfields.split('\n')
513        for field in subfields:
514            if field.endswith('/') and recurse:
515                self.fetch(uri + field)
516            if uri.endswith('/'):
517                new_uri = uri + field
518            else:
519                new_uri = uri + '/' + field
520            if new_uri not in self._data and not new_uri.endswith('/'):
521                content = self._fetch(new_uri)
522                if field == 'security-groups' or field == 'security-group-ids':
523                    sg_fields = ",".join(content.split('\n'))
524                    self._data['%s' % (new_uri)] = sg_fields
525                else:
526                    try:
527                        dict = json.loads(content)
528                        self._data['%s' % (new_uri)] = content
529                        for (key, value) in dict.items():
530                            self._data['%s:%s' % (new_uri, key.lower())] = value
531                    except Exception:
532                        self._data['%s' % (new_uri)] = content  # not a stringified JSON string
533
534    def fix_invalid_varnames(self, data):
535        """Change ':'' and '-' to '_' to ensure valid template variable names"""
536        new_data = data.copy()
537        for key, value in data.items():
538            if ':' in key or '-' in key:
539                newkey = re.sub(':|-', '_', key)
540                new_data[newkey] = value
541                del new_data[key]
542
543        return new_data
544
545    def fetch_session_token(self, uri_token):
546        """Used to get a session token for IMDSv2"""
547        headers = {'X-aws-ec2-metadata-token-ttl-seconds': '60'}
548        response, info = fetch_url(self.module, uri_token, method='PUT', headers=headers, force=True)
549
550        if info.get('status') == 403:
551            self.module.fail_json(msg='Failed to retrieve metadata token from AWS: {0}'.format(info['msg']), response=info)
552        elif info.get('status') not in (200, 404):
553            time.sleep(3)
554            # request went bad, retry once then raise
555            self.module.warn('Retrying query to metadata service. First attempt failed: {0}'.format(info['msg']))
556            response, info = fetch_url(self.module, uri_token, method='PUT', headers=headers, force=True)
557            if info.get('status') not in (200, 404):
558                # fail out now
559                self.module.fail_json(msg='Failed to retrieve metadata token from AWS: {0}'.format(info['msg']), response=info)
560        if response:
561            token_data = response.read()
562        else:
563            token_data = None
564        return to_text(token_data)
565
566    def run(self):
567        self._token = self.fetch_session_token(self.uri_token)  # create session token for IMDS
568        self.fetch(self.uri_meta)  # populate _data with metadata
569        data = self._mangle_fields(self._data, self.uri_meta)
570        data[self._prefix % 'user-data'] = self._fetch(self.uri_user)
571        data[self._prefix % 'public-key'] = self._fetch(self.uri_ssh)
572
573        self._data = {}  # clear out metadata in _data
574        self.fetch(self.uri_dynamic)  # populate _data with dynamic data
575        dyndata = self._mangle_fields(self._data, self.uri_dynamic)
576        data.update(dyndata)
577        data = self.fix_invalid_varnames(data)
578
579        # Maintain old key for backwards compatibility
580        if 'ansible_ec2_instance_identity_document_region' in data:
581            data['ansible_ec2_placement_region'] = data['ansible_ec2_instance_identity_document_region']
582        return data
583
584
585def main():
586    module = AnsibleModule(
587        argument_spec={},
588        supports_check_mode=True,
589    )
590
591    ec2_metadata_facts = Ec2Metadata(module).run()
592    ec2_metadata_facts_result = dict(changed=False, ansible_facts=ec2_metadata_facts)
593
594    module.exit_json(**ec2_metadata_facts_result)
595
596
597if __name__ == '__main__':
598    main()
599