Ansible: variable is defined but error shows not defined - ansible

I have an ansible playbook as follows:
---
- name: test variables
hosts: all
tasks:
- name: test
command: echo {{ ansible_role_dir }}
register: result
- name: show result
debug:
msg="{{ result.stdout }}"
#roles: #line12
# - "{{ ansible_role_dir }}/testrole" #line13
the variable ansible_role_dir is defined under group_vars/all.yaml.
if I run the playbook which comments out the line12 and line13, it shows the result of variable correctly. Obviously it knows where the variable ansible_role_dir is defined. But if I uncomment line12 and line13, it shows error ERROR! 'ansible_role_dir' is undefined. Why it does not know where the ansible_role_dir is defined this time?

Ansible does not like jinja tags without quotes. try this:
---
- name: test variables
hosts: all
tasks:
- name: test
command: "echo {{ ansible_role_dir }}"
register: result
- name: show result
debug:
msg="{{ result.stdout }}"
#roles: #line12
# - "{{ ansible_role_dir }}/testrole" #line13
EDIT:
sorry, i think i misread the question.
have you tried putting in the real path instead of the var, just to see if it works? usually role paths are given in ansible's config, i never saw them run with a direct path

Related

Use value of CLI variable as the name of a host_vars variable?

Is there a way to use the value of one Ansible variable as the name of another variable so I can extract a value from its list?
host_vars:
this:
does: walk
says: hi
that:
does: run
says: hello
On the CLI when I run the playbook, I add -e="thing=this".
In the playbook, I've tried all manner of things to expand the variable thing to its value this, then use this to extract the value of does in the host_vars file.
Using the variable name directly obviously works:
- name: Check what the thing does
debug:
msg: "{{ this['does'] }}"
But the following do not:
{{ thing['does'] }}
{{ {{ thing }}['does'] }}
Those, plus several other iterations I've tried all either throw an error or print out the literal string.
You need the vars lookup plugin to address variables indirectly. See
shell> ansible-doc -t lookup vars
For example,
- debug:
msg: "{{ lookup('vars', thing).does }}"
should give (abridged)
shell> ansible-playbook pb.yml -e "thing=this"
...
msg: walk
Example of a complete playbook for testing
- hosts: localhost
vars:
this:
does: walk
says: hi
that:
does: run
says: hello
tasks:
- debug:
msg: "{{ lookup('vars', thing).does }}"
You can simplify the code further by putting all declarations into the vars. For example, into the group_vars/all
shell> cat group_vars/all/thing.yml
this:
does: walk
says: hi
that:
does: run
says: hello
_thing: "{{ lookup('vars', thing) }}"
Then, the simplified playbook below gives the same results
- hosts: localhost
tasks:
- debug:
var: _thing.does

Ansible hostvars not evaluated in group_vars

I have the following playbook :
- name: Do something
hosts: myHost
tasks:
- debug:
msg: "{{hostvars[groups['gatewayHost'][0]].ansible_host}}"
- debug:
msg: "{{hostvars[item].foo}}"
with_items: "{{ groups['webapps'] }}"
For group webapps I have a groups_vars/webapps.yml :
foo: "{{ hostvars[groups['gatewayHost'][0]].ansible_host }}"
My issue is that when running the playbook the first debug message is correctly evaluated, but the second debug message just display hostvars[groups['gatewayHost'][0]].ansible_host and doesn't evaluate the variable. I guess I'm missing something but I can't see what.
(Note, If I try to evaluate groups['gatewayHost'], it's correctly evaluated in both cases, playbook and group_vars)
After digging into Ansible issues, I found this one https://github.com/ansible/ansible/issues/20545 which is pretty close to my issue.

Use variable from another host

I have a playbook that executes a script on a Windows box that returns a value that I have to re-use later on in my playbook after switching to localhost.
How can I access this value after switching back to localhost?
Here is an example:
- hosts: windows
gather_facts: no
tasks:
- name: Call PowerShell script
win_command: "c:\\windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe c:\\psl_scripts\\getData.ps1"
register: value_to_reuse
- hosts: localhost
gather_facts: no
tasks:
- name: debug store_name from windows host
debug:
var: "{{ hostvars[windows][value_to_reuse][stdout_lines] }}"
What is the correct syntax accessing a variable from another host?
I'm receiving error message:
"msg": "The task includes an option with an undefined variable. The error was: 'windows' is undefined
Here is the code that works for a group in a loop:
- name: print value_to_reuse
debug:
var: hostvars[item].value_to_reuse.stdout_lines
loop: "{{ groups['windows'] }}"
Same code works without iterations:
- name: print value_to_reuse
debug:
var: hostvars[groups['windows'].0].value_to_reuse.stdout_lines
The syntax is:
- debug:
var: hostvars['windows']['value_to_reuse']['stdout_lines']
Three mistakes:
you should quote string values
var parameter takes the variable name, not a template (which should be an msg-parameter value)
windows in the given example should be the host name as all facts are bound to hosts, not group of hosts

How to get value of --limit argument inside an Ansible playbook?

In ansible, is it possible to get the value of the argument to the "--limit" option within a playbook? I want to do is something like this:
---
- hosts: all
remote user: root
tasks:
- name: The value of the --limit argument
debug:
msg: "argument of --limit is {{ ansible-limit-arg }}"
Then when I run he command:
$ ansible-playbook getLimitArg.yaml --limit webhosts
I'll get this output:
argument of --limit is webhost
Of course, I made up the name of the variable "ansible-limit-arg", but is there a valid way of doing this? I could specify "webhosts" twice, the second time with --extra-args, but that seems a roundabout way of having to do this.
Since Ansible 2.5 you can access the value using a new magic variable ansible_limit, so:
- debug:
var: ansible_limit
Have you considered using the {{ ansible_play_batch }} built-in variable?
- hosts: all
become: "False"
gather_facts: "False"
tasks:
- name: The value of the --limit argument
debug:
msg: "argument of --limit is {{ ansible_play_batch }}"
delegate_to: localhost
It won't tell you exactly what was entered as the argument but it will tell you how Ansible interpreted the --limit arg.
You can't do this without additional plugin/module. If you utterly need this, write action plugin and access options via cli.options (see example).
P.S. If you try to use --limit to run playbooks agains different environments, don't do it, you can accidentally blow your whole infrastructure – use different inventories instead.
Here is the small code block to achieve the same
- block:
- shell: 'echo {{inventory_hostname}} >> /tmp/hosts'
- shell: cat /tmp/hosts
register: servers
- file:
path: /tmp/hosts
state: absent
delegate_to: 127.0.0.1
- debug: var=servers.stdout_lines
Then use stdout_lines output as u wish like mine
- add_host:
name: "{{ item }}"
groups: cluster
ansible_user: "ubuntu"
with_items:
- "{{ servers.stdout_lines }}"

ansible: using with_items with notify handler

I want to pass a variable to a notification handler, but can't find anywhere be it here on SO, the docs or the issues in the github repo, how to do it. What I'm doing is deploying multiple webapps, and when the code for one of those webapps is changed, it should restart the service for that webapp.
From this SO question, I got this to work, somewhat:
- hosts: localhost
tasks:
- name: "task 1"
shell: "echo {{ item }}"
register: "task_1_output"
with_items: [a,b]
- name: "task 2"
debug:
msg: "{{ item.item }}"
when: item.changed
with_items: task_1_output.results
(Put it in test.yml and run it with ansible-playbook test.yml -c local.)
But this registers the result of the first task and conditionally loops over that in the second task. My problem is that it gets messy when you have two or more tasks that need to notify the second task! For example, restart the web service if either the code was updated or the configuration was changed.
AFAICT, there's no way to pass a variable to a handler. That would cleanly fix it for me. I found some issues on github where other people run into the same problem, and some syntaxes are proposed, but none of them actually work.
Including a sub-playbook won't work either, because using with_items together with include was deprecated.
In my playbooks, I have a site.yml that lists the roles of a group, then in the group_vars for that group I define the list of webapps (including the versions) that should be installed. This seems correct to me, because this way I can use the same playbook for staging and production. But maybe the only solution is to define the role multiple times, and duplicate the list of roles for staging and production.
So what is the wisdom here?
Variables in Ansible are global so there is no reason to pass a variable to handler. If you are trying to make a handler parameterized in a way that you are trying to use a variable in the name of a handler you won't be able to do that in Ansible.
What you can do is create a handler that loops over a list of services easily enough, here is a working example that can be tested locally:
- hosts: localhost
tasks:
- file: >
path=/tmp/{{ item }}
state=directory
register: files_created
with_items:
- one
- two
notify: some_handler
handlers:
- name: "some_handler"
shell: "echo {{ item }} has changed!"
when: item.changed
with_items: files_created.results
I finally solved it by splitting the apps out over multiple instances of the same role. This way, the handler in the role can refer to variables that are defined as role variable.
In site.yml:
- hosts: localhost
roles:
- role: something
name: a
- role: something
name: b
In roles/something/tasks/main.yml:
- name: do something
shell: "echo {{ name }}"
notify: something happened
- name: do something else
shell: "echo {{ name }}"
notify: something happened
In roles/something/handlers/main.yml:
- name: something happened
debug:
msg: "{{ name }}"
Seems a lot less hackish than the first solution!
To update jarv's answer above, Ansible 2.5 replaces with_items with loop. When getting results, item by itself will not work. You will need to explicitly get the name, e.g., item.name.
- hosts: localhost
tasks:
- file: >
path=/tmp/{{ item }}
state=directory
register: files_created
loop:
- one
- two
notify: some_handler
handlers:
- name: "some_handler"
shell: "echo {{ item.name }} has changed!"
when: item.changed
loop: files_created.results
I got mine to work like this - I had to add some curly brackets
tasks:
- name: Aktivieren von Security-, Backport- und Non-Security-Upgrades
lineinfile:
path: /etc/apt/apt.conf.d/50unattended-upgrades
regexp: '^[^"//"]*"\${distro_id}:\${distro_codename}-{{ item }}";'
line: ' "${distro_id}:${distro_codename}-{{ item }}";'
insertafter: "Unattended-Upgrade::Allowed-Origins {"
state: present
register: aenderung
loop:
- updates
- security
- backports
notify: Auskommentierte Zeilen entfernen
handlers:
- name: Auskommentierte Zeilen entfernen
lineinfile:
path: /etc/apt/apt.conf.d/50unattended-upgrades
regexp: '^\/\/.*{{ item.item }}";.*'
state: absent
when: item.changed
loop: "{{ aenderung.results }}"

Resources