Ansible changes variable values when set - string

I have a series of variables set. Call them Hosts and Inthosts. Each has an appropriate value set, as seen in the debug output. When I try to assign the value of inthosts to hosts, it does not actually make them the same, it bolloxes it up changing the double quotes to single quotes and putting a "u" in front of each "item". It there a way to force Ansible to actually do a literal equals in this case without parsing the text? The text should just be treated as a string. In this case the "modified" value is being output to a file, and the change breaks things.
The plan was to use the default hosts, and override it with inthosts if the server in question should be using a different set of servers.
Default Variables Set
filebeat_kafka_hosts: '["x.compute-1.amazonaws.com:9093", "y.compute-1.amazonaws.com:9093"]'
filebeat_kafka_inthosts: '["x.compute-1.amazonaws.com:9093", "y.compute-1.amazonaws.com:9093", "z.compute-1.amazonaws.com:9093"]'
Ansible Code
- debug:
msg: "Hosts {{ filebeat_kafka_hosts }} "
- debug:
msg: "IntHosts {{ filebeat_kafka_inthosts }} "
- set_fact:
filebeat_kafka_hosts="{{ filebeat_kafka_inthosts }}"
- debug:
msg: "Inthosts -> hosts {{ filebeat_kafka_hosts }} "
Output (edited)
"msg": "Hosts [\"x.compute-1.amazonaws.com:9093\", \"y.compute-1.amazonaws.com:9093\"] " |
"msg": "IntHosts [\"x.compute-1.amazonaws.com:9093\", \"y.compute-1.amazonaws.com:9093\", \"z.compute-1.amazonaws.com:9093\"] "
set {"ansible_facts": {"filebeat_kafka_hosts": ["x.compute-1.amazonaws.com:9093", "y.compute-1.amazonaws.com:9093", "z.compute-1.amazonaws.com:9093"]}, "changed": false}
"msg": "Inthosts -> hosts [u'x.compute-1.amazonaws.com:9093', u'y.compute-1.amazonaws.com:9093', u'z.compute-1.amazonaws.com:9093'] "

Ansible is interpreting filebeat_kafka_inthosts and filebeat_kafka_hosts as lists. That gives you the 'u' characters before each item in your debug. The tasks below
- debug:
msg: "{{ item }}"
with_items: "{{ filebeat_kafka_hosts }}"
- debug:
msg: "{{ item }}"
with_items: "{{ filebeat_kafka_inthosts }}"
Would give you
TASK [debug] *******************************************************************
ok: [127.0.0.1] => (item=y.compute-1.amazonaws.com:9093) => {
"item": "y.compute-1.amazonaws.com:9093",
"msg": "y.compute-1.amazonaws.com:9093"
}
ok: [127.0.0.1] => (item=x.compute-1.amazonaws.com:9093) => {
"item": "x.compute-1.amazonaws.com:9093",
"msg": "x.compute-1.amazonaws.com:9093"
}
TASK [debug] *******************************************************************
ok: [127.0.0.1] => (item=x.compute-1.amazonaws.com:9093) => {
"item": "x.compute-1.amazonaws.com:9093",
"msg": "x.compute-1.amazonaws.com:9093"
}
ok: [127.0.0.1] => (item=y.compute-1.amazonaws.com:9093) => {
"item": "y.compute-1.amazonaws.com:9093",
"msg": "y.compute-1.amazonaws.com:9093"
}
ok: [127.0.0.1] => (item=z.compute-1.amazonaws.com:9093) => {
"item": "z.compute-1.amazonaws.com:9093",
"msg": "z.compute-1.amazonaws.com:9093"
}
Since your writing this line to a file, you shouldn't have to worry about it too much. The 'u' character is a side effect of the debug module. Writing the variable to a file would give the same result (although with single quotes instead of double).
- lineinfile:
path: some_file
line: "{{ filebeat_kafka_hosts }}"
some_file
['x.compute-1.amazonaws.com:9093', 'y.compute-1.amazonaws.com:9093', 'z.compute-1.amazonaws.com:9093']
If you really need double quotes, you can use the to_json filter
- lineinfile:
path: some_file
line: "{{ filebeat_kafka_hosts | to_json }}"
some_file
["x.compute-1.amazonaws.com:9093", "y.compute-1.amazonaws.com:9093", "z.compute-1.amazonaws.com:9093"]

Seems that casting the variable as a string also works. Thanks for the advice!

Related

Ansible loop over a list to define a dynamic dictionary

I am trying to define a dictionary FACTS_VAR in which the key must contain the word SUB. Argument_value and the value is true but when I loop over the variable Argument
hosts: all
gather_facts: true
vars:
Argument:
- value1
- value2
tasks:
- name: DEFINING A variable
set_fact:
FACTS_VAR: {'SUB..item': true}
loop: "{{Argument}}"
- debug:
var: FACTS_VAR
I got this result so I don't know what is missing there. I am expecting to get a dictionary like this
FACTS_ENTRY:
SUB.value1: true
SUB.value2: true
TASK [debug] *****************************************************************************************************************
ok: [control] => {
"FACTS_ENTRY": {
"SUB..item": true
}
}
ok: [ansible4] => {
"FACTS_ENTRY": {
"SUB..item": true
}
Create the keys
facts_entry_keys: "{{ ['sub']|product(argument)|map('join','.')|list }}"
gives
facts_entry_keys:
- sub.value1
- sub.value2
Create the dictionary
facts_entry: "{{ dict(facts_entry_keys|product([true])) }}"
gives
facts_entry:
sub.value1: true
sub.value2: true
Example of a complete playbook
- hosts: localhost
vars:
argument:
- value1
- value2
facts_entry_keys: "{{ ['sub']|product(argument)|map('join','.')|list }}"
facts_entry: "{{ dict(facts_entry_keys|product([true])) }}"
tasks:
- debug:
var: facts_entry
From your current description I understand only that you probably like to define variables somehow dynamically.
The following example could give some guidance.
---
- hosts: test
become: false
gather_facts: false
vars:
ARG:
- val1
- val2
tasks:
- name: Dynamically define variable
set_fact:
FACTS_VAR: "{ 'SUB_{{ item }}': true }"
loop: "{{ ARG }}"
- debug:
var: FACTS_VAR
resulting into an output of
TASK [Dynamically define variable] **
ok: [test.example.com] => (item=val1)
ok: [test.example.com] => (item=val2)
TASK [debug] ************************
ok: [test.example.com] =>
FACTS_VAR:
SUB_val2: true
Please take note that according your current description there can be only one value as result.
Further Q&A
Build variable name at play level

Filter data using JSON query in Ansible to extract data from an ansible_fact

I have created this playbook to extract all mount points starting with any element in the variable whitelist matching the type= ext2, ext3, ext4.
The problem is that I can get all mount_points but I am not able to filter the result with the variables.
- hosts: all
gather_facts: True
become: True
vars:
whitelist:
- /boot
- /home
- /opt
- /var
- /bin
- /usr
tasks:
- name: extract mount_points
set_fact:
mount_point: "{{ansible_facts.mounts | selectattr('fstype', 'in', ['ext2', 'ext3', 'ext4']) | map(attribute='mount') | list }}"
- debug:
var: mount_point
vars:
query: "[?starts_with(mount, whitelist)].mount"
When I execute the playbook I get this
ok: [ansible#controller] => {
"mount_point": [
"/",
"/boot",
"/tmp",
"/home",
"/var",
"/var/opt",
"/var/tmp",
"/var/log",
"/var/log/audit",
"/opt",
]
}
/tmp is included which means that query: "[?starts_with(mount, whitelist)].mount" was skipped and I don't know how to achieve the playbook goal.
You don't really need a json query here IMO. An easy way is to filter the list with match and construct a regex containing all possible prefixes:
- name: show my filtered mountpoints:
vars:
start_regex: "{{ whitelist | map('regex_escape') | join('|') }}"
degug:
msg: "{{ {{ansible_facts.mounts | selectattr('fstype', 'in', ['ext2', 'ext3', 'ext4'])
| map(attribute='mount') | select('match', start_regex) | list }}"
While testing i used a filter expression within the json_query string which achieved the same goal for me Query String with Dynamic Variable
.
In this example the filter expression is used with a built-in function, ?starts_with. This function provides a boolean result, based on a match with a search string, on any element within the fstype and mount_point using a dynamic variable {{whitelist}}.
- name: find mount_points
debug:
msg: "{{ ansible_mounts | to_json | from_json | json_query(mount_point) }}"
vars:
mount_point: " #[?starts_with(fstype, 'ext')] | #[?starts_with(mount, '{{item}}')].mount"
loop: "{{whitelist}}"
this is the output
ok: [ansible#ansible1] => (item=/var) => {
"msg": [
"/var",
"/var/opt",
"/var/tmp",
"/var/log",
"/var/log/audit"
}
ok: [ansible#ansible1] => (item=/usr) => {
"msg": []
}
ok: [ansible#ansible1] => (item=/home) => {
"msg": [
"/home"
>>>

Ansible - set fact for key of dict based on sub-key value

Pfoef, describing my issue at hand is quite difficult. Please bear with me.
I have this dict:
my_dict:
FIRST:
some_key: first_value
SECOND:
some_key: second_value
My Ansible task is:
- shell: "echo {{ item.value['some_key'] }}"
register: exec_output
loop: "{{ my_dict | dict2items }}"
# This is something where I do not know what to do
- set_fact:
desired_output: ???
when: <some_key_contains_a_value>
When Ansible executes, it will execute the shell command twice, because there are 2 items in the dict.
Q: How can I configure Ansible so that: Ansible will set a fact and add the key (FIRST or SECOND) if the value of some_key is e.g. 'second_value'. In this example case, the fact will contain "SECOND".
You can find back the whole item that was part of a loop when register'ing its output via the item.item property.
So, in your case, you will find, in the items under exec_output.results:
item:
key: FIRST
value:
some_key: first_value
item:
key: SECOND
value:
some_key: second_value
So, based on that you could have a playbook like:
- hosts: localhost
gather_facts: no
tasks:
- shell: "echo {{ item.value.some_key }}"
register: exec_output
loop: "{{ my_dict | dict2items }}"
vars:
my_dict:
FIRST:
some_key: first_value
SECOND:
some_key: second_value
- set_fact:
desired_output: "{{ item.item.key }}"
when: some_key in item.stdout
loop: "{{ exec_output.results }}"
vars:
some_key: second
loop_control:
label: "{{ item.stdout }}"
- debug:
var: desired_output
That would give you the expected result:
PLAY [localhost] *******************************************************************************************************************
TASK [shell] ***********************************************************************************************************************
changed: [localhost] => (item={'key': 'FIRST', 'value': {'some_key': 'first_value'}})
changed: [localhost] => (item={'key': 'SECOND', 'value': {'some_key': 'second_value'}})
TASK [set_fact] ********************************************************************************************************************
skipping: [localhost] => (item=first_value)
ok: [localhost] => (item=second_value)
TASK [debug] ***********************************************************************************************************************
ok: [localhost] =>
desired_output: SECOND
PLAY RECAP *************************************************************************************************************************
localhost : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Print json output in Ansible and store in a list?

Ansible playbook:
---
- task:-
- code goes here
- name: Trying to get instance Private IP from ASG
ec2_instance_facts:
instance_ids:
- "{{ item }}"
with_items: "{{ INSTANCE_IDS_FROM_ASG }}"
register: instance_ids_result
- set_fact:
msg: "{{ instance_ids_result | json_query('results[*].instances[*].network_interfaces[*].private_ip_address') }} "
- debug: var=msg
I have the output as follows:
ok: [localhost] => {
"msg": [
[
"172.31.144.74"
],
[
"172.31.147.69"
]
]
}
But, I would need the output in a list as ["172.31.144.74", "172.31.147.69"] or "172.31.147.69" "172.31.147.69".
What is the best way to print it that way?
You could flatten your list using the filter
- set_fact:
msg: "{{ instance_ids_result | json_query('results[*].instances[*].network_interfaces[*].private_ip_address') | flatten }} "

Using register with a loop in Ansible

i want to code a playbook which IF a user exists changes the pw of it.
The playbook should be able to take n User's and change the pw of those Users.
At the moment im having the issue that the when is empty due to the loop, i tried using with_items: {{ user_exists.results }} but this is somehow not working.
(http://docs.ansible.com/ansible/playbooks_loops.html#using-register-with-a-loop)
Am i doing something wrong ?
Br,
Numblesix
---
-
become: true
become_method: sudo
hosts: xetest
name: "Updates the password of given User if exists"
tasks:
-
ignore_errors: true
name: "Check if User exists"
register: user_exists
shell: "grep -q {{ item.key }} /etc/passwd &>/dev/null"
with_dict: "{{ users }}"
-
debug:
var: user_exists
-
debug:
msg: "User name is {{ item.key }} and hash is {{ item.value.passwd}} and return code is: "
with_dict: "{{ users }}"
-
debug:
var: user_exists
with_items: "{{user_exists.results }}"
-
name: "updating password for given User"
user: "name={{ item.key }} update_password=always password={{ item.value.passwd}} createhome=no"
when: user_exists.rc == 0
with_dict: "{{ users }}"
with_items: "{{ user_exists.results }}"
vars:
users:
foo:
passwd: $6$random_salt$12A.ar9eNDsgmds3leKoCDZPmq7OHLvhBtQg/Q3K2G/3yeEa/r8Ou4DxJpN6vzccewugvZt7IkfCbHFF2i.QU.
RESULTS IN ERROR!
duplicate loop in task: items
WITHOUT with_items: "{{ user_exists.results }}" im getting this error
"failed": true, "msg": "The conditional check 'user_exists.rc == 0' failed.
The error was: error while evaluating conditional (user_exists.rc == 0):
'dict object' has no attribute 'rc'
For my testing, I'm using ansible 2.1.4.0.
When running the script, you can see in the debug for user_exists.results that it contains the input value passed in along with the return code:
"results": [
{
"_ansible_item_result": true,
"_ansible_no_log": false,
"_ansible_parsed": true,
"changed": true,
"cmd": "grep -q foo /etc/passwd",
"delta": "0:00:00.009034",
"end": "2017-05-02 17:42:57.835871",
"failed": true,
"invocation": {
"module_args": {
"_raw_params": "grep -q foo /etc/passwd",
"_uses_shell": true,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"warn": true
},
"module_name": "command"
},
"item": {
"key": "foo",
"value": {
"passwd": "foobar"
}
},
"rc": 1,
"start": "2017-05-02 17:42:57.826837",
"stderr": "",
"stdout": "",
"stdout_lines": [],
"warnings": []
},
So instead doing two loops (which would have been done with with_nested and two lists), you can do everything with a single loop:
- name: "updating password for given User"
debug:
msg: "name={{ item.item.key }} update_password=always password={{ item.item.value.passwd}} createhome=no"
when: item.rc == 0
with_items: "{{ user_exists.results }}"
Note: In my testing shell: "grep -q {{ item.key }} /etc/passwd &>/dev/null" was always returning a 0 return code. I had to remove the "&>/dev/null" part to get the proper return code.

Resources