How to append values in ansible variable using conditional statements - ansible

I am passing a set of values to the ansible play book. Using the value I try to create a string.
For example, I pass arguments: first_nm, last_nm and nick_nm to my playbook via --extra-vars. And inside my role/:
<task-name>/
vars/main.yml
I tried to do the following:
full_name: {{first_nm}} {{last_nm}}{{'-'+nick_nm if nick_nm is defined else ''}}
My Problem:
Since nick_nm is optional, when its empty or not defined if get the full name as for example : david john- with a - appended to the value.
So how can I avoid this append. Is there a better way to do the same?

You should also check if string is not empty. In your setup you only checking if the variable exists, and since it does the condition evaluates to True and gives you - + nick_nm
You can do it like this:
---
- hosts: localhost
gather_facts: no
connection: local
vars:
- first_nm: John
- last_nm: Smith
- nick_nm:
tasks:
- set_fact: full_name="{{first_nm}} {{last_nm}}{% if nick_nm is defined and nick_nm %}-{{nick_nm}}{%endif%}"
- debug: var=full_name

Related

Ansible - Passing a dictionary to a module parameter

I'm using fortinet.fortios.system_global module as describe here: https://docs.ansible.com/ansible/latest/collections/fortinet/fortios/fortios_system_global_module.html#ansible-collections-fortinet-fortios-fortios-system-global-module
My goal is to pass a dictionary to the system_global parameter with the allowed sub-parameters. I have the dictionary as follows for example:
forti:
admin-concurrent: enable
admin-console-timeout: 0
admin-hsts-max-age: 15552000
<more key:value>
This dictionary lives in a separate file called forti.yml.
I then use include_vars to pull this yml file into my play as follows:
vars_files:
- /path/to/forti.yml
And then I use the system_global module:
- name: Configure system_global task
fortios_system_global:
access: "{{ access_token }}"
system_global: "{{ forti }}"
However, when I run the play it throws an error like so:
"msg": "Unsupported parameters for (fortios_system_global) module: system_global.admin-concurrent, system_global.admin-console-timeout, system_global.admin-hsts-max-age,<and so on>. Supported parameters include: member_path, member_state, system_global, vdom, enable_log, access_token."
I tried putting the key:value pairs in the vars: in the play level and passed it to the module the same way and it worked.
vars:
forti:
admin-concurrent: enable
admin-console-timeout: 0
admin-hsts-max-age: 15552000
<more key: value>
What am I missing? They're both type: dict, the data are exactly the same. Not sure what I'm missing here. Can someone please help?
You have - and the parameters are supposed to be _ so it is telling you the module parameter does not exist
vars:
forti:
admin-concurrent: enable
admin-console-timeout: 0
admin-hsts-max-age: 15552000
<more key: value>
should be
vars:
forti:
admin_concurrent: enable
admin_console_timeout: 0
admin_hsts_max_age: 15552000
<more key: value>
Keep on automating!
Just look at module examples here: https://docs.ansible.com/ansible/latest/collections/fortinet/fortios/fortios_system_global_module.html#ansible-collections-fortinet-fortios-fortios-system-global-module

Override Ansible hosts on a specific role

I have a playbook like below
- name: Do something
hosts: "view-servers"
roles:
- { role: role1, var1: "abc" }
- { role: role2, var2: "def" }
- { role: role2, var2: "ghi" }
The servers in view-servers are identical and replicated. So there is no difference from variable point of view except the host name.
On the role1 above, I need to actually run it just for 1 of the view servers. Something like view-servers[0].
Is there a way to do it?
The playbook yaml is actually a list, which is why they all start with - hosts: (err, or - name: in your case, but most don't have named playbooks)
Thus:
- hosts: view-servers
roles:
- role: role1
- hosts: view-servers[0]
roles:
- role: role1
And because they are a list, it will run them in the order they exist in the file; so if you want that view-servers[0] to run first, move it before the - hosts: view-servers, else it'll run them all, and then re-connect to the first one of the group and apply the specified roles to it.
Be forewarned that view-servers[0] is highly dependent upon your inventory, so be careful that the 0th item in that group is always the server you intend. If you need more exacting control, you can use a dynamic inventory script, or you can use the add_host: task to choose, or create, a host and add it to a (new or existing) group as a side-effect of your playbook.

Executing roles based on variables

I'm having some odd behavior. I'm still an Ansible novice, so I apologize in advance.
I'm trying to conditionally execute a role, based on an example I saw here: https://github.com/atomicobject/ansible-laptop-playbook-example/blob/1785f25014fa5a7776de5b332d093b60bf59c8e0/laptop.yml
Anyway, I have a host_vars/localhost:
has_thing: true
In my top-level site.yml, I have:
-name: Title
hosts: all
roles:
- { role: myrole, when: has_thing is defined and has_thing == "true" }
This skips the role.
If I do this instead:
-name: Title
hosts: all
roles:
- { role: myrole, when: has_thing is defined }
the role executes.
If I do this:
-name: Title
hosts: all
roles:
- { role: myrole, when: has_thing == "true" }
It does not work.
If I do:
-name: Title
hosts: all
roles:
- { role: myrole, when: has_thing != "true" }
It still does not work. I don't understand why, the variables has_thing is clearly defined because it works as long as I don't test its value.
What am I missing here?
has_thing: true is a boolean, you may test with has_thing: 'true'.
But better change your when statement to handle boolean values.
P.S. And you can't conditionally execute a role a role this way, role will be always executed, but the when statement will be appended to every task in this role generating a lot of skipped tasks in the output.

Ansible Dict and Tags

I have a playbook creating EC2 by using a dictionary declared in vars: then registering the IPs into a group to be used later on.
The dict looks like this:
servers:
serv1:
name: tag1
type: t2.small
region: us-west-1
image: ami-****
serv2:
name: tag2
type: t2.medium
region: us-east-1
image: ami-****
serv3:
[...]
I would like to apply tags to this playbook in the simplest way so I can create just some of them using tags. For example, running the playbook with --tags tag1,tag3 would only start EC2 matching serv1 and serv3.
Applying tags on the dictionary doesn't seem possible and I would like to avoid doing multiplying tasks like:
Creatinge EC2
Register infos
Getting private IP from previously registered infos
adding host to group
While I already have a working loop for the case I want to create all EC2 at once, is there any way to achieve that (without relying on --extra-vars, which would need key=value) ? For example, filtering out the dictionary by keeping only what is tagged before running the EC2 loop ?
I doubt you can do this out of the box. And not sure this is good idea at all.
Because tags are used to filter tasks in Ansible, so you will have to mark all tasks with tags: always.
You can accomplish this with custom filter plugin, for example (./filter_plugins/apply_tags.py):
try:
from __main__ import cli
except ImportError:
cli = False
def apply_tags(src):
if cli:
tags = cli.options.tags.split(',')
res = {}
for k,v in src.iteritems():
keep = True
if 'name' in v:
if v['name'] not in tags:
keep = False
if keep:
res[k] = v
return res
else:
return src
class FilterModule(object):
def filters(self):
return {
'apply_tags': apply_tags
}
And in your playbook:
- debug: msg="{{ servers | apply_tags }}"
tags: always
I found a way to match my needs without touching to the rest so I'm sharing it in case other might have a similar need.
I needed to combine dictionaries depending on tags, so my "main" dictionary wouldn't be static.
Variables became :
- serv1:
- name: tag1
type: t2.small
region: us-west-1
image: ami-****
- serv2:
- name: tag2
type: t2.medium
region: us-east-1
image: ami-****
- serv3:
[...]
So instead of duplicating my tasks, I used set_fact with tags like this:
- name: Combined dict
# Declaring empty dict
set_fact:
servers: []
tags: ['always']
- name: Add Server 1
set_fact:
servers: "{{ servers + serv1 }}"
tags: ['tag1']
- name: Add Server 2
set_fact:
servers: "{{ servers + serv2 }}"
tags: ['tag2']
[..]
20 lines instead of multiply tasks for each server, change vars from dictionary to lists, a few tags and all good :) Now if I add a new server it will only take a few lines.

Ansible looping over nested tasks

Is there any way call one task from another, loop through some array and pass variable to this other task?
- include: { task: create_user.yml user: item }
with_items: users
or something like that.
You can just assume that "user" is available to creater_user.yml
# something like this:
- name: create user
include creater_user.yml
# creater_user.yml:
- name: some task
debug: msg="hello"
with_items: users

Resources