Overwrite ansible variable from file fails - ansible

I'm trying to overwrite a previously defined variable my_var (when it's set to LATEST) by loading a value from a file (containing, say, NEWVALUE).
- name: Load from file
vars:
my_var: "{{ lookup('file', '~/file.txt') }}"
my_var2: "{{ lookup('file', '~/file.txt') }}"
debug: msg="my_var is {{ my_var }} my_var2 is {{ my_var2 }}"
when: "{{ my_var=='LATEST' }}"
This prints
ok: [host] ==> {
"msg": "my_var is LATEST my_var2 is NEWVALUE"
}
So I feel that I've verified that I'm loading the value correctly.. but for some reason I can't set the result of lookup in a previously set variable. Disabling the when clause doesn't seem to make any difference.
Should I be able to do this? As an alternative I'm going to use a third variable and just set it to either the preexisting value or the value from the file - but this seems like an unnecessary step to me.
Ansible version 2.1.0.0 b.t.w.

The vars you defined in your example only are available for the single debug task.
As you mentioned in the comment you figured this out and used set_fact instead. And yes, this won't work if you have passed the same variable as extra-var, as it has the highest precedence. There is no way to override a variable you passed as extra-var.

Related

How to init var of hostvars to null

I would like to reset a varaible to an empty list on all nodes of my inventory, whether it's already existing or not.
The best I come with is:
- name: clear union_files list
set_fact:
hostvars: "{{ hostvars.item | combine({'union_files': []}) }}"
loop: "{{ groups['all'] }}"
run_once: true
But with this code, I've got this error that I don't understand:
MSG:
|combine expects dictionaries, got Undefined
Why it is saying undefined while I'm setting the dict {'union_files': []}
Have you got any clue? What would by the most ansible way to do so?
There are a couple of things going on with this; first, hostvars.item is not the same as hostvars[item]; the former syntax is designed for a quick-and-dirty way of dereferencing keys in a dict, such as ansible_version.full which is not a variable named full, it's ansible_version["full"]. The latter is looking up a variable key name in the dict
The second thing going on is that you are attempting to assign hostvars as if it were a fact, but it's not a fact, it's a namespace in which facts live. So even if you were to do hostvars: {{ hostvars[item] }} it would create a fact named hostvars in the hostvars namespace
If you just want to clear the union_files fact, then use set_fact as you normally would:
- name: clear union_files list
set_fact:
union_files: []
and, assuming the playbook in which that task lives is hosts: all, then it will cheerfully set, or reset, union_files to [] on every host
Thanks Matthew,
I finally come up with this solution:
- name: clear list
set_fact:
union_files: []
delegate_to: item
loop: "{{ ansible_play_hosts }}"
run_once: true
which do the job.

how to display a message in ansible

I'm trying to debug the ansible_hostname variable in order to have the names of the different hosts to display.
unfortunately I am unable for the moment to display the value of the variable even the name I wrote in the task (:
- name: display variable
debug:
msg: "value variable {{ ansible_hostname }}"
verbosity: 4
tags:
- attrest
do i ve to add a parameters in my playbook or in other files ?
msg: value variable "{{ ansible_hostname }}"
Parenthesis to add in the playbook where variable is used

Set variable if empty or not defined with ansible

In my ansible vars file, I have a variable that will sometimes be set and other times I want to dynamically set it. For example, I have an RPM that I want to install. I can manually store the location in a variable, or if I don't have a particular one in mind, I want to pull the latest from Jenkins. My question is, how can I check if the variable is not defined or empty, and if so, just use the default from Jenkins (already stored in a var)?
Here is what I have in mind:
...code which gets host_vars[jenkins_rpm]
- hosts: "{{ host }}"
tasks:
- name: Set Facts
set_fact:
jenkins_rpm: "{{ hostvars['localhost']['jenkins_rpm'] }}"
- name: If my_rpm is empty or not defined, just use the jenkins_rpm
set_fact: my_rpm=jenkins_rpm
when: !my_rpm | my_rpm == ""
There is default filter for that:
- set_fact:
my_rpm: "{{ my_rpm | default(jenkins_rpm) }}"

Ansible: variable interpolation in task name

I cannot get this seemingly simple example to work in Ansible 1.8.3. The variable interpolation does not kick in the task name. All examples I have seen seem to suggest this should work. Given that the variable is defined in the vars section I expected the task name to print the value of the variable. Why doesn't this work?
Even the example from the Ansible documentation seems to not print the variable value.
---
- hosts: 127.0.0.1
gather_facts: no
vars:
vhost: "foo"
tasks:
- name: create a virtual host file for {{ vhost }}
debug: msg="{{ vhost }}"
This results in the following output:
PLAY [127.0.0.1]
**************************************************************
TASK: [create a virtual host file for {{ vhost }}]
****************************
ok: [127.0.0.1] => {
"msg": "foo"
}
PLAY RECAP
********************************************************************
127.0.0.1 : ok=1 changed=0 unreachable=0 failed=0
Update
This works with 1.7.2 but does not work with 1.8.3. So either this is a bug or a feature.
Variables are not resolved inside the name. Only inside the actual tasks/conditions etc. the placeholders will be resolved. I guess this is by design. Imagine you have a with_items loop and use the {{ item }}in the name. The tasks name will only be printed once, but the {{ item }} would change in every iteration.
I see the examples, even the one in the doc you linked to, use variables in the name. But that doesn't mean the result would be like you expected it. The docs are community managed. It might be someone just put that line there w/o testing it - or maybe it used to work like that in a previous version of Ansible and the docs have not been updated then. (I'm only using Ansible since about one year). But even though it doesn't work like we wish it would, I'm still using variables in my name's, just to indicate that the task is based on dynamic parameters. Might be the examples have been written with the same intention.
An interesting observation I recently made (Ansible 1.9.4) is, default values are written out in the task name.
- name: create a virtual host file for {{ vhost | default("foo") }}
When executed, Ansible would show the task title as:
TASK: [create a virtual host file for foo]
This way you can avoid ugly task names in the output.
Explanation
Whether the variable gets interpolated depends on where it has been declared.
Imagine You have two hosts: A and B.
If variable foo has only per-host values, when Ansible runs the play, it cannot decide which value to use.
On the other hand, if it has a global value (global in a sense of host invariance), there is no confusion which value to use.
Source: https://github.com/ansible/ansible/issues/3103#issuecomment-18835432
Hands on playbook
ansible_user is an inventory variable
greeting is an invariant variable
- name: Test variable substitution in names
hosts: localhost
connection: local
vars:
greeting: Hello
tasks:
- name: Sorry {{ ansible_user }}
debug:
msg: this won't work
- name: You say '{{ greeting }}'
debug:
var: ansible_user
I experienced the same problem today in one of my Ansible roles and I noticed something interesting.
When I use the set_fact module before I use the vars in the task name, they actually get translated to their correct values.
In this example I wanted to set the password for a remote user:
Notice that I use the vars test_user and user_password that I set as facts before.
- name: Prepare to set user password
set_fact:
user_password: "{{ linux_pass }}"
user_salt: "s0m3s4lt"
test_user: "{{ ansible_user }}"
- name: "Changing password for user {{ test_user }} to {{ user_password }}"
user:
name: "{{ ansible_user }}"
password: "{{ user_password | password_hash('sha512', user_salt) }}"
state: present
shell: /bin/bash
update_password: always
This gives me the following output:
TASK [install : Changing password for user linux to LiNuXuSeRPaSs#]
So this solved my problem.
It might be ugly, but you can somewhat workaround with something like this:
- name: create a virtual host file
debug:
msg: "Some command result"
loop: "{{ [ vhost ] }}"
or
- name: create a virtual host file
debug:
msg: "Some command result"
loop_control:
label: "{{ vhost }}"
loop: [1]
I wouldn't do this in general, but it shows how you can use items or label to give information outside of the command result. While it might not
Source: https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html

Using variables for file name and file contents in lineinfile module

I am trying to read the contents of a file, store these in a variable and then insert them into another file if they don't already exist.
So, how I'm attempting to go about this is as follows:
# Create a variable that represents the path to the file that you want to read from
ssh_public_key_file: '../../jenkins_master/files/{{ hostvars[inventory_hostname]["environment"] }}/id_rsa.pub'
# Create a variable that represents the contents of this file:
ssh_public_key: "{{ lookup('file', '{{ ssh_public_key_file }}') }}"
I then use these variables in my Ansible playbook as follows:
- name: Install SSH authorized key
lineinfile: create=yes dest=~/.ssh/authorized_keys line=" {{ ssh_public_key }}" mode=0644
However, when I try and run the playbook, I get the following error message:
could not locate file in lookup: {{ ssh_public_key_file }}
Can anyone recommend a solution or suggest what I may have done wrong?
Thanks,
Seán
You have to change the line to:
# Create a variable that represents the contents of this file:
ssh_public_key: "{{ lookup('file', ssh_public_key_file) }}"
If you need to concatenate variables and strings you can do it like this:
# Example with two variables
ssh_public_key: "{{ lookup('file', var_1+var_2) }}"
# Example with string and variable
ssh_public_key: "{{ lookup('file', '~/config/'+var_1) }}"
. .
First I would make sure that your ssh_public_key_file variable is set up properly. If you add a task like the following what does it show?
- name: display variable
debug: var=ssh_public_key_file
If the output looks something like this then the variable isn't defined properly (eg. the "environment" fact doesn't exist for the host):
ok: [localhost] => {
"ssh_public_key_file": "../../jenkins_master/files/{{ hostvars[inventory_hostname][\"environment\"] }}/id_rsa.pub"
}
However if everything is defined properly then your output should show the variables replaced with their correct values:
ok: [localhost] => {
"ssh_public_key_file": "../../jenkins_master/files/foo/id_rsa.pub"
}
Once you've verified that then I would do the same thing with your ssh_public_key variable. Just output its value using the debug module. It should display as the contents of the public key file.
One other thing I would strongly suggest is to avoid using lineinfile altogether. Since you're working with SSH keys I would recommend you use the authorized_key module instead. It's a much cleaner way of managing authorized_keys files.

Resources