Define `become=yes' per role with Ansible - ansible

In my system provisioning with Ansible, I don't want to specify become=yes in every task, so I created the following ansible.cfg in the project main directory, and Ansible automatically runs everything as root:
[privilege_escalation]
become = True
But as the project kept growing, some new roles should not be run as root. I would like to know if it is possible to have some instruction inside the role that all tasks whithin that role should be run as root (eg. something in vars/), instead of the global ansible.cfg solution above!

I have found a solution, although I think a better solution should be implemented by the Ansible team. Rename main.yml to tasks.yml, and then write the following to main.yml:
---
- { include: tasks.yml, become: yes }
Another solution is to pass the parameter directly in site.yml, but the main idea of the question was reusing the role in other projects without forgetting it needs root:
---
- hosts: localhost
roles:
- { role: name, become: yes }

You can also wrap your tasks in a block and put become: yes on the block. So, inside your roles/role_name/tasks/main.yml, you'd do this:
- block:
- name: Tasks go here as normal
...
become: yes
This will run all the tasks inside the block as root. More details of Ansible's blocks here (latest docs).

Not really a fundamentally different answer, rather a cosmetic reformatting of what's already been said. Looks the shortest, cleanest and YAML-ishest to me:
- name: My play
hosts: myhosts
roles:
- role: role1
become: yes
- role: role2
Role1 will be run as root while role2 won't.

In Ansible documentation for 2.4, you can find a way to define connection variables, such as ansible_become and ansible_user. They are defined as usual variables. Below is a snippet.
The first role prepare_user connects to hosts using user root without elevation rights. The second role register connects to hosts using the remote_user set via ansible.cfg (looked for in a defined order; search for "in the following order").
---
- hosts: all
name: Prepare VMs for cluster
roles:
- role: prepare_user
vars:
- ansible_become: false
- ansible_user: root
- role: register
...

There is a way to do what you are asking, but you need to be careful with how you use it, because Ansible evaluates most vars before running any tasks. If you use this trick, you must be sure to use it consistently or you could unintentionally use become where you don't want to.
Under the hood, Ansible uses the variable ansible_become to determine whether to use become for that task. Within your role, you can create a defaults/main.yml and set ansible_become: [true/false] This will cause that entire role to accept that value, unless overwritten by a higher-precedence definition (important to understand variable precedence)
The critical "gotcha" here is that if you use a role where this is defined, it will affect all other roles called below it in the play, unless they also have it defined.
Examples:
role_default_become_true has ansible_become: true defined as true in defaults
role_default_become_false has ansible_become: false defined as true in defaults
role_no_default has no default ansible_become value
---
- name: test1
hosts: localhost
connection: local
roles:
- role_default_become_true
- role_default_become_false
- role_no_default
- name: test2
hosts: localhost
connection: local
roles:
- role_default_become_false
- role_default_become_true
- role_no_default
- name: test3
hosts: localhost
connection: local
roles:
- role_default_become_false
- role_default_become_true
- { role: role_no_default, become: false }
In test1, role_no_default will run without become, because the previous role defined it as false, and it does not have its own definition.
In test2, role_no_default will run with become, because the previous role defined it as true, and it does not have its own definition.
In test3, role_no_default will run without become, because it has its own definition.

This is also possible using the include_task module
Create a main.yaml which includes the yaml with the tasks
---
- name: main
include_tasks:
file: "my_tasks_that_needs_become_true.yaml"
apply:
become: true

Related

Ansible. Reconnecting the playbook connection

The server is being created. Initially there is user root, his password and ssh on 22 port (default).
There is a written playbook, for example, for a react application.
When you start playbook'a, everything is deployed for it, but before deploying, you need to configure the server to a minimum. Those. create a new sudo user, change the ssh port and copy the ssh key to the server. I think this is probably needed for any server.
After this setting, yaml appears in the host_vars directory with the variables for this server (ansible_user, ansible_sudo_pass, etc.)
For example, there are 2 roles: initial-server, deploy-react-app.
And the playbook itself (main.yml) for a specific application:
- name: Deploy
hosts: prod
roles:
- role: initial-server
- role: deploy-react-app
How to make it so that when you run ansible-playbook main.yml, the initial-server role is executed from the root user with his password, and the deploy-react-app role from the newly created one user and connection was by ssh key and not by password (root)? Or is it, in principle, not the correct approach?
Note: using dashes (-) in role names is deprecated. I fixed that in my below example
Basically:
- name: initialize server
hosts: prod
remote_user: root
roles:
- role: initial_server
- name: deploy application
hosts: prod
# That one will prevent to gather facts twice but is not mandatory
gather_facts: false
remote_user: reactappuser
roles:
- role: deploy_react_app
You could also set the ansible_user for each role vars in a single play:
- name: init and deploy
hosts: prod
roles:
- role: initial_server
vars:
ansible_user: root
- role: deploy_react_app
vars:
ansible_user: reactappuser
There are other possibilities (using an include_role task). This really depends on your precise requirement.

Can I use custom variables when conditionally calling an Ansible role?

As per ansible documentation here I am using the below syntax to trigger a role when the variable "mdb_user" starts with prod.
- hosts: category_workstation
gather_facts: False
name: common workstation applications
roles:
- apps_workstation
- { role: apps_workstation_production, when: mdb_user.startswith('prod') }
This works nicely but what i want to know is if i can do something similar to adjust the variables fed to the role in different conditions. For instance the below:
- hosts : category_workstation
name: common workstation applications
roles:
- apps_workstation
- { role: apps_workstation_production, vars={'user':'prod'}, when: mdb_user.startswith('prod')}
Currently I am having to use when and set_fact to get the right variables setup before caling a roles and this approach above (if possible) seems more concise.
You can try something like:
- hosts : category_workstation
name: common workstation applications
roles:
- apps_workstation
- { role: apps_workstation_production, user: prod, when: mdb_user.startswith('prod')}

Parameter remote_user in role include is deprecated, what's the workaround?

I'm using Ansible to do the automation of my systems.
I have an Ansible playbook that depends on two roles.
The first role creates a user ("specific_user") on a remote server.
The second role uses this user to do a bunch of stuff.
My first solution was the following (my playbook) :
---
- hosts: all
roles:
- { role: ansible-role1, remote_user: root }
- { role: ansible-role2, remote_user: specific_user }
...
However, I'm getting the following warning from Ansible when running it:
Using 'remote_user' as a role param has been deprecated.
In the future, these values should be entered in the `vars:` section for
roles, but for now we'll store it as both a param and an attribute..
What is the alternative ?
Currently this is only a warning message (until version 2.7 of Ansible).
As the message suggests, you need to change syntax to (using YAML in the example below, because it's more readable):
roles:
- role: ansible-role1
vars:
remote_user: root
- role: ansible-role2
vars:
remote_user: specific_user
...

Can I load a role without executing its tasks (to get the modules only)?

I want to use the module mongodb_replication defined in greendayonfire.mongodb role.
I know I can use the module in my tasks after applying the role in the same play. But I don't want to apply the role (and execute all it's tasks). Is there any way to get to "include" the role without executing the tasks?
I want to have it like this
---
- hosts: mongodb-nodes
become: true
roles:
- base
- greendayonfire.mongodb
vars:
mongodb_package: mongodb-org
mongodb_version: "3.2"
mongodb_force_wait_for_port: true
mongodb_net_bindip: 0.0.0.0
mongodb_net_http_enabled: true
mongodb_replication_replset: "rs1"
mongodb_storage_prealloc: false
- hosts: mongodb-0
tasks:
- mongodb_replication: replica_set=rs1 host_name=item state=present
with_items:
- mongodb-0
- mongodb-1
- mongodb-2
where the second play is the one that runs the mongodb_replication module (only in the node mongodb-0). Right now it can't find the module.
I guess can I copy the module out of the role into my playbook but I will be cleaner if I could just import the module from the role (which I don't want to edit)
I found that it's possible to load the role without executing the task by using the when: false clause when referring to the role. This loads the vars, defaults, modules, etc.
- hosts: mongodb-0
roles:
- role: greendayonfire.mongodb
when: false
tasks:
- mongodb_replication: replica_set=rs1 host_name=item state=present
with_items:
- mongodb-0
- mongodb-1
- mongodb-2

Ansible Roles with tags not respecting the tag, Instead playing all the tast defined

Here is my play book:
- name: Install MySQL with replication
hosts: mysql-master:mysql-slave
user: root
sudo: false
roles:
- common
- admin-users
- generic-directories
- { role: iptables, tags: [ 'mysql-iptables'] }
- mysql
I have ip tables tasks for different ports, I want to run the task depending on the group of servers.
I have tagged the iptables task based on the group.
When i ran the play book instead of playing the tagged task, its run through all the tasks defined in iptables role.
Please let me know if am doing anything wrong here.
In practices roles should not contains code/configuration used only by you. Try to develop roles like if you're going to publish them, doing this you will create more generic/usefull roles
With an iptable role, what you want in the end is to open port/change firewall configuration.
The role should contains tasks that allow configuration in the playbook:
---
- name: iptables | Open ports
command: 'open port {{item.protocol}} {{item.port}}
with_items: 'iptable_conf'
tags:
- iptables
then you playbook
- name: Install MySQL with replication
hosts: mysql-master:mysql-slave
user: root
sudo: false
vars:
- iptables_conf:
- {protocol: tcp, port: 3307}
- {protocol: tcp, port: 3306}
roles:
- common
- admin-users
- generic-directories
- iptables
- mysql
Hope you like opinionated software all the way: https://github.com/ansible/ansible/issues/3283 .
If I'm reading that correctly, you're experiencing a feature, with everything in that role being tagged for you and your CLI tag specification subsequently matching all those tasks. I hate this feature. It's stupid. "Application" vs. "selection" w/r/t to tags is an obvious first question when you're initially exposed to Ansible tags. It should have a more flexible answer, or at least a nod in the docs.
I would suggest a different way of organizing the versatile iptables role. First, consider not having the role if it's wrapping a very sparse amount of tasks. I would recommend for roles to have meaning to you, and not be module-adapters. So maybe the sql role can handle sql-specific rules in a separate tasks file.
Otherwise, a role parameter which can then be used to load variables dynamically (e.g. the list of firewall rules). Here's what that would look like, stubbed out:
Playbook:
---
- hosts: loc
roles:
- { role: does-too-much, focus_on: 'specific-kind' }
Role tasks/main.yml:
---
- include_vars: "{{ focus_on }}.yml"
- debug:
msg: "ok - {{ item }}"
with_items: stuff
Variables vars/specific-kind.yml:
---
stuff:
- b
- c

Resources