Repository: ansible/ansible

Issue:
Cannot pipe the vault password via stdin ansible-playbook when ran without a tty


##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
ansible-playbook

##### ANSIBLE VERSION
```
ansible 2.4.0.0
  python version = 2.7.9 (default, Jun 29 2016, 13:08:31) [GCC 4.9.2]
```

##### CONFIGURATION
DEFAULT_ROLES_PATH(/vagrant/ansible.cfg) = [u'/vagrant/common_roles', u'/vagrant/{{ roles_path }}']

##### OS / ENVIRONMENT
Debian jessie

##### SUMMARY
Since version 2.4.0.0 one cannot pipe the vault password via stdin into ansible-playbook anymore

##### STEPS TO REPRODUCE
This works in version <2.4.0.0
`echo "secret_vault_pass" | ansible-playbook  deploy_with_vaulted_file.yml --ask-vault-pass`

Since version 2.4.0.0 this results in
`ERROR! Attempting to decrypt but no vault secrets found`

Same happens with the new vault_id parameter:
`echo "secret_vault_pass" | ansible-playbook  deploy_with_vaulted_file.yml --vault-id @prompt`
results in the error:
`ERROR! Attempting to decrypt but no vault secrets found`
 
Is this on purpose or is it a bug?


Discussion:
I can reproduce that...

```
echo "secret_vault_pass" | ansible-playbook deploy_with_vaulted_file.yml --ask-vault-pass
```

... doesn't work in 2.4, but I can't reproduce the rest.

For me, on 2.4.0.0, I get:

```
[newswoop:F25:ansible (v2.4.0.0-1-branch %)]$ ansible --version
ansible 2.4.0.0 (v2.4.0.0-1-branch d14467b029) last updated 2017/09/20 16:07:02 (GMT -400)
  config file = /home/adrian/src/ansible/ansible.cfg
  configured module search path = [u'/home/adrian/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /home/adrian/src/ansible/lib/ansible
  executable location = /home/adrian/src/ansible/bin/ansible
  python version = 2.7.13 (default, May 10 2017, 20:04:28) [GCC 6.3.1 20161221 (Red Hat 6.3.1-1)]
[newswoop:F25:ansible (v2.4.0.0-1-branch %)]$ echo "password" | ansible-playbook -v ping_vault_files.yml --ask-vault-pass
Using /home/adrian/src/ansible/ansible.cfg as config file

PLAY [The first play] **********************************************************

TASK [copy] ********************************************************************
fatal: [localhost]: FAILED! => {"failed": true, "msg": "A vault password or secret must be specified to decrypt /home/adrian/src/ansible/files/foo_vault_12_default_id"}

PLAY RECAP *********************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=1   

```

But it also fails on 2.3, 2.2, and 2.1

2.3
```
[newswoop:F25:ansible (v2.3.0.0-1-branch %)]$ ansible --version
ansible 2.3.0.0 (detached HEAD ref: refs/) last updated 2017/09/28 11:48:46 (GMT -400)
  config file = /home/adrian/src/ansible/ansible.cfg
  configured module search path = Default w/o overrides
  python version = 2.7.13 (default, May 10 2017, 20:04:28) [GCC 6.3.1 20161221 (Red Hat 6.3.1-1)]

[newswoop:F25:ansible (v2.3.0.0-1-branch %)]$ echo "password" | ansible-playbook -v ping_vault_files.yml --ask-vault-pass
Using /home/adrian/src/ansible/ansible.cfg as config file
Vault password:  [ERROR]: User interrupted execution
```

2.2
```
[newswoop:F25:ansible (v2.2.0.0-1-branch % u=)]$ ansible --version
ansible 2.2.0.0 (v2.2.0.0-1-branch cdec853e37) last updated 2017/09/28 11:50:02 (GMT -400)
  lib/ansible/modules/core: (detached HEAD fe9c56a003) last updated 2017/09/28 11:50:30 (GMT -400)
  lib/ansible/modules/extras: (detached HEAD f564e77a08) last updated 2017/09/28 11:50:31 (GMT -400)
  config file = /home/adrian/src/ansible/ansible.cfg
  configured module search path = Default w/o overrides

[newswoop:F25:ansible (v2.2.0.0-1-branch % u=)]$ echo "password" | ansible-playbook -v ping_vault_files.yml --ask-vault-passUsing /home/adrian/src/ansible/ansible.cfg as config file
Vault password: 
```

2.1

```
[newswoop:F25:ansible (v2.1.0.0-1-branch % u=)]$ ansible --version
ansible 2.1.0.0 (v2.1.0.0-1-branch f14fe1c8d2) last updated 2017/09/28 11:51:40 (GMT -400)
  lib/ansible/modules/core: (detached HEAD 4fe583e29b) last updated 2017/09/28 11:51:45 (GMT -400)
  lib/ansible/modules/extras: (detached HEAD 709114d55f) last updated 2017/09/28 11:51:45 (GMT -400)
  config file = /home/adrian/src/ansible/ansible.cfg
  configured module search path = Default w/o overrides

[newswoop:F25:ansible (v2.1.0.0-1-branch % u=)]$ echo "password" | ansible-playbook -v ping_vault_files.yml --ask-vault-passUsing /home/adrian/src/ansible/ansible.cfg as config file
Vault password:  [ERROR]: User interrupted execution
```
@sebastianneubauer paste the 'ansible --version' and an example output of ansible 2.3 working?

```
echo "secret_vault_pass" | ansible-playbook some_example_playbook --ask-vault-pass
```

I can reproduce the 'ERROR! Attempting to decrypt but no vault secrets found' error with 2.4, if the playbook I am using is vault encrypted.

In this case, 'ping_noop.yml' has been vault-encrypted with the password 'password'

```
[newswoop:F25:ansible (stable-2.4 % u=)]$ echo "password" | ansible-playbook ping_noop.yml --ask-vault-pass
ERROR! Attempting to decrypt but no vault secrets found
```

But it also doesn't work on 2.3:

```
[newswoop:F25:ansible (v2.3.2.0-1-branch %)]$ ansible --version
ansible 2.3.2.0 (detached HEAD ref: refs/) last updated 2017/09/28 12:17:25 (GMT -400)
  config file = /home/adrian/src/ansible/ansible.cfg
  configured module search path = Default w/o overrides
  python version = 2.7.13 (default, May 10 2017, 20:04:28) [GCC 6.3.1 20161221 (Red Hat 6.3.1-1)]
[newswoop:F25:ansible (v2.3.2.0-1-branch %)]$ echo "password" | ansible-playbook ping_noop.yml --ask-vault-pass
Vault password:  [ERROR]: User interrupted execution
```

Or 2.2:

```
[newswoop:F25:ansible (v2.2.0.0-1-branch % u=)]$ ansible --version
ansible 2.2.0.0 (v2.2.0.0-1-branch cdec853e37) last updated 2017/09/28 11:50:02 (GMT -400)
  lib/ansible/modules/core: (detached HEAD 4fe583e29b) last updated 2017/09/28 11:51:45 (GMT -400)
  lib/ansible/modules/extras: (detached HEAD 709114d55f) last updated 2017/09/28 11:51:45 (GMT -400)
  config file = /home/adrian/src/ansible/ansible.cfg
  configured module search path = Default w/o overrides
[newswoop:F25:ansible (v2.2.0.0-1-branch % u=)]$ echo "password" | ansible-playbook ping_noop.yml --ask-vault-pass
Vault password:  [ERROR]: User interrupted execution
```

Or 2.1:

```
[newswoop:F25:ansible (v2.1.0.0-1-branch % u=)]$ ansible --version
ansible 2.1.0.0 (v2.1.0.0-1-branch f14fe1c8d2) last updated 2017/09/28 11:51:40 (GMT -400)
  lib/ansible/modules/core: (detached HEAD 4fe583e29b) last updated 2017/09/28 11:51:45 (GMT -400)
  lib/ansible/modules/extras: (detached HEAD 709114d55f) last updated 2017/09/28 11:51:45 (GMT -400)
  config file = /home/adrian/src/ansible/ansible.cfg
  configured module search path = Default w/o overrides
[newswoop:F25:ansible (v2.1.0.0-1-branch % u=)]$ echo "password" | ansible-playbook ping_noop.yml --ask-vault-pass
Vault password:  [ERROR]: User interrupted execution
```
crazy, in my local bash, similar to you I cannot get this sdtin/echo thing to work also with 2.3...But in Jenkins it works
```
03:11:56 + echo ****
03:11:56 + ansible-playbook -i dev deploy.yml -vvvv --ask-vault-pass
03:11:56 Using /home/jenkins/workspace/ansible_deploy/ansible.cfg as config file
03:11:56 /usr/lib/python2.7/getpass.py:83: GetPassWarning: Can not control echo on the terminal.
03:11:56   passwd = fallback_getpass(prompt, stream)
03:11:56 Warning: Password input may be echoed.
03:11:56 Vault password: 
03:11:56 Loading callback plugin default of type stdout, v2.0 from /home/jenkins/workspace/venv/local/lib/python2.7/site-packages/ansible/plugins/callback/__init__.pyc
03:11:56 
03:11:56 PLAYBOOK: deploy.yml *******************************************
03:11:56 3 plays in deploy.yml
03:11:56 
03:11:56 PLAY [dev] ************************************************************
```

And this is the snipped from my Jenkinsfile:
```
                    withCredentials([[$class: 'StringBinding', credentialsId: 'ansible-vault-pass', variable: 'ANSIBLE_VAULT_PWD']]) {
                        sh '''#!/bin/bash -xe
                                source ../../venv/bin/activate
                                echo $ANSIBLE_VAULT_PWD | ansible-playbook -i dev deploy.yml -vvvv --ask-vault-pass
                            '''
                        }

```

So I must admit: maybe it was pure luck that it worked so far for me. 
I am fine with closing this bug, as it seems not to be a bug..

But is there a good way to inject the vault password via environment variables? echoing the environment variable into a file and using this seems really to be a very bad workaround...needing a script which reads out the environment variable also feels like a big mess, as I would have to have such a "boilerplate" script in all my ansible projects... 
@alikins I think that after the merge of #22756 (Support of multiple vault passwords), the vault password is no longer captured by the Python `getpass.getpass` "secure password prompt" function. 

See
 * https://github.com/ansible/ansible/pull/22756/files#diff-bdd6c847fae8976ab8a7259d0b583f34L175
 * https://github.com/ansible/ansible/blob/a14d0f3586617f678b2843a25b521243a99cd589/lib/ansible/parsing/vault/__init__.py (no idea, how the interactive prompt is currently done...)
@gildegoma It still uses getpass.getpass() eventually.

https://github.com/ansible/ansible/blob/devel/lib/ansible/parsing/vault/__init__.py#L280  PromptVaultSecret classes uses ansible.utils.display.Display.prompt(private=True) and display.Display.prompt() uses getpass.getpass()
2.4.0 checks if sys.stdin.isatty() to decide if it should show the interactive prompt (eventually via getpass.getpass()). But for cases that aren't via tty (likely vagrant and jenkins cases), that means it never calls PromptVautlSecret() or getpass.getpass().

But... getpass.getpass() will fallback to reading from stdin if it is not a tty. Since 2.3 will always call getpass.getpass(), it will eventually fallback to stdin via getpass() if there isnt a tty. 

Testing against 2.4 without a tty:

```
[newswoop:F25:ansible (devel % u=)]$ cat password
password
[newswoop:F25:ansible (devel % u=)]$ ansible-vault view --vault-id password ping_noop.yml 
---
- name: The first play
  hosts: localhost
  tasks:
    - name: ping
      ping:

    - name: some debug
      debug:
        msg: "some debug message"
[newswoop:F25:ansible (devel % u=)]$ setsid sh -c 'tty; echo password |ansible-playbook -vvvvv --ask-vault-pass ping_noop.yml' < /dev/null > log 2>&1
[newswoop:F25:ansible (devel % u=)]$ cat log
not a tty
ansible-playbook 2.5.0 (devel 5bf9f271b3) last updated 2017/10/09 14:49:25 (GMT -400)
  config file = /home/adrian/src/ansible/ansible.cfg
  configured module search path = [u'/home/adrian/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /home/adrian/src/ansible/lib/ansible
  executable location = /home/adrian/src/ansible/bin/ansible-playbook
  python version = 2.7.13 (default, May 10 2017, 20:04:28) [GCC 6.3.1 20161221 (Red Hat 6.3.1-1)]
Using /home/adrian/src/ansible/ansible.cfg as config file
setting up inventory plugins
 [WARNING]: Unable to parse /etc/ansible/hosts as an inventory source
 [WARNING]: No inventory was parsed, only implicit localhost is available
 [WARNING]: Could not match supplied host pattern, ignoring: all
 [WARNING]: provided hosts list is empty, only localhost is available
ERROR! Attempting to decrypt but no vault secrets found
```

But running same command with 2.3 without a tty works as mentioned above:

```
[newswoop:F25:ansible (stable-2.3 % u-3)]$ ansible --version
ansible 2.3.2.0 (stable-2.3 fd37f27d2b) last updated 2017/09/28 09:57:20 (GMT -400)
  config file = /home/adrian/src/ansible/ansible.cfg
  configured module search path = Default w/o overrides
  python version = 2.7.13 (default, May 10 2017, 20:04:28) [GCC 6.3.1 20161221 (Red Hat 6.3.1-1)]
[newswoop:F25:ansible (stable-2.3 % u-3)]$ setsid sh -c 'tty; echo password |ansible-playbook -vvvvv --ask-vault-pass ping_noop.yml' < /dev/null > log 2>&1
[newswoop:F25:ansible (stable-2.3 % u-3)]$ cat log 
not a tty
Using /home/adrian/src/ansible/ansible.cfg as config file
/usr/lib64/python2.7/getpass.py:83: GetPassWarning: Can not control echo on the terminal.
  passwd = fallback_getpass(prompt, stream)
Warning: Password input may be echoed.
Vault password: 
 [WARNING]: Host file not found: /etc/ansible/hosts
 [WARNING]: provided hosts list is empty, only localhost is available
Loading callback plugin default of type stdout, v2.0 from /home/adrian/src/ansible/lib/ansible/plugins/callback/__init__.pyc

PLAYBOOK: ping_noop.yml ********************************************************
1 plays in ping_noop.yml

PLAY [The first play] **********************************************************

TASK [Gathering Facts] *********************************************************
Using module file /home/adrian/src/ansible/lib/ansible/modules/system/setup.py
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: adrian
<127.0.0.1> EXEC /bin/sh -c 'echo ~ && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/adrian/.ansible/tmp/ansible-tmp-1507577928.63-197522661787151 `" && echo ansible-tmp-1507577928.63-197522661787151="` echo /home/adrian/.ansible/tmp/ansible-tmp-1507577928.63-197522661787151 `" ) && sleep 0'
<127.0.0.1> PUT /tmp/tmpqGQ3Uf TO /home/adrian/.ansible/tmp/ansible-tmp-1507577928.63-197522661787151/setup.py
<127.0.0.1> EXEC /bin/sh -c 'chmod u+x /home/adrian/.ansible/tmp/ansible-tmp-1507577928.63-197522661787151/ /home/adrian/.ansible/tmp/ansible-tmp-1507577928.63-197522661787151/setup.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '/usr/bin/python /home/adrian/.ansible/tmp/ansible-tmp-1507577928.63-197522661787151/setup.py; rm -rf "/home/adrian/.ansible/tmp/ansible-tmp-1507577928.63-197522661787151/" > /dev/null 2>&1 && sleep 0'
```

Fix seems to be just to remove the isatty() check. 

stable-2.4 + rm that check:

``` 
[newswoop:F25:ansible (stable-2.4 % u-9)]$ setsid sh -c 'tty; echo password |ansible-playbook -vvvvv --ask-vault-pass ping_noop.yml' < /dev/null > log 2>&1
[newswoop:F25:ansible (stable-2.4 % u-9)]$ cat log 
not a tty
ansible-playbook 2.4.1.0 (stable-2.4 e886da782b) last updated 2017/10/06 11:51:06 (GMT -400)
  config file = /home/adrian/src/ansible/ansible.cfg
  configured module search path = [u'/home/adrian/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /home/adrian/src/ansible/lib/ansible
  executable location = /home/adrian/src/ansible/bin/ansible-playbook
  python version = 2.7.13 (default, May 10 2017, 20:04:28) [GCC 6.3.1 20161221 (Red Hat 6.3.1-1)]
Using /home/adrian/src/ansible/ansible.cfg as config file
/usr/lib64/python2.7/getpass.py:83: GetPassWarning: Can not control echo on the terminal.
  passwd = fallback_getpass(prompt, stream)
Warning: Password input may be echoed.
Vault password: 
setting up inventory plugins
 [WARNING]: Unable to parse /etc/ansible/hosts as an inventory source
 [WARNING]: No inventory was parsed, only implicit localhost is available
 [WARNING]: Could not match supplied host pattern, ignoring: all
 [WARNING]: provided hosts list is empty, only localhost is available
Found a vault_id (default) in the vaulttext
We have a secret associated with vault id (default), will try to use to decrypt /home/adrian/src/ansible/ping_noop.yml
Trying to use vault secret=(<ansible.parsing.vault.PromptVaultSecret object at 0x7f00a7668550>) id=default to decrypt /home/adrian/src/ansible/ping_noop.yml
Trying secret <ansible.parsing.vault.PromptVaultSecret object at 0x7f00a7668550> for vault_id=default
decrypt succesful with secret=<ansible.parsing.vault.PromptVaultSecret object at 0x7f00a7668550> and vault_id=default
Loading callback plugin default of type stdout, v2.0 from /home/adrian/src/ansible/lib/ansible/plugins/callback/__init__.pyc

PLAYBOOK: ping_noop.yml ********************************************************
1 plays in ping_noop.yml

PLAY [The first play] **********************************************************

TASK [Gathering Facts] *********************************************************

<.. unrelated debug output removed ..>

<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: adrian
<127.0.0.1> EXEC /bin/sh -c 'echo ~ && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/adrian/.ansible/tmp/ansible-tmp-1507578002.19-60916837434826 `" && echo ansible-tmp-1507578002.19-60916837434826="` echo /home/adrian/.ansible/tmp/ansible-tmp-1507578002.19-60916837434826 `" ) && sleep 0'
<127.0.0.1> PUT /tmp/tmp0igBRi TO /home/adrian/.ansible/tmp/ansible-tmp-1507578002.19-60916837434826/setup.py
<127.0.0.1> EXEC /bin/sh -c 'chmod u+x /home/adrian/.ansible/tmp/ansible-tmp-1507578002.19-60916837434826/ /home/adrian/.ansible/tmp/ansible-tmp-1507578002.19-60916837434826/setup.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '/usr/bin/python /home/adrian/.ansible/tmp/ansible-tmp-1507578002.19-60916837434826/setup.py; rm -rf "/home/adrian/.ansible/tmp/ansible-tmp-1507578002.19-60916837434826/" > /dev/null 2>&1 && sleep 0'

```

``` patch
diff --git lib/ansible/cli/__init__.py lib/ansible/cli/__init__.py
index 3660e4b..706e252 100644 some_name@prompt_ask_vault_pass --vault-id other_name@prompt_ask_vault_pass will be a little
--- lib/ansible/cli/__init__.pynce it will use the old format without the vault id in the prompt
+++ lib/ansible/cli/__init__.py.4 % u-9)]$ fg
@@ -259,10 +259,6 @@ class CLI(with_metaclass(ABCMeta, object)):
             vault_id_name, vault_id_value = CLI.split_vault_id(vault_id_slug)
             if vault_id_value in ['prompt', 'prompt_ask_vault_pass']:
 
-                # prompts cant/shouldnt work without a tty, so dont add prompt secrets
-                if not sys.stdin.isatty():
-                    continue
-
                 # --vault-id some_name@prompt_ask_vault_pass --vault-id other_name@prompt_ask_vault_pass will be a little
                 # confusing since it will use the old format without the vault id in the prompt
                 built_vault_id = vault_id_name or C.DEFAULT_VAULT_IDENTITY
```

Resolving patch:
diff --git a/lib/ansible/cli/__init__.py b/lib/ansible/cli/__init__.py
--- a/lib/ansible/cli/__init__.py
+++ b/lib/ansible/cli/__init__.py
@@ -213,7 +213,9 @@ def build_vault_ids(vault_ids, vault_password_files=None,
 
         # if an action needs an encrypt password (create_new_password=True) and we dont
         # have other secrets setup, then automatically add a password prompt as well.
-        if ask_vault_pass or (auto_prompt and not vault_ids):
+        # prompts cant/shouldnt work without a tty, so dont add prompt secrets
+        if ask_vault_pass or (not vault_ids and auto_prompt):
+
             id_slug = u'%s@%s' % (C.DEFAULT_VAULT_IDENTITY, u'prompt_ask_vault_pass')
             vault_ids.append(id_slug)
 
@@ -260,10 +262,6 @@ def setup_vault_secrets(loader, vault_ids, vault_password_files=None,
             vault_id_name, vault_id_value = CLI.split_vault_id(vault_id_slug)
             if vault_id_value in ['prompt', 'prompt_ask_vault_pass']:
 
-                # prompts cant/shouldnt work without a tty, so dont add prompt secrets
-                if not sys.stdin.isatty():
-                    continue
-
                 # --vault-id some_name@prompt_ask_vault_pass --vault-id other_name@prompt_ask_vault_pass will be a little
                 # confusing since it will use the old format without the vault id in the prompt
                 built_vault_id = vault_id_name or C.DEFAULT_VAULT_IDENTITY


Test patch:
diff --git a/test/integration/targets/vault/runme.sh b/test/integration/targets/vault/runme.sh
--- a/test/integration/targets/vault/runme.sh
+++ b/test/integration/targets/vault/runme.sh
@@ -34,6 +34,45 @@ WRONG_RC=$?
 echo "rc was $WRONG_RC (1 is expected)"
 [ $WRONG_RC -eq 1 ]
 
+# Use linux setsid to test without a tty. No setsid if osx/bsd though...
+if [ -x "$(command -v setsid)" ]; then
+    # tests related to https://github.com/ansible/ansible/issues/30993
+    CMD='ansible-playbook -vvvvv --ask-vault-pass test_vault.yml'
+    setsid sh -c "echo test-vault-password|${CMD}" < /dev/null > log 2>&1 && :
+    WRONG_RC=$?
+    cat log
+    echo "rc was $WRONG_RC (0 is expected)"
+    [ $WRONG_RC -eq 0 ]
+
+    setsid sh -c 'tty; ansible-vault --ask-vault-pass -vvvvv view test_vault.yml' < /dev/null > log 2>&1 && :
+    WRONG_RC=$?
+    echo "rc was $WRONG_RC (1 is expected)"
+    [ $WRONG_RC -eq 1 ]
+    cat log
+
+    setsid sh -c 'tty; echo passbhkjhword|ansible-playbook -vvvvv --ask-vault-pass test_vault.yml' < /dev/null > log 2>&1 && :
+    WRONG_RC=$?
+    echo "rc was $WRONG_RC (1 is expected)"
+    [ $WRONG_RC -eq 1 ]
+    cat log
+
+    setsid sh -c 'tty; echo test-vault-password |ansible-playbook -vvvvv --ask-vault-pass test_vault.yml' < /dev/null > log 2>&1
+    echo $?
+    cat log
+
+    setsid sh -c 'tty; echo test-vault-password|ansible-playbook -vvvvv --ask-vault-pass test_vault.yml' < /dev/null > log 2>&1
+    echo $?
+    cat log
+
+    setsid sh -c 'tty; echo test-vault-password |ansible-playbook -vvvvv --ask-vault-pass test_vault.yml' < /dev/null > log 2>&1
+    echo $?
+    cat log
+
+    setsid sh -c 'tty; echo test-vault-password|ansible-vault --ask-vault-pass -vvvvv view vaulted.inventory' < /dev/null > log 2>&1
+    echo $?
+    cat log
+fi
+
 # old format
 ansible-vault view "$@" --vault-password-file vault-password-ansible format_1_0_AES.yml
 
diff --git a/test/units/cli/test_cli.py b/test/units/cli/test_cli.py
--- a/test/units/cli/test_cli.py
+++ b/test/units/cli/test_cli.py
@@ -46,6 +46,13 @@ def test_version_info_gitinfo(self):
 
 
 class TestCliBuildVaultIds(unittest.TestCase):
+    def setUp(self):
+        self.tty_patcher = patch('ansible.cli.sys.stdin.isatty', return_value=True)
+        self.mock_isatty = self.tty_patcher.start()
+
+    def tearDown(self):
+        self.tty_patcher.stop()
+
     def test(self):
         res = cli.CLI.build_vault_ids(['foo@bar'])
         self.assertEqual(res, ['foo@bar'])
@@ -102,6 +109,7 @@ def test_everything(self):
                                       ask_vault_pass=True,
                                       create_new_password=True,
                                       auto_prompt=False)
+
         self.assertEqual(set(res), set(['blip@prompt', 'baz@prompt_ask_vault_pass',
                                         'default@prompt_ask_vault_pass',
                                         'some-password-file', 'qux@another-password-file',
@@ -115,8 +123,14 @@ def setUp(self):
         self.tty_patcher = patch('ansible.cli.sys.stdin.isatty', return_value=True)
         self.mock_isatty = self.tty_patcher.start()
 
+        self.display_v_patcher = patch('ansible.cli.display.verbosity', return_value=6)
+        self.mock_display_v = self.display_v_patcher.start()
+        cli.display.verbosity = 5
+
     def tearDown(self):
         self.tty_patcher.stop()
+        self.display_v_patcher.stop()
+        cli.display.verbosity = 0
 
     def test(self):
         res = cli.CLI.setup_vault_secrets(None, None, auto_prompt=False)
@@ -144,7 +158,8 @@ def test_prompt(self, mock_prompt_secret):
 
         res = cli.CLI.setup_vault_secrets(loader=self.fake_loader,
                                           vault_ids=['prompt1@prompt'],
-                                          ask_vault_pass=True)
+                                          ask_vault_pass=True,
+                                          auto_prompt=False)
 
         self.assertIsInstance(res, list)
         matches = vault.match_secrets(res, ['prompt1'])
@@ -156,15 +171,19 @@ def test_prompt(self, mock_prompt_secret):
     def test_prompt_no_tty(self, mock_prompt_secret):
         self.mock_isatty.return_value = False
         mock_prompt_secret.return_value = MagicMock(bytes=b'prompt1_password',
-                                                    vault_id='prompt1')
+                                                    vault_id='prompt1',
+                                                    name='bytes_should_be_prompt1_password',
+                                                    spec=vault.PromptVaultSecret)
         res = cli.CLI.setup_vault_secrets(loader=self.fake_loader,
                                           vault_ids=['prompt1@prompt'],
-                                          ask_vault_pass=True)
+                                          ask_vault_pass=True,
+                                          auto_prompt=False)
 
         self.assertIsInstance(res, list)
-        self.assertEqual(len(res), 0)
+        self.assertEqual(len(res), 2)
         matches = vault.match_secrets(res, ['prompt1'])
-        self.assertEquals(len(matches), 0)
+        self.assertIn('prompt1', [x[0] for x in matches])
+        self.assertEquals(len(matches), 1)
 
     @patch('ansible.cli.get_file_vault_secret')
     @patch('ansible.cli.PromptVaultSecret')
@@ -192,7 +211,7 @@ def _assert_ids(self, vault_id_names, res, password=b'prompt1_password'):
         self.assertIsInstance(res, list)
         len_ids = len(vault_id_names)
         matches = vault.match_secrets(res, vault_id_names)
-        self.assertEqual(len(res), len_ids)
+        self.assertEqual(len(res), len_ids, 'len(res):%s does not match len_ids:%s' % (len(res), len_ids))
         self.assertEqual(len(matches), len_ids)
         for index, prompt in enumerate(vault_id_names):
             self.assertIn(prompt, [x[0] for x in matches])
@@ -214,6 +233,7 @@ def test_multiple_prompts(self, mock_prompt_secret):
 
     @patch('ansible.cli.PromptVaultSecret')
     def test_multiple_prompts_and_ask_vault_pass(self, mock_prompt_secret):
+        self.mock_isatty.return_value = False
         mock_prompt_secret.return_value = MagicMock(bytes=b'prompt1_password',
                                                     vault_id='prompt1')
 
@@ -223,6 +243,8 @@ def test_multiple_prompts_and_ask_vault_pass(self, mock_prompt_secret):
                                                      'prompt3@prompt_ask_vault_pass'],
                                           ask_vault_pass=True)
 
+        # We provide some vault-ids and secrets, so auto_prompt shouldn't get triggered,
+        # so there is
         vault_id_names = ['prompt1', 'prompt2', 'prompt3', 'default']
         self._assert_ids(vault_id_names, res)
 
@@ -242,14 +264,27 @@ def test_default_file_vault(self, mock_prompt_secret,
         res = cli.CLI.setup_vault_secrets(loader=self.fake_loader,
                                           vault_ids=[],
                                           create_new_password=False,
-                                          ask_vault_pass=True)
+                                          ask_vault_pass=False)
 
         self.assertIsInstance(res, list)
         matches = vault.match_secrets(res, ['default'])
         # --vault-password-file/DEFAULT_VAULT_PASSWORD_FILE is higher precendce than prompts
         # if the same vault-id ('default') regardless of cli order since it didn't matter in 2.3
+
+        self.assertEqual(matches[0][1].bytes, b'file1_password')
+        self.assertEqual(len(matches), 1)
+
+        res = cli.CLI.setup_vault_secrets(loader=self.fake_loader,
+                                          vault_ids=[],
+                                          create_new_password=False,
+                                          ask_vault_pass=True,
+                                          auto_prompt=True)
+
+        self.assertIsInstance(res, list)
+        matches = vault.match_secrets(res, ['default'])
         self.assertEqual(matches[0][1].bytes, b'file1_password')
         self.assertEqual(matches[1][1].bytes, b'prompt1_password')
+        self.assertEqual(len(matches), 2)
 
     @patch('ansible.cli.get_file_vault_secret')
     @patch('ansible.cli.PromptVaultSecret')
diff --git a/test/units/cli/test_vault.py b/test/units/cli/test_vault.py
--- a/test/units/cli/test_vault.py
+++ b/test/units/cli/test_vault.py
@@ -32,6 +32,13 @@
 
 
 class TestVaultCli(unittest.TestCase):
+    def setUp(self):
+        self.tty_patcher = patch('ansible.cli.sys.stdin.isatty', return_value=False)
+        self.mock_isatty = self.tty_patcher.start()
+
+    def tearDown(self):
+        self.tty_patcher.stop()
+
     def test_parse_empty(self):
         cli = VaultCLI([])
         self.assertRaisesRegexp(errors.AnsibleOptionsError,
@@ -46,14 +53,18 @@ def test_parse_view_file(self):
         cli = VaultCLI(args=['ansible-vault', 'view', '/dev/null/foo'])
         cli.parse()
 
-    def test_view_missing_file_no_secret(self):
+    @patch('ansible.cli.vault.VaultCLI.setup_vault_secrets')
+    def test_view_missing_file_no_secret(self, mock_setup_vault_secrets):
+        mock_setup_vault_secrets.return_value = []
         cli = VaultCLI(args=['ansible-vault', 'view', '/dev/null/foo'])
         cli.parse()
         self.assertRaisesRegexp(errors.AnsibleOptionsError,
                                 "A vault password is required to use Ansible's Vault",
                                 cli.run)
 
-    def test_encrypt_missing_file_no_secret(self):
+    @patch('ansible.cli.vault.VaultCLI.setup_vault_secrets')
+    def test_encrypt_missing_file_no_secret(self, mock_setup_vault_secrets):
+        mock_setup_vault_secrets.return_value = []
         cli = VaultCLI(args=['ansible-vault', 'encrypt', '/dev/null/foo'])
         cli.parse()
         self.assertRaisesRegexp(errors.AnsibleOptionsError,