inserting logic into ansible variable - ansible

very noob question, I have this piece of ansible code in /vars/myvars.yml:
service: "{{ 'service1' if litespeed|bool==true else 'service2' }}"
services_node:
- foo
- bar
- "{{ service }}"
- baz
i have tried to look online for my answer but cannot find anything, what syntax is in the curly braces? Searching online results in "there is no if/else in ansible or YAML" in most cases. To what kind of query or language that syntax is from?

Ansible uses Jinja for templating. You should read ansible templating doc.
You'll also find a lot of useful informations on Jinja's template designer doc.
In your example, the syntax with the pipe (the | symbol) is the syntax for filters. Here you'll find the list of builtin filters with documentation and examples.
You'll see that the bool filter is not listed as it is a filter brought by ansible. You'll find the list of these filters here.
The template itself is not part of Yaml, that's why it is expressed as a string.

The braces are part of a Jinja2 template within the YAML data. See Templating (Jinja2) for complete information.

Related

Using variables in yaml

I have defined an entry in a YAML file like this
run1:
exec_dir: "/home/mahmood/app/bin/"
data_dirs: "/home/mahmood/data/1/2/3/"
execs:
- sim:
- args: market input_1.txt
In fact input_1.txt exists in data_dirs. So, I would like to use data_dirs as a variable in the args. How can I define that?
YAML isn't a programming language, nor is it a templating language. It doesn't have variables (you can use anchors & aliases to simulate variables to some degree though). It doesn't have procedural instructions, and you'd need concatenation/interpolation to insert the scalar content of a YAML node into another YAML node.
Your options are either to use a templating language on top of YAML, which is done by a lot of heavy YAML users like e.g. Ansible and Kubernetes Helm, or you could implement special logic on loading if you are in control of the loading code. If you are writing YAML for an existing tool, chances are that it already provides one of those options.

Alternative ansible syntax

I've seen a playbook which looks something like this:
- hosts:
- foo
- bar
roles:
- role: whatever
It works, but from the documentation I would have expected that:
a. Hosts would be given as a single space separated line e.g.:
- hosts: foo bar
rather than a list.
b. The value for the "roles" key in the play would be a list, e.g.:
roles:
- whatever
rather than a key:value pair.
Can someone explain what I'm missing either in yaml which makes these alternatives equivalent once parsed, or where in the ansible docs it explains these alternative definitions?
TL;DR
For hosts use the syntax that you and the other people working with this are most comfortable with.
For roles, you need the role: <name> syntax only in cases where you want to also set other attributes for the role.
Longer answer
I have wondering about this occasionally as well.
In the docs section Intro to Playbooks, Basics, it says:
The hosts line is a list of one or more groups or host patterns, separated by colons, as described in the Working with Patterns documentation.
It does, however, not mention explicitly that this list, could also be a space separated string.
As far as the roles attribute of a play is concerned, I think the alternate syntax variant is straight forward. If you just pass a name (a single string), then this is obviously the name of the role.
If you want to pass additional arguments, like variables, then you need to create a dictionary. See an example of the two syntaxes used together here in the docs (search for "Roles can accept other keywords").
The definite answer to both questions is in the source code:
Here is the part that parses the hosts list in a play:
https://github.com/ansible/ansible/blob/devel/lib/ansible/playbook/play.py#L104-L116
Here is the part that does it for a role in roles:
https://github.com/ansible/ansible/blob/devel/lib/ansible/playbook/role/definition.py#L68-L135
There is another hint in the playbook/base.py#preprocess_data:
infrequently used method to do some pre-processing of legacy terms
The Play class for example inherits / overrides this method, directly below the snippet I linked to above.

Remove Config Lines on ASA with Ansible

I have an ansible playbook that creates a network object and sets ACL policies. It's working well, but I would like to create the complementary playbook to remove the object and its associated config but I don't know the correct way to approach the task.
I could just use asa_command to issue the 'no' prefix for the appropriate lines, however, that doesn't feel like the "Ansible Way" since it would try to execute the commands even if they were already absent in the config.
I have seen that some modules have a state: absent operator. However, the asa_ modules don't indicate that as an option.
Any suggestions would be much appreciated.
I think having a state: absent option makes a lot of sense, as I don't think there is a simple way of doing this more efficiently with the current asa_ modules. The Ansible team is extremely responsive to issues and PRs, so I would submit one for this feature.
It looks like there isn't a clean way to do this as of Ansible 2.4. I have a working playbook, however, I had to settle for issuing the no commands using asa_config and putting ignore_errors: yes in for each play. It's inelegant to say the least and in some cases can break down. I think there may be a way to use an error handling along with check_mode: yes. My initial attempt at this failed because when registering the result of a play to a variable, I cannot use that variable to interpret which of the affected hosts actually required a change it's just a generic yes/no for the entire play.
What I'm doing currently:
- name: Remove Network Object
asa_config:
commands:
- no object network {{ object_name }}
provider: "{{ cli }}"
ignore_errors: yes
register: dno

Prevent duplicate key warnings in Ansible 2

I use a lot of YAML anchors and references in my roles to keep the logic in a single spot instead of repeating myself in multiple tasks. Following is a very very basic example.
- &sometask
name: "Some Task"
some_module: with a lot of parameters
with_items: list_A
- <<: *sometask
name: "Some OTHER Task"
with_items: list_B
This example might not show how this is actually useful, but it is. Imagine you loop over a list of dicts, passing various keys from each dict to the module, maybe having quite complex "when", "failed_when" and "changed_when" conditions. You simply want to DRY.
So instead of defining the whole task twice, I use an anchor to the first one and merge all its content into a new task, then override the differing pieces. That works fine.
Just to be clear, this is basic YAML functionality and has nothing to do with Ansible itself.
The result of above definition (and what Ansible sees when it parsed the YAML file) would evaluate to:
- name: "Some Task"
some_module: with a lot of parameters
with_items: list_A
- name: "Some Task"
some_module: with a lot of parameters
with_items: list_A
name: "Some OTHER Task"
with_items: list_B
Ansible 2 now has a feature to complain when keys have been defined multiple times in a task. It still works, but creates unwanted noise when running the playbook:
TASK [Some OTHER Task] *******************************************************
[WARNING]: While constructing a mapping from /some/file.yml, line 42, column 3, found a duplicate dict key (name). Using last defined value only.
[WARNING]: While constructing a mapping from /some/file.yml, line 42, column 3, found a duplicate dict key (with_items). Using last defined value only.
Ansible configuration allows to prevent deprecation_warnings and command_warnings. Is there a way to also prevent this kind of warning?
Coming in late here I'm going to disagree with the other answers and endorse YAML merge. Playbook layout is highly subjective and what's best for you depends on the config you need to describe.
Yes, ansible has merge-like functionality with includes or with_items / with_dict loops.
The use case I've found for YAML merge is where tasks have only a few outliers, therefore a default value which can be overridden is the most compact and readable representation. Having ansible complain about perfectly valid syntax is frustrating.
The comment in the relevant ansible code suggests The Devs Know Better than the users.
Most of this is from yaml.constructor.SafeConstructor. We replicate it here so that we can warn users when they have duplicate dict keys (pyyaml silently allows overwriting keys)
PyYAML silently allows "overwriting" keys because key precedence is explicitly dealt with in the YAML standard.
As of Ansible 2.9.0, this can be achieved by setting the ANSIBLE_DUPLICATE_YAML_DICT_KEY environment variable to ignore. Other possible values for this variable are warn, which is the default and preserves the original behaviour, and error, which makes the playbook execution fail.
See this pull request for details about the implementation.
There is also a system_warnings configuration option, but none of these will silence that output you're seeing.
Here is the code generating that message from
ansible/lib/ansible/parsing/yaml/constructor.py
if key in mapping:
display.warning('While constructing a mapping from {1}, line {2}, column {3}, found a duplicate dict key ({0}). Using last defined value only.'.format(key, *mapping.ansible_pos))
While your use of YAML references is quite clever I doubt this is going to change anytime soon as a core tenant of Ansible is the human readability of the playbooks and tasks. Blocks will help with the repetition of conditionals on tasks, although they seem to be limited to tasks within a playbook at this time..
You could always submit a pull request adding an option for disabling these warnings and see where it goes.
To create reusable functionality at the task level in Ansible you should look into task includes. Task includes will allow you more freedom to do things like iterate using with_items, etc. At my employer we use anchors/references liberally, but only for variables. Given several existing ways of creating reusable tasks in Ansible such as task includes, playbook includes, and roles we have no need to use anchors/references for tasks the way you have described.
If you just want the module args to be copied between tasks you could go the templating route:
args_for_case_x: arg1='some value' arg2=42 arg3='value3'
- name: a task of case x for a particular scenario
the_module: "{{ args_for_case_x }}"
when: condition_a
- name: a different use of case x
the_module: "{{ args_for_case_x }}"
when: condition_b
As you can see though, this doesn't easily support varying the args based on loop iteration which you could get if you used one of the aforementioned reuse features.

Is there a Ansible "not" tag for roles available?

I'm looking for a short way to not execute specific tags that are listed in my playbook.yml. Example:
- hosts: all
roles:
- base
- webdev
roles_not:
- temp_disabled
The problem in my case is, webdev contains a lot of sites and I would like to add just something like temp_disabled instead of changing all tags.
You can provide --skip-tags "temp_disabled" on the command line to skip the tag temp_disabled. You can not skip roles but it's simple to tag all roles with the same tag as the role name.
I know this is really old, but I stumbled across this looking for the same answer. --skip-tags won't work in my situation (the tags I can use are limited).
I ended up using this:
when: "'temp_disabled' not in ansible_run_tags"

Resources