89

I have this play.yml

---
- hosts: localhost
    
  tasks:
    - include: apache.yml

My apache.yml file looks like this:

vars:
  url: http://example.com/apache
    
- name: Download apache
  shell: wget {{ url }} 

This is giving me an error.

If I remove vars then it works. But, I want to keep the variable inside the included tasks file, so that I can keep different variables for different tasks separate.

β.εηοιτ.βε
  • 33,893
  • 13
  • 69
  • 83
user1994660
  • 5,253
  • 11
  • 30
  • 33

7 Answers7

95

NOTE: Using set_fact as described below sets a fact/variable onto the remote servers that the task is running against. This fact/variable will then persist across subsequent tasks for the entire duration of your playbook.

Also, these facts are immutable (for the duration of the playbook), and cannot be changed once set.


ORIGINAL ANSWER

Use set_fact before your task to set facts which seem interchangeable with variables:

- name: Set Apache URL
  set_fact:
    apache_url: 'http://example.com/apache'

- name: Download Apache
  shell: wget {{ apache_url }}

See http://docs.ansible.com/set_fact_module.html for the official word.

slm
  • 15,396
  • 12
  • 109
  • 124
dodgio
  • 2,169
  • 21
  • 19
  • 1
    It seems to be a better solution since you can use the fact as a variable. – douglaslps Feb 13 '15 at 12:29
  • 14
    Note this subtle difference compared with variables: _This module allows setting new variables. Variables are set on a host-by-host basis just like facts discovered by the setup module. **These variables will survive between plays.**_ – rrauenza Oct 07 '15 at 16:13
  • 9
    Sidenote: it seems, that you can't change a variable in place using `set_fact`... so if the the name is already taken by a variable it won't modify that... which can be error prone to setting a variable to the same name on a higher level of a playbook. So be sure you won't do that (e.g. use a potentially unique name for example, not 'item'). – masu Jun 30 '16 at 10:38
  • 5
    Note on your note... `Also, these facts are immutable (for the duration of the playbook), and cannot be changed once set.` -- This isn't strictly true. You can overwrite a fact created with `set_fact` in an earlier task, you just can't overwrite a variable set in any other way... As an example, if you loop through a set of tasks, and at the start of each loop iteration you use `set_fact: loop_item='{{ item }}'`, then use `debug: msg='{{ loop_item }}'`, you'll see the *fact* changes with each loop iteration. – Jack_Hu Mar 28 '19 at 02:46
81

Just move the variable definition inside inside your task:

- name: Download apache
  shell: wget {{ url }}
  vars:
    url: http://example.com/apache
β.εηοιτ.βε
  • 33,893
  • 13
  • 69
  • 83
appleitung
  • 1,043
  • 7
  • 10
14

Variable definitions are meant to be used in tasks. But if you want to include them in tasks probably use the register directive. Like this:

- name: Define variable in task.
  shell: echo "http://www.my.url.com"
  register: url

- name: Download apache
  shell: wget {{ item }}
  with_items: url.stdout

You can also look at roles as a way of separating tasks depending on the different roles roles. This way you can have separate variables for each of one of your roles. For example you may have a url variable for apache1 and a separate url variable for the role apache2.

Rico
  • 58,485
  • 12
  • 111
  • 141
  • 1
    I thought register only worked with output from a command? How can I make a static variable in a role's `roles//tasks/main.yml` ? – ThorSummoner Mar 05 '15 at 22:15
  • @ThorSummoner you mean this like a regular variable? http://docs.ansible.com/playbooks_variables.html#variables-defined-in-a-playbook and maybe this? http://docs.ansible.com/playbooks_roles.html#role-default-variables – Rico Mar 10 '15 at 22:04
  • 1
    This solution is a total overkill. U send the var value over ssh to the remote host then retrieve it back, to use it locally on the control node!! – Samha' Nov 05 '19 at 02:53
5

In Your example, apache.yml is tasklist, but not playbook

In depends on desired architecture, You can do one of:

1. Convert apache.yml to role. Then define tasks in roles/apache/tasks/mail.yml and variables in roles/apache/defaults/mail.yml (vars in defaults can be overriden when role applied)

play.yml :

---
- hosts: localhost
  connection: local
  sudo: false

  roles:
     - apache

roles/apache/defaults/main.yml :

---
url: czxcxz

roles/apache/tasks/main.yml :

---
- name: Download apache
  shell: wget {{url}} 

2. Set vars in play.yml playbook

play.yml :

---
- hosts: localhost
  connection: local
  sudo: false

  vars:
    url: czxcxz

  tasks:
     - include: apache.yml

apache.yml :

- name: Download apache
  shell: wget {{url}} 

3. Make apache.yml complete playbook and import it in play.yml as playbook

play.yml :

---
- name: Configure Apache
  import_playbook: apache.yml

apache.yml :

---
- name: Configure Apache
  hosts: localhost
  connection: local
  sudo: false

  vars:
    url: czxcxz

  tasks:
  - name: Download apache
    shell: wget {{url}} 

4. Import variables from separate file

play.yml :

---
- hosts: localhost

  tasks:
  - include: apache.yml

apache.yml :

---
- name: Import apache vars
  # Static import var-file with single var look ugly
  include_vars: apache-vars.yml

- name: Download apache
  shell: wget {{ url }} 

apache-vars.yml :

---
url: http://example.com/apache

5. Consider put variables to host_var or group_var

host_vars/localhost.yml : or host_vars/localhost/apache.yml :

---
url: http://example.com/apache
mmv-ru
  • 219
  • 6
  • 13
1

If you have multiple tasks use a block instead

- name: Scope change (Kind of)
  block:
  - name: Download apache
    shell: wget {{ url }} 
  - name: Debug
    debug: 
      msg: "It ran: wget {{ url }}"
  vars:
      url: http://example.com/apache  

In general, avoid using set_fact . If you do use it, its high precedence will override most other constructs including vars: . It can cause very difficult to debug side effects, even outside the role, or include where it was used.

0

If you need to have local variables that only persist through specific task, you can do this:

- name: My Task
  vars:
    my_var: 123

This can be useful, when you want to have more reusable roles, where it expects some generic variables (arguments). For example:

my_debug_role/main.yml:

- name: Output me
  ansible.builtin.debug:
    msg: "Your output was: {{ my_msg }}"

Then in your playbook, you can reuse it with more specific variable, like:

my_playbook.yml:

- name: Output my custom log
  include_role:
    name: my_debug_role
  vars:
    my_msg: "{{ some_custom_specific_variable }}"
Andrius
  • 19,658
  • 37
  • 143
  • 243
  • 1
    For ansible 2.9.6 these approach `- name: My Task vars: my_var: 123` gives me an error: `ERROR! no module/action detected in task.` – Vasiliy Fateev Aug 12 '22 at 12:22
-3

Whenever you have a module followed by a variable on the same line in ansible the parser will treat the reference variable as the beginning of an in-line dictionary. For example:

- name: some example
  command: {{ myapp }} -a foo

The default here is to parse the first part of {{ myapp }} -a foo as a dictionary instead of a string and you will get an error.

So you must quote the argument like so:

- name: some example
  command: "{{ myapp }} -a foo"
slm
  • 15,396
  • 12
  • 109
  • 124
Dwayne Mcnab
  • 131
  • 1
  • 1