Ansible Playbook – How to Pass Variable from Previous Host to Next Target Host

ansibleansible-playbook

I want to pass variable called ssh_user from the first play on ansible playbook to next different host play. I have tried to use "set_fact" but seems like it's still can't retrieve variable from the previous play.

Below is my ansible playbook code:

- hosts: localhost

  vars_prompt:

    - name: target_host
      prompt: Target Host IP
      private: false
    - name: auth_method
      prompt: Authentication method (password/key)
      private: false

  vars:

    ssh_user: "{{ ssh_user_out.user_input }}"
    ssh_pass: "{{ ssh_pass_out.user_input }}"
    ssh_keypath: "{{ ssh_keypath_out.user_input }}"
    
  tasks:

    - block:
        - pause:
            prompt: SSH Username
          register: ssh_user_out
        - pause:
            prompt: SSH Password
            echo: false
          register: ssh_pass_out
        - set_fact:
            ssh_user: "{{ ssh_user }}"
            ssh_pass: "{{ ssh_pass }}"
      when: auth_method == 'password'

    - block:
        - pause:
            prompt: SSH Username
          register: ssh_user_out
        - pause:
            prompt: Private Key Path
          register: ssh_keypath_out
        - set_fact:
            ssh_user: "{{ ssh_user }}"
            ssh_keypath: "{{ ssh_keypath }}"
      when: auth_method == 'key'
    - add_host:
        name: "{{ target_host }}"
        groups: dynamically_created_hosts



- name: Download files from FTP on target host
  hosts: dynamically_created_hosts
  tasks:
    - name: set the facts per host
      set_fact:
        ssh_user: "{{hostvars['localhost']['ssh_user']}}"
        ssh_pass: "{{hostvars['localhost']['ssh_pass']}}"
        ssh_keypath: "{{hostvars['localhost']['ssh_keypath']}}"
  gather_facts: false
  remote_user: "{{ ssh_user }}"
  
  become: true
  become_user: root
  become_method: sudo

  tasks:
    - name: Create upgrade directory if not exist
      file:
        path: /root/upgrade
        state: directory

    - name: Download file from FTP
      get_url:
        url: "ftp://something.com/{{ file_path }}"
        dest: "/root/upgrade/{{ file_path }}"
        force: yes
        timeout: 30
        remote_src: yes
        validate_certs: false
        url_username: "{{ ftp_username }}"
        url_password: "{{ ftp_password }}"
      register: download_result
      ignore_errors: true

    - name: Check if file downloaded successfully
      debug:
        msg: "File '{{ file_path }}' downloaded successfully."
      when: download_result is success

    - name: Display error message if file download failed
      debug:
        msg: "Failed to download file '{{ file_path }}'. Please check the file path or FTP credentials."
      when: download_result is failed and file_path != 'q'

    - name: Exit playbook if 'q' is pressed
      meta: end_play
      when: file_path == 'q'

Below is the error:

WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
[WARNING]: While constructing a mapping from /Related/ansible/upgrade/upgrade.yml, line 50, column 3, found a duplicate
dict key (tasks). Using last defined value only.
Target Host IP: 192.168.10.1
Authentication method (password/key): password

PLAY [localhost] *******************************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************
ok: [localhost]

TASK [pause] ***********************************************************************************************************************************************
[pause]
SSH Username:
ok: [localhost]

TASK [pause] ***********************************************************************************************************************************************
[pause]
SSH Password (output is hidden):
ok: [localhost]

TASK [set_fact] ********************************************************************************************************************************************
ok: [localhost]

TASK [pause] ***********************************************************************************************************************************************
skipping: [localhost]

TASK [pause] ***********************************************************************************************************************************************
skipping: [localhost]

TASK [set_fact] ********************************************************************************************************************************************
skipping: [localhost]

TASK [add_host] ********************************************************************************************************************************************
changed: [localhost]

PLAY [Download files from FTP on target host] **************************************************************************************************************

TASK [Create upgrade directory if not exist] ***************************************************************************************************************
fatal: [192.168.10.1]: FAILED! => {"msg": "The field 'remote_user' has an invalid value, which includes an undefined variable. The error was: 'ssh_user' is undefined. 'ssh_user' is undefined. 'ssh_user' is undefined. 'ssh_user' is undefined"}

PLAY RECAP *************************************************************************************************************************************************
192.168.10.1               : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
localhost                  : ok=5    changed=1    unreachable=0    failed=0    skipped=3    rescued=0    ignored=0   

as you can see even after I tried to call vars from previous play using set_fact and hostvars , it's still showing
" {"msg": "The field 'remote_user' has an invalid value, which includes an undefined variable. The error was: 'ssh_user' is undefined. 'ssh_user' is undefined. 'ssh_user' is undefined. 'ssh_user' is undefined"}"

I have tried several ways on the similar case but none is working so far.

Appreciate your help!

Best Answer

Declare the variables when adding the host. See the first example. For example,

    - add_host:
        name: "{{ target_host }}"
        groups: dynamically_created_hosts
        ssh_user: "{{ ssh_user|d('UNDEF') }}"
        ssh_pass: "{{ ssh_pass|d('UNDEF') }}"
        ssh_keypath: "{{ ssh_keypath|d('UNDEF') }}"

Set default values because some of the variables will be undefined.


  • Example of a complete playbook for testing
shell> cat pb.yml
- hosts: localhost

  vars_prompt:

    - name: target_host
      prompt: Target Host IP
      private: false
    - name: auth_method
      prompt: Authentication method (password/key)
      private: false

  vars:

    ssh_user: "{{ ssh_user_out.user_input }}"
    ssh_pass: "{{ ssh_pass_out.user_input }}"
    
  tasks:

    - block:
        - pause:
            prompt: SSH Username
          register: ssh_user_out
        - pause:
            prompt: SSH Password
            echo: false
          register: ssh_pass_out
      when: auth_method == 'password'

    - add_host:
        name: "{{ target_host }}"
        groups: dynamically_created_hosts
        ssh_user: "{{ ssh_user|d('UNDEF') }}"
        ssh_pass: "{{ ssh_pass|d('UNDEF') }}"
        ssh_keypath: "{{ ssh_keypath|d('UNDEF') }}"

- hosts: dynamically_created_hosts

  tasks:

    - debug:
        msg: |
          ssh_user: {{ ssh_user }}
          ssh_pass: {{ ssh_pass }}
          ssh_keypath: {{ ssh_keypath }}

gives

shell> ansible-playbook pb.yml
Target Host IP: 10.1.0.10
Authentication method (password/key): password

PLAY [localhost] ******************************************************************************

TASK [pause] **********************************************************************************
[pause]
SSH Username:
admin^Mok: [localhost]

TASK [pause] **********************************************************************************
[pause]
SSH Password (output is hidden):
ok: [localhost]

TASK [add_host] *******************************************************************************
changed: [localhost]

PLAY [dynamically_created_hosts] **************************************************************

TASK [debug] **********************************************************************************
ok: [10.1.0.10] => 
  msg: |-
    ssh_user: admin
    ssh_pass: 1234
    ssh_keypath: UNDEF

PLAY RECAP ************************************************************************************
10.1.0.10: ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
localhost: ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

  • Use set_fact to put variables into the hostvars. For example, the playbook below also does the job
shell> cat pb.yml
- hosts: localhost

  vars_prompt:

    - name: target_host
      prompt: Target Host IP
      private: false
    - name: auth_method
      prompt: Authentication method (password/key)
      private: false
    
  tasks:

    - block:
        - pause:
            prompt: SSH Username
          register: ssh_user_out
        - pause:
            prompt: SSH Password
            echo: false
          register: ssh_pass_out
        - set_fact:
            ssh_user: "{{ ssh_user_out.user_input }}"
            ssh_pass: "{{ ssh_pass_out.user_input }}"
      when: auth_method == 'password'

    - add_host:
        name: "{{ target_host }}"
        groups: dynamically_created_hosts

- hosts: dynamically_created_hosts

  vars:

    ssh_user: "{{ hostvars.localhost.ssh_user|d('UNDEF') }}"
    ssh_pass: "{{ hostvars.localhost.ssh_pass|d('UNDEF') }}"
    ssh_keypath: "{{ hostvars.localhost.ssh_keypath|d('UNDEF') }}"

  tasks:

    - debug:
        msg: |
          ssh_user: {{ ssh_user }}
          ssh_pass: {{ ssh_pass }}
          ssh_keypath: {{ ssh_keypath }}

gives

shell> ansible-playbook pb.yml
Target Host IP: 10.1.0.10
Authentication method (password/key): password

PLAY [localhost] ******************************************************************************

TASK [pause] **********************************************************************************
[pause]
SSH Username:
admin^Mok: [localhost]

TASK [pause] **********************************************************************************
[pause]
SSH Password (output is hidden):
ok: [localhost]

TASK [set_fact] *******************************************************************************
ok: [localhost]

TASK [add_host] *******************************************************************************
changed: [localhost]

PLAY [dynamically_created_hosts] **************************************************************

TASK [debug] **********************************************************************************
ok: [10.1.0.10] => 
  msg: |-
    ssh_user: admin
    ssh_pass: 1234
    ssh_keypath: UNDEF

PLAY RECAP ************************************************************************************
10.1.0.10: ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
localhost: ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0