Troubleshooting Ansible connection issues

How to handle this:

managed-host | UNREACHABLE! => {  
    "changed": false,
    "msg": "Failed to connect to the host via ssh.",
    "unreachable": true
}

or other connection error when you manage hosts with Ansible?
You try ssh managed-host and get into your box, but Ansible can't. Be prepared that it might be actually not connection error, but something else. Let's see what you can do.

Assumptions

You have checked the connectivity with your target host (let's call it managed-host) and execution of ssh managed-host 'echo ok' from your terminal gives you ok result.
It is recommended to use passwordless authentication.

Try raw module

Ansible heavily relies on Phyton and many other tricks to make modules work as sleek as possible. But sometimes things go wrong. There is a raw module that:

Executes a low-down and dirty SSH command, not going through the module subsystem

Execute ansible managed-host -m raw -a 'echo ok' – this calls raw module with echo ok as parameter: Ansible tries to execute simple echo command on the remote host without any Python wrappers.
If you get this result:

managed-host | SUCCESS | rc=0 >>  
ok  

then ssh connection is OK and you should look for error somewhere else. If you still get ssh error, then it is a connection related issue (e.g. Ubuntu 16.04 has no Python2 by default).

Double check your inventory

You may have something wrong in your configuration.
Set inventory file explicitly with -i switch:

ansible managed-host -i my_hosts -m ping  

There is also possibility to use inline inventory with comma-trick:

ansible managed-host -i 'managed-host,' -m ping  

This will force Ansible to use inventory made of single entry managed-host.

Use verbose output

Execute your command again with -vvv switch (verbose, level 3).
For ad-hoc: ansible managed-host -vvv -m ping
For playbook: ansible-playbook -vvv myplaybook.yml
You'll get the SSH command that Ansible try to execute:

<managed-host> SSH: EXEC ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o ControlPath=/Users/demo/.ansible/cp/ansible-ssh-%h-%p-%r managed-host '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1473166522.52-177854868483455 `" && echo ansible-tmp-1473166522.52-177854868483455="` echo $HOME/.ansible/tmp/ansible-tmp-1473166522.52-177854868483455 `" ) && sleep 0'"'"''  

Copy the long line after SSH: EXEC and execute it from your terminal. In the example above it is:

ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o ControlPath=/Users/demo/.ansible/cp/ansible-ssh-%h-%p-%r managed-host '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1473166522.52-177854868483455 `" && echo ansible-tmp-1473166522.52-177854868483455="` echo $HOME/.ansible/tmp/ansible-tmp-1473166522.52-177854868483455 `" ) && sleep 0'"'"''  

If the error is still unclear, you may also replace -q (quiet) with -vvv (verbose) switch. If you don't have -q in your command, just add -vvv straight after ssh:

ssh -vvv -C -o ControlMas.... <remaining line>  
Enable debug mode

There is a debug mode in Ansible. You can enable it with Ansible configuration option. The easiest way to do so is to use environment variable. Execute your command as follows:

ANSIBLE_DEBUG=1 ansible managed-host -m ping  

This may help to trace down the problem.

Overwriting ssh_args

Be prepared that by setting ssh_args with your custom value you overwrite default connection parameters. Check current defaults in the documentation and append them to you custom value.