I cannot find the answer in the docs, neither I can find an example: is it possible to name files in role/defaults/ other then main.yml? I mean, is main.yml the only valid name for a file with default values?
By the sound of this documentation;
http://docs.ansible.com/ansible/playbooks_roles.html#roles
Roles are just automation around ‘include’ directives as described
above, and really don’t contain much additional magic beyond some
improvements to search path handling for referenced files. However,
that can be a big thing!
This designates the following behaviors, for each role ‘x’:
If roles/x/tasks/main.yml exists, tasks listed therein will be added
to the play
If roles/x/handlers/main.yml exists, handlers listed
therein will be added to the play
If roles/x/vars/main.yml exists,
variables listed therein will be added to the play
If
roles/x/defaults/main.yml exists, variables listed therein will be
added to the play
If roles/x/meta/main.yml exists, any role
dependencies listed therein will be added to the list of roles (1.3
and later) Any copy, script, template or include tasks (in the role)
can reference files in roles/x/{files,templates,tasks}/ (dir depends
on task) without having to path them relatively or absolutely
Q: I mean, is main.yml the only valid name for a file with default values?
A: Sounds like it is only if you are wanting ansible to automatically load certain var_files for that role and auto include the task file.
Q: Is it possible to name files
A: Yes, it just means in the playbook you have to use the include directive and explicitly point it to the unique task file name.
Example:
- name: Do role
hosts: localhost
tasks:
- include: roles/operations/tasks/installProgram.yml
As you tell in the comments, it's possible to call a file anything else under the roles/my_role/defaults/. The issue is that files like roles/my_role/defaults/anything_else.yml will never be loaded as default. Even you will be able to load it with include_vars, it will gain the high precedence of included vars (18/22).
Nevertheless, since Ansible 2.6, your are able to use a directory main in place of a main.yml file!
By doing so, you can reach your goal by splitting your variables into roles/my_role/defaults/main/something.yml, roles/my_role/defaults/main/anything_else.yml, …
More details in this StackOverflow answer.
But indeed this feature is not yet documented (I just created an issue about that).
Ansible will look in the roles/ directory for tasks, variables, handlers, etc by default. roles/default is simply an example provided in their docs. You can structure your roles differently to suit your needs. For example, I have mine set up as such;
roles/windows/<somerole>/tasks/main.yml
roles/linux/<somerole>/tasks/main.yml
main.yml is the only file that will be loaded automatically (by "default") when running playbooks. If you want to specify another file name for whatever reason you need to use the include function and point to your file as described above.
The "Best Practices" guide offers some good insight into directory structuring.
Related
I'm trying to assemble an iptables file suitable for iptables-restore using multiple roles:
There's a role setup-iptables that creates an initial file containing some common rules (i.e. allowing SSH). The file is created from a big template.
There're multiple smaller roles, that add to this file (i.e. setup-httpd adds rules for ports 80 and 443).
Playbooks are supposed to import the role setup-iptables first and then import the smaller roles that are needed to configure the host.
My problem is, that this file gets recreated every time the playbook is run. This is potentially dangerous if the play gets interupted and the incomplete rules get applied. Also it's ugly because ansible will show changes to the host, even if the resulting file is identical to its original state.
Simply working with lineinfile and blockinfile from the beginnning is not an option because the iptable rules are highly dependend on the ordering of the rules. The initial template is required because it provides a lot of 'markers'. These markers are then used by the lineinfile statements of the subsequent roles to corretly position their rules.
My current workaround idea is to assemble the file locally and at the end of the play 'flush' it to the remote host.
Is there a better way? What's the 'ansible way' to do this?
You can check the file exists and if it does, skip recreating it...
- name: check if the file exists
stat:
path: /etc/sysconfig/iptables #Example path
register: iptables_file
- name: Create the file if it doesnt exists
copy:
content: "your initial rules"
dest: /etc/sysconfig/iptables
when: iptables_file.stat.exists == False
I have a situation where we have 3 tiers of boxes, in each tier we apply different variables settings (like where the cache dir is), but there are a bunch of defaults. I also need to override on a per node basis, which is usually done via inventory vars on the host itself. I am not sure what is the best way to organize the hosts so that the precedence works in my favor.
Here are the different things I have tried. In each case I have entries in the inventory file like this:
[bots-fancy]
fancy-1
[bots-super-fancy]
super-1
[bots-magical]
magic-1
magic-2 provider=aws
At first, I had each of them with a long string of variable definitions. I also had different group_var/bots/[bots-magical | bots-super-fancy | bots-fancy].yaml files. This quickly became untenable.
attempt 1: with playbook variables
In the playbook I had something like this:
---
hosts:
- bots
vars_files:
- "group_vars/bots/defaults.yml"
- "group_vars/bots/{{ groups_names[0] }}.yml"
roles:
- somethign
this worked (though yes brittle) but it wouldn't let me override on a per host basis. I had to set things different on nodes occasionally, but not on the whole group.
attempt 2: using group_vars for each
I added
[bots:children]
bots-fancy
bots-super-fancy
bots-magical
to the hosts file. Removed any vars_files from the playbook and created group_vars for each group. I added the default/shared settings to group_vars/bots.yaml. When I'd run the playbook, it would only load the bots group_vars it seemed. Ideally, I want it to load the bots and then override it with the bots-fancy. And then finally the values from the hosts file.
I am not sure the best way to structure these groups, so any input would be very helpful!
Not sure what is your problem. You should be fine with:
hosts:
[bots-a]
bot1
[bots-b]
bot2
[bots:children]
bots-a
bots-b
dirs:
./group_vars/bots.yml
./group_vars/bots-a.yml
./group_vars/bots-b.yml
There is a concept of group depth in Ansible (at least in recent versions). In this example, group variables for host bot2 will be populated in the following order:
depth 0: group all, all.yml (missing here, ignoring)
depth 1: group bots, bots.yml
depth 2: group bots-b, bots-b.yml
You can see details and processing order here in the source code.
So if you define defaults in bots.yml and specific values in bots-b.yml, you should achieve what you expect.
(I'm currently running Ansible 2.1)
I have a playbook that gathers a list of elements and I have another playbook (that calls different hosts and whatnot) using said element as the basis for most operations. Therefore, whenever I use with_items over the playbook, it causes an error.
The loop control section of the docs say that "In 2.0 you are again able to use with_ loops and task includes (but not playbook includes) ". Is there a workaround? I really need to be able to call multiple hosts in an included playbook that runs over a set of entries. Any workarounds, ideas for such or anything are greatly appreciated!
P.S. I could technically command: ansible-playbook but I dont want to go down that rabbit hole if necessary
I think I faced same issues, and by the way, migrating to shows more than in 'item' already in use.
refering to http://docs.ansible.com/ansible/playbooks_best_practices.html , you should have an inventory (that contains all your hosts), and a master playbook (even if theorical).
A good way, instead of including playbooks, is to design roles, even if empty. Try to find a "common" role for everything that could be applied to most of your hosts.Then, include additional roles depending of usage, this will permit you to trigg on correct hosts.
You can also have roles that do nothing (meaning, nothing in 'tasks'), but that contain set of variables that can be common for two roles (you avoid then duplicate entries).
When creating a new Ansible role, the template creates both a vars and a defaults directory with an empty main.yml file. When defining my role, I can place variable definitions in either of these, and they will be available in my tasks.
What's the difference between putting the definitions into defaults and vars? What should go into defaults, and what should to into vars? Does it make sense to use both for the same data?
I know that there's a difference in precedence/priority between the two, but I would like to understand what should go where.
Let's say that my role would create a list of directories on the target system. I would like to provide a list of default directories to be created, but would like to allow the user to override them when using the role.
Here's what this would look like:
---
- directories:
- foo
- bar
- baz
I could place this either into the defaults/main.yml or in the vars/main.yml, from an execution perspective, it wouldn't make any difference - but where should it go?
The Ansible documentation on variable precedence summarizes this nicely:
If multiple variables of the same name are defined in different places, they win in a certain order, which is:
extra vars (-e in the command line) always win
then comes connection variables defined in inventory (ansible_ssh_user, etc)
then comes "most everything else" (command line switches, vars in play, included vars, role vars, etc)
then comes the rest of the variables defined in inventory
then comes facts discovered about a system
then "role defaults", which are the most "defaulty" and lose in priority to everything.
So suppose you have a "tomcat" role that you use to install Tomcat on a bunch of webhosts, but you need different versions of tomcat on a couple hosts, need it to run as different users in other cases, etc. The defaults/main.yml file might look something like this:
tomcat_version: 7.0.56
tomcat_user: tomcat
Since those are just default values it means they'll be used if those variables aren't defined anywhere else for the host in question. You could override these via extra-vars, via facts in your inventory file, etc. to specify different values for these variables.
Edit: Note that the above list is for Ansible 1.x. In Ansible 2.x the list has been expanded on. As always, the Ansible Documentation provides a detailed description of variable precedence for 2.x.
Role variables defined in var have a very high precedence - they can only be overwritten by passing them on the command line, in the specific task or in a block. Therefore, almost all your variables should be defined in defaults.
In the article "Variable Precedence - Where To Put Your Role Vars" the author gives one example of what to put in vars: System-specific constants that don't change much. So you can have vars/debian.yml and vars/centos.yml with the same variable names but different values and include them conditionally.
IMHO it is impractical and not sensible that Ansible places such high priority on configuration in vars of roles. Configuration in vars/main.yml and defaults/main.yml should be low and probably the same priority.
Are there any real life examples of cases where we want this type of behavior?
There are examples that we dont' want this.
The point to make here is that configuration in defaults/main.yml cannot be dynamic. Configuration in vars/main.yml can. So for example you can include configuration for specific OS and version dynamically as shown in geerlingguy.postgresql
But because precedence is so strange and impractical in Ansible geerlingguy needs to introduce pseudo variables as can be seen in variables.yml
- name: Define postgresql_packages.
set_fact:
postgresql_packages: "{{ __postgresql_packages | list }}"
when: postgresql_packages is not defined
This is a concrete real life example that demonstrates that the precedence is impractical.
Another point to make here is that we want roles to be configurable. Roles can be external, managed by someone else. As a general rule you don't want configuration in roles to have high priority.
Basically, anything that goes into “role defaults” (the defaults folder inside the role) is the most malleable and easily overridden. Anything in the vars directory of the role overrides previous versions of that variable in namespace. The idea here to follow is that the more explicit you get in scope, the more precedence it takes with command line -e extra vars always winning. Host and/or inventory variables can win over role defaults, but not explicit includes like the vars directory or an include_vars task.
doc
Variables and defaults walk hand in hand. here's an example
-name: install package
yum: name=xyz{{package_version}} state=present
in your defaults file you would have something like:
package_version: 123
What ansible will do is, it's gonna take the value of package_version and put it next to the package name so it will read somewhere as:
-name: install package
yum: name=xyz123 state=present
This way it will install xyz123 and not xyz123.4 or whatever is in the great repository of xyz's.
At the end it will do yum install -y xyz123
So basically the defaults are the values present, if you do not set a specific value for the variables, cause that space can't stay empty.
Is it possible to declare variables the inventory level? For example I have a inventory for my development env, and another one for my production env.
I have templates that I need populated with certain variables that are specific to either environment.
At the moment it seems I can only set these variables at the group or host level, but I want it to apply for the entire inventory to avoid repeating myself for each group or host I have defined!
It turns out, my previous answer was not so right. There is an easier solution. Just define the vars for the all group. I would have expected ansible complaining there is no matching group definition, but it works, I just tested it:
[some_group]
some_host
[another_group]
another_host
[all:vars]
some_var=some_value
But in this case you really are forced to define the vars in the inventory file and can not define it in group_vars/all, but this should be obvious.
Leaving this here for reference, but check out my other answer, there is a simpler way to do it.
I believe this is not directly possible. You can define variables directly in the inventory, but they still have to be defined per host or group.
You could create a group which inherits from all other groups of that inventory file and then assign the variables to this group.
Inventory file myInventory:
[some_group]
some_host
[another_group]
another_host
[myInventory:children]
some_group
another_group
[myInventory:vars]
some_var=some_value
Separate inventories are a very good idea. The above answer's fully accurate, but I don't believe you'll be able to follow that approach if your groups are split up across inventory files, which I think your question implies they are.
Depending on your platform (the mode being AWS), it's effortless to group all your machines by environment, and it doesn't matter which inventory or inventories pick them up. I would just add an environment group variable file.
Please edit if group_vars are something you're trying to avoid categorically. If that's the case, then appropriate or create your own inventory scripts (adding a constant variable to the ec2 one is very very easy).