Search code examples
ansible

How to set an Ansible variable for all plays/hosts?


This question is NOT answered. Someone mentioned environment variables. Can you elaborate on this?

5/28/2024 - Simplified the question (below): This is an oracle problem. I have 4 PCs. I need program 1 run on the one machine that has Drive E. Out of the remaining 3 that don't have drive E, I need program 2 run on ONLY one of the 3. For the other 2, don't run anything.

This seems like a simple problem, but not in ansible. It keeps coming up. Especially in error conditions. I need a global variable. One that I can set when processing one host play, then check at a later time with another host. In a nutshell, so I can branch later in the playbook, depending on the variable.

We have no control over custom software installation, but if it is installed, we have to put different software on other machines. To top it off, the installations vary, depending on the VM folder. My kingdom for a global var.

The scope of variables relates ONLY to the current ansible_hostname. Yes, we have group_vars/all.yml as globals, but we can't set them in a play. If I set a variable, no other host's play/task can see it. I understand the scope of variables, but I want to SET a global variable that can be read throughout all playbook plays. The actual implementation is unimportant but variable access is (important).

My Question: Is there a way to set a variable that can be checked when running a different task on another host? Something like setGlobalSpaceVar(myvar, true)? I know there isn't any such method, but I'm looking for a work-around. Rephrasing: set a variable in one tasks for one host, then later in another task for another host, read that variable.

The only way I can think of is to change a file on the controller, but that seems bogus.

An example

The following relates to oracle backups and our local executable, but I'm keeping it generic. For below - Yes, I can do a run_once, but that won't answer my question. This variable access problem keeps coming up in different contexts.

I have 4 xyz servers. I have 2 programs that need to be executed, but only on 2 different machines. I don't know which. The settings may be change for different VM environments.

Our programOne is run on the server that has a drive E. I can find which server has drive E using ansible and do the play accordingly whenever I set a variable (driveE_machine). It only applies to that host. For that, the other 3 machines won't have driveE_machine set. In a later play, I need to execute another program on ONLY one of the other 3 machines. That means I need to set a variable that can be read by the other 2 hosts that didn't run the 2nd program. I'm not sure how to do it.

Inventory file:

[xyz]
serverxyz[1:4].private.mystuff

Playbook example:

---
- name: stackoverflow variable question
  hosts: xyz
  gather_facts: no
  serial: 1
  tasks:
      - name: find out who has drive E
         win_shell: dir e:\
         register: adminPage
         ignore_errors: true

       # This sets a variable that can only be read for that host
      - name: set fact driveE_machine when rc is 0
        set_fact:
           driveE_machine: "{{inventory_hostname}}"
        when: adminPage.rc == 0

       - name: run program 1
         include: tasks/program1.yml
         when: driveE_machine is defined

       # program2.yml executes program2 and needs to set some kind of variable
       # so this include can only be executed once for the other 3 machines 
       # (not one that has driveE_machine defined and ???
       - name: run program 2
         include: tasks/program2.yml
         when: driveE_machine is undefined and ???
         # please don't say run_once: true - that won't solve my variable access question

Is there a way to set a variable that can be checked when running a task on another host?


Solution

  • No sure what you actually want, but you can set a fact for every host in a play with a single looped task (some simulation of global variable):

    playbook.yml

    ---
    - hosts: mytest
      gather_facts: no
      vars:
      tasks:
        # Set myvar fact for every host in a play
        - set_fact:
            myvar: "{{ inventory_hostname }}"
          delegate_to: "{{ item }}"
          with_items: "{{ play_hosts }}"
          run_once: yes
        # Ensure that myvar is a name of the first host
        - debug:
            msg: "{{ myvar }}"
    

    hosts

    [mytest]
    aaa ansible_connection=local
    bbb ansible_connection=local
    ccc ansible_connection=local
    

    result

    PLAY [mytest] ******************
    META: ran handlers
    
    TASK [set_fact] ******************
    ok: [aaa -> aaa] => (item=aaa) => {"ansible_facts": {"myvar": "aaa"}, "ansible_facts_cacheable": false, "changed": false, "failed": false, "item": "aaa"}
    ok: [aaa -> bbb] => (item=bbb) => {"ansible_facts": {"myvar": "aaa"}, "ansible_facts_cacheable": false, "changed": false, "failed": false, "item": "bbb"}
    ok: [aaa -> ccc] => (item=ccc) => {"ansible_facts": {"myvar": "aaa"}, "ansible_facts_cacheable": false, "changed": false, "failed": false, "item": "ccc"}
    
    TASK [debug] ******************
    ok: [aaa] => {
        "msg": "aaa"
    }
    ok: [bbb] => {
        "msg": "aaa"
    }
    ok: [ccc] => {
        "msg": "aaa"
    }