Ansible set fact: evaluate a json path with a variable - ansible

I need to fetch data with conditions. This piece of code worked for me.
- name: retriving passwords...
uri:
...........
register: this
- name: Setting ssh secret fact...
set_fact:
ssh_secret: "{{ this.json.data.data.devuservalue }}"
when: ansible_user == "{{ user_dev }}"
The value of {{ user_dev }} is also devuservalue.
How can I remove this devuservalue hardcoded value from ssh_secret: line. Because I'll have to add several set facts with conditions. If I could add variable within variable.

Related

Ansible task with switching variables

I'm trying to create ansible playbook that will use variables if they are defined without using "while:" and manually typing the undefined variables & duplicating tasks.
For example I have the below variables:
vars:
service_List:
- 1:
state: present
address_type: ipv4
ip: 10.0.0.0
- 2:
state: present
jump: true
ip: 10.5.5.0
hold_true: yes
- 3:
state: present
address_type: ipv4
is_enabled: true
dhcp: none
I want to have a single task that will use the above variables on a specific module.
example of a task: (notice the with_dict)
tasks:
- name: task name here
some_module:
**This here will include the code for adding the variables form vars**
**So for example, for 1st dict it will include state, address_type and ip**
**for 2nd dict it will include variables state,jump,ip,hold_true**
**example: state: "{{ item.value.state }}"
with_dict: "{{ service_List }}"
Please help with missing code inside the task
It depends on the some_module use case. In particular, whether the parameters are required or not. And, if required, whether there is a default value or not. There are three options if a parameter is missing in the dictionary
The parameter is not required. Use default(omit)
The parameter is required. Use default(defaul_value_of_this_param)
The parameter is required but there is no default. The module will crash.
For example,
tasks:
- name: task name here
some_module:
state: "{{ item.value.state }}"
address_type: "{{ item.value.address_type|default('ipv4') }}"
ip: "{{ item.value.ip|default(omit) }}"
jump: "{{ item.value.jump|default(False) }}"
hold_true: "{{ item.value.hold_true|default(omit) }}"
with_dict: "{{ service_List }}"
In addition to this, you can use Module defaults.

Ansible when conditional with variable and item

I'm having hard time trying to figure it out what I'm doing wrong with my Ansible playbook.
I've got a bunch of tasks, which define or not some variables according to context, depending of the result, some task will be ignored or not.
For this specific case, I check if a VlanID already exists, if it doesn't then I create one, and retrieve the new VlanID from the result.
Here is the playbook :
---
#Tasks for portGroup_add
- name: Get all portgroups in dvswitch vDS
community.vmware.vmware_dvs_portgroup_find:
hostname: "{{ vcenter_server }}"
username: "{{ vcenter_user }}"
password: "{{ vcenter_pass }}"
dvswitch: "{{ vcenter_dvSwitch }}"
validate_certs: False
register: portGroup_infos
when: (OLD_VLANID is not defined) or (OLD_VLANID|length < 1)
#Get last VLAN ID for HDS client, and set VLANID + 1
- name: get portGroup_infos
set_fact:
VLANID: "{{ item.vlan_id }}"
with_items: "{{ portGroup_infos.dvs_portgroups}}"
when:
- (portGroup_infos is defined) and (portGroup_infos|length > 0)
- item.name | regex_search("\(HDS :\s*")
While everything is working pretty well for most of the tasks, this one fires the following error :
The conditional check 'item.name | regex_search("\(HDS :\s*")' failed.
The error was: error while evaluating conditional (item.name | regex_search("\(HDS :\s*")): 'item' is undefined
Which is pretty obvious, because the dict portGroup_infos, is not defined.
In order to get the new VlanID, I'm using a "when" conditionnal , which check if in the item, the value "(HDS :" , is present.
But I don't want the task to launch if the portGroup_infos variable defined above is not set, I though I'd should use nested "when", but can't succeed.
Ansible version : 2.10.7
python version : 3.7.3
Thank you for your help.
Put both tasks into a block, e.g.
- block:
- name: Get all portgroups in dvswitch vDS
...
- name: get portGroup_infos
...
when: OLD_VLANID|default('')|length == 0

How to restrict scope of set_fact variable in ansible

below is my ansible playbook for validation of objects - I am first using validateip role and under that executing login,validation and then logout tasks.
- name: validate object
vars:
mserver: [1.1.1.1,2.2.2.2]
domain: [3.3.3.3,4.4.4.4]
tasks:
- include_role:
name: validateip
when: object_type == "ip"
with_together:
- "{{ mserver_hostname }}"
- "{{ domain }}"
- name: Checking Network objects
uri:
url: "https://{{item.0}}/web_api/show-objects"
validate_certs: False
method: POST
headers:
x-chkp-sid: "{{ login.json.sid }}"
body:
type: host
filter: "{{ip}}"
ip-only: true
body_format: json
register: check_host_result
when: item.0 != ""
- debug:
var: check_host_result
- name: Checking if Network Object exists
set_fact:
item_ip_exists: true
obj_name: "{{ item2['name'] }}"
loop: "{{ check_host_result.json.objects }}"
loop_control:
loop_var: item2
when:
- item2['ipv4-address'] is defined and item2['ipv4-address'] == ip
- debug:
msg: "Network Object exists with name [{{obj_name}}]"
when: item_ip_exists is defined
- debug:
msg: " Network Object ({{ip}}) will be created"
when: item_ip_exists is not defined
I am facing issue for set_fact variable like obj_name and item_ip_exists
so when loop runs on first item and if object is present so it set both the variable (obj_name and item_ip_exists ) and print the correct debug messages.
but when 2nd item executed and there if object is not present so it is printing the wrong debug message due to the set_fact variables( obj_name and item_ip_exists) which has already the value from the first items execution
so how i can restrict the scope of set_fact variables ( obj_name and item_ip_exists ) so when second item execute the variables take the value from there not from previously set_fact values.
I am totally stuck here.
Please help me. Thanks in advance.
Alas, the variable that is set by set_fact stays defined (for the same host) and keeps the value that had been assigned on the previous loop's pass.
As a solution (which is pretty ugly, to be frank) you may want to redefine the variable's value in the very beginning of the loop and set it to None by something like this:
- set_fact:
my_variable:
Then you'll have to check if it's equal or unequal to None. Alas, no one could undefine a previously defined variable in Ansible, that's why your condition won't be my_variable is undefined, but will be my_variable | default(None) == None.
Ugly, right.

Ansible - Use default if a variable is not defined

I'm customizing linux users creation inside my role. I need to let users of my role customize home_directory, group_name, name, password.
I was wondering if there's a more flexible way to cope with default values.
I know that the code below is possible:
- name: Create default
user:
name: "default_name"
when: my_variable is not defined
- name: Create custom
user:
name: "{{my_variable}}"
when: my_variable is defined
But as I mentioned, there's a lot of optional variables and this creates a lot of possibilities.
Is there something like the code above?
user:
name: "default_name", "{{my_variable}}"
The code should set name="default_name" when my_variable isn't defined.
I could set all variables on defaults/main.yml and create the user like that:
- name: Create user
user:
name: "{{my_variable}}"
But those variables are inside a really big hash and there are some hashes inside that hash that can't be a default.
You can use Jinja's default:
- name: Create user
user:
name: "{{ my_variable | default('default_value') }}"
Not totally related, but you can also check for both undefined AND empty (for e.g my_variable:) variable. (NOTE: only works with ansible version > 1.9, see: link)
- name: Create user
user:
name: "{{ ((my_variable == None) | ternary('default_value', my_variable)) \
if my_variable is defined else 'default_value' }}"
If anybody is looking for an option which handles nested variables, there are several such options in this github issue.
In short, you need to use "default" filter for every level of nested vars. For a variable "a.nested.var" it would look like:
- hosts: 'localhost'
tasks:
- debug:
msg: "{{ ((a | default({})).nested | default({}) ).var | default('bar') }}"
or you could set default values of empty dicts for each level of vars, maybe using "combine" filter. Or use "json_query" filter. But the option I chose seems simpler to me if you have only one level of nesting.
In case you using lookup to set default read from environment you have also set the second parameter of default to true:
- set_facts:
ansible_ssh_user: "{{ lookup('env', 'SSH_USER') | default('foo', true) }}"
You can also concatenate multiple default definitions:
- set_facts:
ansible_ssh_user: "{{ some_var.split('-')[1] | default(lookup('env','USER'), true) | default('foo') }}"
If you are assigning default value for boolean fact then ensure that no quotes is used inside default().
- name: create bool default
set_fact:
name: "{{ my_bool | default(true) }}"
For other variables used the same method given in verified answer.
- name: Create user
user:
name: "{{ my_variable | default('default_value') }}"
If you have a single play that you want to loop over the items, define that list in group_vars/all or somewhere else that makes sense:
all_items:
- first
- second
- third
- fourth
Then your task can look like this:
- name: List items or default list
debug:
var: item
with_items: "{{ varlist | default(all_items) }}"
Pass in varlist as a JSON array:
ansible-playbook <playbook_name> --extra-vars='{"varlist": [first,third]}'
Prior to that, you might also want a task that checks that each item in varlist is also in all_items:
- name: Ensure passed variables are in all_items
fail:
msg: "{{ item }} not in all_items list"
when: item not in all_items
with_items: "{{ varlist | default(all_items) }}"
The question is quite old, but what about:
- hosts: 'localhost'
tasks:
- debug:
msg: "{{ ( a | default({})).get('nested', {}).get('var','bar') }}"
It looks less cumbersome to me...
#Roman Kruglov mentioned json_query. It's perfect for nested queries.
An example of json_query sample playbook for existing and non-existing value:
- hosts: localhost
gather_facts: False
vars:
level1:
level2:
level3:
level4: "LEVEL4"
tasks:
- name: Print on existing level4
debug:
var: level1 | json_query('level2.level3.level4') # prints 'LEVEL4'
when: level1 | json_query('level2.level3.level4')
- name: Skip on inexistent level5
debug:
var: level1 | json_query('level2.level3.level4.level5') # skipped
when: level1 | json_query('level2.level3.level4.level5')
You can also use an if statement:
# Firewall manager: firewalld or ufw
firewall: "{{ 'firewalld' if ansible_os_family == 'RedHat' else 'ufw' }}"

Conditionally define variable in Ansible

I want to conditionally define a variable in an Ansible playbook like this:
my_var: "{{ 'foo' if my_condition}}"
I would like the variable to remain undefined if the condition does not resolve to true.
Ansible gives the following error if I try to execute the code:
fatal: [foo.local] => {'msg': 'AnsibleUndefinedVariable: One or more undefined
variables: the inline if-expression on line 1 evaluated
to false and no else section was defined.', 'failed': True}
Why is this an error anyway?
The complete case looks like this:
{role: foo, my_var: "foo"}
If my_var is defined, the role does something special. In some cases, I don't want the role to do this. I could use when: condition, but then I would have to copy the whole role block. I could also use an extra bool variable, but I would like a solution without having to change the "interface" to the role.
Any ideas?
You could use something like this:
my_var: "{{ 'foo' if my_condition else '' }}"
The 'else' will happen if condition not match, and in this case will set a empty value for the variable. I think this is a short, readable and elegant solution.
This code may help you to define a variable with condition.
- hosts: node1
gather_facts: yes
tasks:
- name: Check File
shell: ls -ld /etc/postfix/post-install
register: result
ignore_errors: yes
- name: Define Variable
set_fact:
exists: "{{ result.stdout }}"
when: result|success
- name: Display Variable
debug: msg="{{ exists }}"
ignore_errors: yes
So here the exists will display only if the condition is true.
My example, after https://stackoverflow.com/a/43403229/5025060:
vars:
sudoGroup: "{{ 'sudo' if ansible_distribution == 'Ubuntu' else 'wheel' }}"
Because of the different sudo conventions used by Ubuntu versus other platforms, here I am telling Ansible to set a variable named sudoGroup to sudo if the platform is Ubuntu, otherwise set it to wheel.
Later in my playbook, I combine the variable with Ansible's user module to add either sudo or wheel to an account's secondary groups depending on the OS Ansible is running on:
- name: Add or update bob account
user:
name: bob
uid: 3205
groups: "{{ sudoGroup }}"
append: yes
NOTES:
Double quotes around the {{ variable }} are required in the user: groups: definition above.
Once I define sudoGroup as above in my playbook's global vars: section, Ansible configures it at run time (based on ansible_distribution) for each target I define in my hosts: section.
I believe you're after the default(omit) filter. (Reference).
As per the example, mode will behave like it wasn't set at all for the first two items in the loop.
- name: touch files with an optional mode
file:
dest: "{{item.path}}"
state: touch
mode: "{{item.mode|default(omit)}}"
loop:
- path: /tmp/foo
- path: /tmp/bar
- path: /tmp/baz
mode: "0444"
This can be set as with bool:
- name: Conditional (true and false)
set_fact:
my_boolean_set_to_be: "{{ 'true' if my_var == 'foo' else 'false' }}"
- name: Display Variable
debug: msg="{{ my_boolean_set_to_be }}"
This can be set as for more conditionals like 'if-ifelse-else' statements:
- name: Conditional for 'my_var' (2 options and one default)
set_fact:
my_var_set_to_be: "{{ 'breakfast' if my_var == 'morning' else 'lunch' if my_var == 'afternoon' else 'dinner' }}"
- name: Display Variable
debug: msg="{{ my_var_set_to_be }}"

Resources