3

Edit: The env variable SYSTEM_VERSION I want to set can not be set before the task execution. It is created during a task (by reading a file for example which is only updated during playbook execution)

I have various tasks one of them sets an environment variable like so for example:

- name: Set current version
  shell: export SYSTEM_VERSION=$(cat system_version.txt)
  register: version_result

and then I wish to read in that environment variable for the same host like:

- name: Get the version name
  command: echo $SYSTEM_VERSION
  register: get_version_result

- name: Print variable
  debug:
    msg: "The operating system is {{ get_version_result.stdout }}"

but this always return an empty result. Why? Even if this is not the right way, how can I read a variable that I have set in one task with shell in another task for the same host later on?

KZiovas
  • 3,491
  • 3
  • 26
  • 47
  • 1
    A subprocess can't set environment variables in the environment of the parent process. If you want to set a variable that will be available in a subsequent task, set an ansible variable using e.g. the `set_fact` module (or the `register` directive if you're capturing the output of some command). – larsks Apr 06 '22 at 02:43

1 Answers1

3

Q: "Set env variable SYSTEM_VERSION by reading a file that is updated during playbook execution."

A: You can set environment in a play, role, block, or task. See Playbook Keywords. If you must read the value during the execution of the playbook set the environment to a block. For example, the playbook

- hosts: test_11
  gather_facts: false
  tasks:
    - file:
        state: directory
        path: system-versions
    - shell: uname -sr > system_version.txt
    - fetch:
        src: system_version.txt
        dest: system-versions
    - block:
        - command: 'echo $SYSTEM_VERSION'
          register: result
        - debug:
            var: result.stdout
      environment:
        SYSTEM_VERSION: "{{ lookup('file', system_version_path) }}"
      vars:
        system_version_path: "system-versions/{{ inventory_hostname}}/system_version.txt"

creates the dictionary system-versions where the files will be stored. The next task creates the file system_version.txt at the remote host

shell> ssh admin@test_11 cat system_version.txt
FreeBSD 13.0-RELEASE

The next task fetches the file and stores it in the directory system-versions/test_11

shell> cat system-versions/test_11/system_version.txt 
FreeBSD 13.0-RELEASE

The block then reads this file at the controller and sets the environment for the tasks in the block

TASK [debug] *********************************************
ok: [test_11] => 
  result.stdout: FreeBSD 13.0-RELEASE

The next option is to fetch the files in the 1st play and use them in the 2nd play. For example, given the files at the remote hosts
shell> ssh admin@test_11 cat system_version.txt
System A
shell> ssh admin@test_12 cat system_version.txt
System B

The playbook

- hosts: test_11,test_12
  gather_facts: false
  tasks:
    - file:
        state: directory
        path: system-versions
      run_once: true
    - fetch:
        src: system_version.txt
        dest: system-versions

- hosts: test_11,test_12
  gather_facts: false
  vars:
    system_version_path: "system-versions/{{ inventory_hostname}}/system_version.txt"
  environment:
    SYSTEM_VERSION: "{{ lookup('file', system_version_path) }}"
  tasks:
    - command: 'echo $SYSTEM_VERSION'
      register: result
    - debug:
        var: result.stdout

fetches the files in the 1st play

shell> cat system-versions/test_11/system_version.txt 
System A
shell> cat system-versions/test_12/system_version.txt 
System B

and set the environment in the 2nd play. Gives (abridged)

TASK [debug] ***********************************************
ok: [test_11] => 
  result.stdout: System A
ok: [test_12] => 
  result.stdout: System B

Notes

Ansible works by connecting to your nodes and pushing out scripts called “Ansible modules” to them. Most modules accept parameters that describe the desired state of the system. Ansible then executes these modules (over SSH by default), and removes them when finished. ...

  • You can take a look at what environment a module is working in. For example, given the inventory
shell> cat hosts
[test]
test_11

[test:vars]
ansible_connection=ssh
ansible_user=admin

the playbook

shell> cat playbook.yml
- hosts: test_11
  gather_facts: false
  tasks:
    - command: '/bin/sh -c set'
      register: result
    - debug:
        var: result.stdout

gives when you enable connection debugging (-vvvv)

shell> ansible-playbook -i hosts playbook.yml -vvvv

... <test_11> ESTABLISH SSH CONNECTION FOR USER: admin <test_11> SSH: EXEC ssh -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="admin"' -o ConnectTimeout=10 -o ControlPath=/export/home/vlado.config/.ansible/cp/9d96571b11 -tt test_11 '/bin/sh -c '"'"'/usr/local/bin/python3.8 /home/admin/.ansible/tmp/ansible-tmp-1649217448.1346543-1545740-216049530990371/AnsiballZ_command.py && sleep 0'"'"'' <test_11> (0, b'\r\n{"changed": true, "stdout": "BLOCKSIZE=K\nHOME=/home/admin\nIFS=$' \\t\n'\nLANG=C.UTF-8\nLOGNAME=admin\nMAIL=/var/mail/admin\nMM_CHARSET=UTF-8\nOPTIND=1\nPATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/home/admin/bin\nPPID=26056\nPS1='$ '\nPS2='> '\nPS4='+ '\nPWD=/home/admin\nSHELL=/bin/sh\nSSH_CLIENT='10.1.0.184 50874 22'\nSSH_CONNECTION='10.1.0.184 50874 10.1.0.61 22'\nSSH_TTY=/dev/pts/0\nTERM=xterm-256color\nUSER=admin", "stderr": "", "rc": 0, "cmd": ["/bin/sh", "-c", "set"], "start": "2022-04-06 03:57:29.500158", "end": "2022-04-06 03:57:29.516366", "delta": "0:00:00.016208", "msg": "", "invocation": {"module_args": {"_raw_params": "/bin/sh -c set", "_uses_shell": false, "warn": false, "stdin_add_newline": true, "strip_empty_ends": true, "argv": null, "chdir": null, "executable": null, "creates": null, "removes": null, "stdin": null}}}\r\n', b'OpenSSH_8.2p1 Ubuntu-4ubuntu0.2, OpenSSL 1.1.1f 31 Mar 2020\r\ndebug1: Reading configuration data /home/vlado/.ssh/config\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: /etc/ssh/ssh_config line 2: Applying options for *\r\ndebug1: auto-mux: Trying existing master\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug2: mux_client_hello_exchange: master version 4\r\ndebug3: mux_client_forwards: request forwardings: 0 local, 0 remote\r\ndebug3: mux_client_request_session: entering\r\ndebug3: mux_client_request_alive: entering\r\ndebug3: mux_client_request_alive: done pid = 1545744\r\ndebug3: mux_client_request_session: session request sent\r\ndebug3: mux_client_read_packet: read header failed: Broken pipe\r\ndebug2: Received exit status from master 0\r\nShared connection to test_11 closed.\r\n')

  result.stdout: |-
    BLOCKSIZE=K
    HOME=/home/admin
    IFS=$' \t
    '
    LANG=C.UTF-8
    LOGNAME=admin
    MAIL=/var/mail/admin
    MM_CHARSET=UTF-8
    OPTIND=1
    PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/home/admin/bin
    PPID=26008
    PS1='$ '
    PS2='> '
    PS4='+ '
    PWD=/home/admin
    SHELL=/bin/sh
    SSH_CLIENT='10.1.0.184 50834 22'
    SSH_CONNECTION='10.1.0.184 50834 10.1.0.61 22'
    SSH_TTY=/dev/pts/0
    TERM=xterm-256color
    USER=admin
  • Optionally, you can let Ansible collect the facts
shell> cat playbook.yml
- hosts: test_11
  gather_facts: true
  tasks:
    - debug:
        var: ansible_env

gives

  ansible_env:
    BLOCKSIZE: K
    HOME: /home/admin
    LANG: C.UTF-8
    LOGNAME: admin
    MAIL: /var/mail/admin
    MM_CHARSET: UTF-8
    PATH: /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/home/admin/bin
    PWD: /home/admin
    SHELL: /bin/sh
    SSH_CLIENT: 10.1.0.184 51008 22
    SSH_CONNECTION: 10.1.0.184 51008 10.1.0.61 22
    SSH_TTY: /dev/pts/0
    TERM: xterm-256color
    USER: admin

For example, the playbook

- hosts: test_11
  gather_facts: false
  tasks:
    - shell: 'echo $SHELL'
      register: result
    - debug:
        var: result.stdout
    - shell: 'export MYVAR1=test; echo $MYVAR1'
      register: result
    - debug:
        var: result.stdout
    - shell: 'echo $MYVAR1'
      register: result
    - debug:
        var: result.stdout

gives

TASK [shell] ********************************************************
changed: [test_11]

TASK [debug] ********************************************************
ok: [test_11] => 
  result.stdout: /bin/sh

TASK [shell] ********************************************************
changed: [test_11]

TASK [debug] ********************************************************
ok: [test_11] => 
  result.stdout: test

TASK [debug] ********************************************************
changed: [test_11]

TASK [debug] ********************************************************
ok: [test_11] => 
  result.stdout: ''
  • You can see that the environment variable hasn't been set persistently. Each module creates a new login. The solution is using the keyword environment. For example
- hosts: test_11
  gather_facts: false
  environment:
    MYVAR1: test
  tasks:
    - shell: 'echo $MYVAR1'
      register: result
    - debug:
        var: result.stdout

gives as expected

  result.stdout: test
Vladimir Botka
  • 58,131
  • 4
  • 32
  • 63
  • thanks, I short of found out what you describe but I thought that I was doing something wrong, I dint know that you simply cannot etc the variables like I was trying to. But my actual use case is slightly different. I want to calculate the value that I will assign to the environment variable during a task. I dont know it before hand. For example read it from a file in the host. So how can that be achieved? – KZiovas Apr 06 '22 at 07:22
  • Set the environment in a play, role, or block. I added an example. – Vladimir Botka Apr 06 '22 at 10:32