Repository: ansible/ansible

Issue:
aws_ec2 inventory plugin to return private_id_address instead of private_dns_name
<!---
Verify first that your issue/request is not already reported on GitHub.
THIS FORM WILL BE READ BY A MACHINE, COMPLETE ALL SECTIONS AS DESCRIBED.
Also test if the latest release, and devel branch are affected too.
ALWAYS add information AFTER (OUTSIDE) these html comments.
Otherwise it may end up being automatically closed by our bot. -->

##### SUMMARY
<!--- Explain the problem briefly -->
When usin aws_ec2 inventory plugin and hosts have only private IP addresses it would be preferable to have the plugin return their IP addresses instead of dns names because internal dns names won't be resolved outside VPC.

##### ISSUE TYPE
 - Feature Idea

##### COMPONENT NAME
<!--- Insert, BELOW THIS COMMENT, the name of the module, plugin, task or feature.
Do not include extra details here, e.g. "vyos_command" not "the network module vyos_command" or the full path-->
aws_ec2
##### ANSIBLE VERSION
<!--- Paste, BELOW THIS COMMENT, verbatim output from "ansible --version" between quotes below -->
```
ansible 2.5.3
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/jussi/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/dist-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.12 (default, Dec  4 2017, 14:50:18) [GCC 5.4.0 20160609]
```

##### CONFIGURATION
<!--- If using Ansible 2.4 or above, paste, BELOW THIS COMMENT, the results of "ansible-config dump --only-changed"
Otherwise, mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).-->
DEFAULT_STDOUT_CALLBACK(/etc/ansible/ansible.cfg) = debug
##### OS / ENVIRONMENT
<!--- Mention, BELOW THIS COMMENT, the OS you are running Ansible from, and the OS you are
managing, or say "N/A" for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.-->
N/A
##### STEPS TO REPRODUCE
<!--- For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used. -->

<!--- Paste example playbooks or commands between quotes below -->
Command
```
ansible-inventory -i inventories/dev.aws_ec2.yml --list
```
outputs (stripped):
```json
                "private_dns_name": "ip-10-200-100-125.eu-west-1.compute.internal", 
                "private_ip_address": "10.200.100.125", 
```

and command
```
ansible-inventory -i inventories/dev.aws_ec2.yml --graph
```

outputs
```
@all:
  |--@aws_ec2:
  |  |--ip-10-200-100-125.eu-west-1.compute.internal
  |--@ungrouped:
```

when inventory config is:

```yaml
plugin: aws_ec2
boto_profile: dev
regions:
  - eu-west-1
filters:
  tag:Name: dev
```

Running playbook with this configuration gives the following error message

```
ansible-playbook -v -i inventories/dev.aws_ec2.yml deploy.yml

PLAY [all] ***********************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************************************************************************************************************************************
fatal: [ip-10-200-100-125.eu-west-1.compute.internal]: UNREACHABLE! => {
    "changed": false, 
    "unreachable": true
}

MSG:

Failed to connect to the host via ssh: ssh: connect to host ip-10-200-100-125.eu-west-1.compute.internal port 22: Connection timed out
```

This happens because internal dns name ip-10-200-100-125.eu-west-1.compute.internal is not resolvable outside the VPC.


<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->

I would wish that there were configuration that would make this inventory plugin to output private_ip_address.


##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->
```

```



Discussion:
Files identified in the description:
* [lib/ansible/plugins/inventory/aws_ec2.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/inventory/aws_ec2.py)

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 --->
Hi @jnupponen. If you want to set the inventory_hostname (which will modify the output of ansible -i inv.aws_ec2.yml --graph) to only use private IP addresses you can use:
```
plugin: aws_ec2
boto_profile: shertel
regions:
  - us-east-1
hostnames:
  # list in order of hostname precedence; if no var in the list exists for the host do not add the host to inventory
  - private-ip-address
```
If you want to keep your displayed hostnames the same but use the IP for ansible_host you can use constructed features (you'll be able to see that var added if you use --list rather than --graph):
```
plugin: aws_ec2
boto_profile: shertel
regions:
  - us-east-1
compose:
  ansible_host: private_ip_address
```
I'll open a PR to add the compose example to the aws_ec2 docs since that isn't documented yet.

Resolving patch:
diff --git a/lib/ansible/plugins/inventory/aws_ec2.py b/lib/ansible/plugins/inventory/aws_ec2.py
--- a/lib/ansible/plugins/inventory/aws_ec2.py
+++ b/lib/ansible/plugins/inventory/aws_ec2.py
@@ -56,6 +56,14 @@
 '''
 
 EXAMPLES = '''
+
+# Minimal example using environment vars or instance role credentials
+# Fetch all hosts in us-east-1, the hostname is the public DNS if it exists, otherwise the private IP address
+plugin: aws_ec2
+regions:
+  - us-east-1
+
+# Example using filters, ignoring permission errors, and specifying the hostname precedence
 plugin: aws_ec2
 boto_profile: aws_profile
 regions: # populate inventory with instances in these regions
@@ -71,11 +79,19 @@
   instance.group-id: sg-xxxxxxxx
 # ignores 403 errors rather than failing
 strict_permissions: False
+# note: I(hostnames) sets the inventory_hostname. To modify ansible_host without modifying
+# inventory_hostname use compose (see example below).
 hostnames:
   - tag:Name=Tag1,Name=Tag2  # return specific hosts only
   - tag:CustomDNSName
   - dns-name
+  - private-ip-address
 
+# Example using constructed features to create groups and set ansible_host
+plugin: aws_ec2
+regions:
+  - us-east-1
+  - us-west-1
 # keyed_groups may be used to create custom groups
 strict: False
 keyed_groups:
@@ -97,6 +113,11 @@
   # create a group per region e.g. aws_region_us_east_2
   - key: placement.region
     prefix: aws_region
+# set individual variables with compose
+compose:
+  # use the private IP address to connect to the host
+  # (note: this does not modify inventory_hostname, which is set via I(hostnames))
+  ansible_host: private_ip_address
 '''
 
 from ansible.errors import AnsibleError, AnsibleParserError


Test patch: