ansible passing to a mapped variable - ansible

I have an ansible tomcat role
defaults/main.yml
tomcat_http:
port: "8080"
protocol: "HTTP/1.1"
connectionTimeout: "120000"
URIEncoding: "UTF-8"
I have another role (app) which uses tomcat role as a dependency and looks like below
defaults/main.yml
app_uriencoding: "ISO-8859-1"
meta/main.yml
dependencies:
- { role: common, tags: app }
- { role: tomcat, tomcat_http.URIEncoding: "{{ app_uriencoding }}", tags: app }
When I run the app role on my targets, Im expecting the URIEncoding value defined in the app role (ISO-8859-1) to be passed to the tomcat role and override the tomcat role default value for uriencoding.
Im unable to pass a value into tomcat roles' {{ tomcat_http.URIEncoding }}. Some of the options I have tried
tomcat_http.URIEncoding
tomcat_http[URIEncoding]
tomcat_http.["URIEncoding"]
Either I get syntax errors or it just doesn't work. Please let me know if anybody has any ideas on how to pass a value into a mapped variable.

Generally, this is not possible, because with default (and advised) Ansible configuration variables overrides lower-priority ones.
But there is hash_behavior option, which you can set to merge.
In this case, you can use:
- role: tomcat
tomcat_http:
URIEncoding: "{{ app_uriencoding }}"
tags: app
This way tomcat_http from role's var will be merged with role's defaults. But beware, this can brake some other parts of your playbooks.
If you expect role's defaults to be overriden independently, use:
tomcat_http_port: "8080"
tomcat_http_protocol: "HTTP/1.1"
tomcat_http_connectionTimeout: "120000"
tomcat_http_URIEncoding: "UTF-8"

Related

How to have multiple tasks under one role in Ansible?

I am setting up Ansible as a newbie. I would like to group a few tasks under an Nginx role.
See my folder structure
The only way I could get this to work is to use include statements in my playbooks... but that means I needed to start changing everything to relative paths since Ansible stopped being able to find things.
My playbook currently:
- name:
Install Nginx, Node, and Verdeccia
hosts:
all
remote_user:
root
tasks:
- include: ../roles/nginx/tasks/install.yml
- include: ../roles/nginx/tasks/create_node_config.yml
vars:
hostname: daz.com
How can I reference the sub task in the playbook to be something like
tasks:
- nginx.install
and still be within best practices.
Your use of roles so far is not in line with the norm or the idea of Ansible. From a purely technical point of view, it is quite possible that you have multiple task bundles in yml files that you include in the playbook.
If you want to include a specific task file from a role, you should better do this via the module include_role with the parameter tasks_from.
However, the workflow with roles usually looks different.
in the folder tasks should always be a file main.yml, this is called automatically, if simply the role is included, no matter over which way.
in this main.yml you can then add further control logic to include your yml files as required.
As it looks to me, for example, you always need to install, but depending on the use case you want to have different configurations.
create in your nginx role a file defaults/main.yml.
---
config_flavor: none
This initializes the config_flavor variable with the string none.
create in your nginx role a file tasks/main.yml.
---
- name: include installation process
include_tasks: install.yml
- name: configure node
include_tasks: create_node_config.yml
when: config_flavor == "node"
- name: configure symfony
include_tasks: create_symfony_config.yml
when: config_flavor == "symfony"
- name: configure wordpress
include_tasks: create_wordpress_config.yml
when: config_flavor == "wordpress"
The main.yml will be included by default when the role is applied.
This is where the installation is done first, then the proper configuration is done.
Which configuration should be done is defined by the variable config_flavor. In the listed example the values are node, symfony and wordpress. In all other cases the installation will be done, but no configuration (so in the default case with none).
include your role in the playbook as follows
---
- name: Install Nginx, Node, and Verdeccia
hosts: all
remote_user: root
vars:
hostname: daz.com
roles:
- role: nginx
config_flavor: node
At this point you can set the value for config_flavor. If you set wordpress instead, the tasks create_wordpress_config.yml will be included automatically, using the logic from tasks/main.yml.
You can find more about roles in the Ansible Docs.
There are many more possibilities and ways, you will get to know them in the course of your learning. Basically I would recommend you to read a lot in the Ansible docs and to use them as a reference book.
Please refer to bellow document. you can use "include_role" with "tasks_from" attribute. you don't need to provide full path of task files, Just file names.
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/include_role_module.html
Example:
- name: Run tasks/other.yaml instead of 'main'
ansible.builtin.include_role:
name: myrole
tasks_from: other

Ansible role dependencies and facts with delegate_to

The scenario is: I have several services running on several hosts. There is one special service - the reverseproxy/loadbalancer. Any service needs to configure that special service on the host, that runs the rp/lp service. During installation/update/deletion of a random service with an Ansible role, I need to call the ReverseProxy role on the specific host to configure the corresponding vhost.
At the moment I call a specific task file in the reverse proxy role to add or remove a vhost by the service with include_role and set some vars (very easy example without service and inventory specific vars).
- name: "Configure ReverseProxy"
include_role:
name: reverseproxy
tasks_from: vhost_add
apply:
delegate_to: "{{ groups['reverseproxy'][0] }}"
vars:
reverse_proxy_url: "http://{{ ansible_fqdn }}:{{ service_port }}/"
reverse_proxy_domain: "sub.domain.tld"
I have three problems.
I know, it's not a good idea to build such dependencies between roles and different hosts. I don't know a better way, especially if you think about the situation, where you need to do some extra stuff after creating the vhost (f.e. configure the service via REST API, which needs the external fqdn). In case of two separate playbooks with "backend"-service and "reverseproxy"-service - then I need a third playbook for configuring "hanging" services. Also I'm not sure, if I can retrieve the correct backend URL in the reverse proxy role (only think about the HTTP scheme or paths). That sounds not easy, or?
Earlier I had separate roles for adding/removing vhosts to a reverseproxy. This roles didn't have dependencies, but I needed to duplicate several defaults and templates and vars etc. which isn't nice too. Then I've changed that to a single role. Of course - in my opinion, this isn't really that, what a "role" should be. A role is something like "webserver" or "reverseproxy" (a state). But not something like "add_vhost_to_reverseproxy" (a verb). This would be something like a playbook - but is calling a parameterized playbook via a role a good idea/possible? The main problem is, that the state of reverseproxy is the sum of all services in the inventory.
In case of that single included role, including it, starts also all dependent roles (configure custom, firewall, etc.). Nevertheless in that case I found out, that the delegation did not use the facts of the delegated host.
I tested that with the following example - the inventory:
all:
hosts:
server1:
my_var: a
server2:
my_var: b
children:
service:
hosts:
server1:
reverseproxy:
hosts:
server2:
And playbook which assigns a role-a to the group webserver. The role-a has a task like:
- block:
- setup:
- name: "Include role b on delegated {{ groups['reverseproxy'][0] }}"
include_role:
name: role-b
delegate_to: "{{ groups['reverseproxy'][0] }}"
delegate_facts: true # or false or omit - it has no effect on Ansible 2.9 and 2.10
And in role-b only outputing the my_var of the inventory will output
TASK [role-b : My_Var on server1] *******************
ok: [server1 -> <ip-of-server2>] =>
my_var: a
Which says me, that role-b that should be run on server2 has the facts of server1. So - configuring the "reverseproxy" service is done in context of the "backend"-service. Which would have several other issues - when you think about firewall-dependencies etc. I can avoid that, by using tags - but then I need to run the playbook not just with the tag of the service, but also with all tags I want to configure, and I cannot use include_tasks with args-apply-tags anymore inside a role that also includes other roles (the tags will applied to all subtasks...). I miss something like include_role but only that specific tags or ignore dependencies. This isn't a bug, but has possible side effects in case of delegate_to.
I'm not really sure, what is the question? The question is - what is a good way to handle dependencies between hosts and roles in Ansible - especially when they are not on the same host?
I am sure I do not fully understand your exact problem, but when I was dealing with load balancers I used a template. So this was my disable_workers playbook:
---
- hosts: "{{ ip_list | default( 'jboss' ) }}"
tasks:
- name: Tag JBoss service as 'disabled'
ec2_tag:
resource: "{{ ec2_id }}"
region: "{{ region }}"
state: present
tags:
State: 'disabled'
delegate_to: localhost
- action: setup
- hosts: httpd
become: yes
become_user: root
vars:
uriworkermap_file: "{{ httpd_conf_dir }}/uriworkermap.properties"
tasks:
- name: Refresh inventory cache
ec2_remote_facts:
region: "{{ region }}"
delegate_to: localhost
- name: Update uriworkermap.properties
template:
backup: yes
dest: "{{ uriworkermap_file }}"
mode: 0644
src: ./J2/uriworkermap.properties.j2
Do not expect this to work as-is. It was v1.8 on AWS hosts, and things may have changed.
But the point is to set user-defined facts, on each host, for that host's desired state (enabled, disabled, stopped), reload the facts, and then run the Jinja template that uses those facts.

Does Ansible duplicate role variables for role dependencies that enable "allow_duplicates"?

Does Ansible duplicate role variables for role dependencies that have enabled allow_duplicates?
For example, given a playbook that includes more than once role application-environment that allows duplicates, will Ansible create multiple copies of its variables?
meta/main.yml:
---
allow_duplicates: yes
dependencies:
- src: git+http://javasource/git/ansible/roles/organization
version: 1.1.0
vars/main.yml:
---
application_directory: "{{ organization.directory }}/{{ application_name }}"
application_component_directory: "{{ application_directory }}/{{ application_component_name }}"
If Ansible does not create multiple copies of these variables, how could I rework the role so that it can support multiple variables?
You may find some helpful information here:
About vars:
Anything in the vars directory of the role overrides previous versions of that variable in namespace.
About defaults:
Tasks in each role will see their own role’s defaults. Tasks defined outside of a role will see the last role’s defaults.

Ansible variable override default in another role

I'm unsure how to override variables between roles in Ansible.
To simplify the setup a little, I have two roles applied to the same host. The first role defines a variable in its default/main.yml:
do_some_task: yes
And looks for that variable in its tasks:
- name: Some Task
when: do_some_task
The second role overrides that in its vars/main.yml, which is supposed to take precedence over the defaults:
do_some_task: no
However, the task is still being run, indicating that the variable wasn't overridden. It seems that the override is scoped to the tasks of the second role. I tested that by adding a debug task to both roles:
- name: Test some task
debug: "msg='do_some_task = {{ do_some_task }}'"
This confirms that the first role sees a different value of the variable than the second.
TASK: [role1 | Test some task]
ok: [myhost] => {
"msg": "do_some_task = True"
}
...
TASK: [role2 | Test some task]
ok: [myhost] => {
"msg": "do_some_task = False"
}
The common answer to this appears to be to set the variables in the inventory or the host vars. However this isn't particularly DRY: if you have many hosts in different inventories, you'd have to set the same variables in lots of places.
So is there some way of overriding a variable from another role?
If you use Ansible >= 2.2, you maybe able to use include_roles:
- name: Pass variables to role
include_role:
name: role1
vars:
do_some_task: "{{ role2_do_some_task }}"
You can also use dependencies of roles for Anslbie >= 1.3. Write the role2/meta/main.yml like:
---
dependencies:
- { role: role1, do_some_task: yes }
If you want neither, given that you don't want to put the variable definition in playbook, the only sensible place to define them is in inventory.
You can define the ones that need to overwrite the role1 default in the same group:
[overwrite_do_some_task]
host1
host3
[overwrite_do_some_task:vars]
do_some_task=yes
If you don't give the variable value before running role1, then the expected behavior is, use the role default, which is what OP observed.
Note: It is more readable to prefix role variables. i.e. use role1_do_some_task instead of do_some_task. Doing so, you are less likely to confuse yourself. See Ansible Best Practices: The Essentials.
If you want to wrap a role with another role then you can override the inner role's variable by specifically passing variables from the outer role.
So as per your example you would have an inner role task of:
- name: Some Task
when: do_some_task
This inner role would have a variable set like this:
do_some_task: true
And then you can override the inner role when you are calling your outer role in a playbook like this:
- name: outer-role
hosts: outer-role-nodes
roles:
- role: outer-role
- { role: inner role,
do_some_task: "{{ do_some_task }}" }
This should take the do_some_task variable from the outer role and override the inner role's do_some_task.
TLDR : to be able to inherit defaults/ config, you have to separate it into other roles and include it where it's going to be used
One way to apparently work around this is to create a separate role which will hold the defaults, and then import/include it in your main role. The roles you create with the default configurations can import each other as a way of "inheriting" configuration from defaults, and then the main role would load the top-most configuration role and take all the configs as expected..
For example, in my case I want to have a templating system which has a "stacked" level of configuration.. all of them inheriting from a base set of config in defaults, and not vars, otherwise you can't override it from inventory.
these are the roles:
template-base
template-config-standard
template-config-performance
-> Template-base has the whole logic for deploying templates based on the selected configuration.. this has some logic to include the selected config role on the tasks files
-> template-config-standard has the standard set of config defined in defaults/main.yml, and nothing else
-> template-config-performance has some additional tweaking on the defaults/main.yml file, but does not repeat anything from template-config-standard, and instead simply uses it on "import_role" on the tasks/main.yml file.. probably dependencies still work
In a bit more examplified way:
role template-base:
- files/ (contains all the template files)
- tasks/
|---- main.yml (contains the code below)
# Includes the template configuration based on the selected template
- include_role:
name: "template-config-{{ selected_template}}"
public: true # Important so that configs are exposed here
- ** Does templating work **
role template-config-std:
- defaults/
|---- main.yml (contains a bunch of configuration)
role template-config-perf:
- defaults/
|---- main.yml (contains just a few config items, overriding stuff from the role above)
- tasks/
|---- main.yml (contains below code)
#Simply includes the standard configuration.. all this does is take defaults in the defaults/main.yml file from the below role
- import_role:
name: template-config-std
The approach above seems to work just fine, respecting the desired inheritage of variables and precedence. It looks like you cannot have the role that will use the variables competing with the roles that contain the default variables, unfortunately, because it will always win.. but in any case the example above seems okay :).. if you need to add more things other than just configuration to the supporting roles, you have to create a kind of mechanism on the main role, but hopefully nothing very complex..
Good luck and have a good one!
PS: one of the most important aspects of this approach is that we keep the default/ precedence on the configuration, meaning the inventory can override it if needed!

Vagrant ignores tags if i specify them in ansible configuration

I have a very simple ansible configuration:
- name: Prepare and intsall RethinkDB on available machines
hosts: all
roles:
- { role: rethinkdb, tags: ["install"] }
And a corresponding vagrant config:
universe.vm.provision "ansible" do |a|
a.playbook = "configuration.yml"
a.groups = {
"primary" => ["rmaster"],
"secondary" => ["rsecondary"]
}
end
The problem is if i call vagrant provision ansible runs rethinkdb role ignoring specified in configuration.yml file install tag. Though it works if i put it into vagrant as a.tags = "install", but it won't work if i have a sequence of ansible tasks to run with different tags, so i'd like to specify them from ansible playbook.
As far as I know, it's not possible for a playbook to define the role tags that will be run by the role. What tags: ["install"] does is to assign the install tag to the tasks from the rethinkdb role ("you may wish to assign tags to the roles you specify").
If your install, configure, launch, etc. tags are run in sequence, then you should be able to dispatch them to different (possibly dependent) roles. If not, then your only other solution is to run individual tasks using not tags but variable values.
E.g:
in roles/rethinkdb/tasks/main.yml:
- name: my_task
when: run_rethinkdb_install
in playbook.yml:
- name: Prepare and intsall RethinkDB on available machines
hosts: all
vars:
run_rethinkdb_install: True
roles:
- { role: rethinkdb, tags: ["install"] }

Resources