Include yaml object as child object with Ansible - ansible

Let's say I have a playbook that uses two roles like this.
---
- hosts: database
become: yes
roles:
- role: foo_role_one
- role: foo_role_two
And I have a file in /group_vars/database/vars.yml like this.
---
username: bar
The username variable is used by both foo_role_one and foo_role_two, but the values are not identical. I know I could include the variable directly as a child of - role: foo_role_one, but I am attempting to keep my playbooks clean with the variables for groups and and hosts in their own files. To accomplish this I'd like to do some like this.
---
- hosts: database
become: yes
roles:
- role: foo_role_one
'{{ foo_one }}'
- role: foo_role_two
'{{ foo_two }}'
And then in /group_vars/database/vars.yml have the following.
---
foo_one:
username: bar
foo_two:
username: baz
I cannot find a syntax with the yaml spec that will allow me to do this. I thought anchors and references would do it, but it appears that they do not span yaml files.

I think you'll find what you're looking for right in the roles documentation, which shows, for example in which two roles both want variables named dir and app_port, but the values for each role need to be different:
- hosts: webservers
roles:
- common
- role: foo_app_instance
vars:
dir: '/opt/a'
app_port: 5000
tags: typeA
- role: foo_app_instance
vars:
dir: '/opt/b'
app_port: 5001
tags: typeB
I believe that's the same situation you're asking about.

Related

How to reuse Ansible role with different set of variables in the same playbook

I want to invoke the same role twice with different set of variables. However, when I run the playbook, the first instance of role is executed, the second call is ignored.
The details are as follows.
---
- hosts: server
gather_facts: true
roles:
- role: upgrade_opatch_utility
vars:
source: /u1/software/OPatch
opatch_file_name: p6880880_200000_Linux-x86-64.zip
oracle_home: /u1/app/oracle/product/19.3.0/dbhome_1
- role: upgrade_opatch_utility
vars:
source: /u1/software/OPatch
opatch_file_name: p6880880_200000_Linux-x86-64.zip
oracle_home: /u1/app/oracle/product/12.2.0/dbhome_1
How can I re arrange the roles invocation multiple times with different set of variables?
Thanks in advance.
FR
Set a different name for each role invocation. That way Ansible won't consider them duplicates.
roles:
- name: upgrade_opatch_19
role: upgrade_opatch_utility
vars:
[...]
- name: upgrade_opatch_12
role: upgrade_opatch_utility
vars:
[...]

How to Execute Role Inside the Ansible Blocks

I have written a ansible playbook which do a deployment on the remote machines
- name: Deployment part of the script
vars:
hostName:
build_num:
hosts: "{{hostName}}"
become: true
serial: 1
tasks: this does deployment
after this I want to execute the util which is on localhost from where this playbook will be executed.
Now I have written a roles which does this for me if I execute them separately as a playbook
- name: Roles Demo
hosts: 127.0.0.1
connection: local
vars:
var1: "sometextvalue"
var2: "sometextvalue"
var3: "someurl"
roles:
- demorole #role which I created
Now I want to integrate the role in my main playbook mentioned at the top but I am getting
ERROR! no action detected in task. This often indicates a misspelled module name, or incorrect module path.
although its the same snippet which is working fine when run individually
Also I wanted to execute this using "Ansible blocks" like when a certain condition is matched execute a certain role but for that also I am getting the same above error just to summarize what I want to achieve using the blocks is as below
- name: Deployment part of the script
vars:
hostName:
build_num:
hosts: "{{hostName}}"
become: true
serial: 1
tasks: this does deployment complete
- name: Task for Doing some check
hosts: 127.0.0.1
connection: local
vars:
var1: "dakdkadadakdhkahdkahkdh
var2: "jdjaldjlaj"
var3: "djasjdlajdlajdljadljaldjlaj"
block:
- name: Doing Check for some1
roles:
- role1
when: x == "somevalue1"
- block:
- name: Doing check for some2
roles:
- role2
when: x == "somevalue2"
.
.
.
assuming the vars value are same
so I am not sure if this could be achieved
Using a block outside of the tasks section is not valid.
You can however execute roles from within the tasks section, which will allow you to use blocks and when conditionals however you choose.
Example:
- name: Task for Doing some check
hosts: 127.0.0.1
connection: local
vars:
var1: "dakdkadadakdhkahdkahkdh
var2: "jdjaldjlaj"
var3: "djasjdlajdlajdljadljaldjlaj"
tasks:
- name: Doing Check for some1
import_role:
name: role1
when: x == "somevalue1"
You will need to decide whether to use import_role or include_role. Take a look at https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse.html#dynamic-vs-static for an explanation of the differences.

How to modularize Ansible playbook with multiple virtual hosts?

(EDIT: the question was thoroughly rewritten based on feedback in comments, aiming to follow a suggestion to use "more code, less talk")
I've accumulated some ansible playbooks for setting up our host(s). I want to start modularizing them. The current situation is roughly like below:
docker-registry-playbook.yml:
- hosts: foo
tasks:
- name: install docker
# ...
- name: copy config files
# ...
- name: start docker registry
# ...etc...
issue-tracker-playbook.yml:
- hosts: foo
tasks:
- name: install issue tracker
# ...etc...
tools-playbook.yml:
- hosts: foo
tasks:
- name: configure nginx virtual hosts
copy:
src: "{{ item }}"
dest: /etc/nginx/sites-available/
# ...
with_items:
- docker.example.com
- issues.example.com
- name: start nginx
# ...
- name: configure letsencrypt
# ...
My first idea was to split it into roles like below:
- hosts: foo
roles: webserver docker_registry issue_tracker
However, in such case, I don't know how to make the webserver role "auto-detect" the host names (virtual hosts) it should expose? I would like to be easily able to move e.g. docker_registry role to a different host (bar), and have the webserver auto-detect the change and update virtual hosts accordingly when I change the new playbook to:
- hosts: foo
roles: webserver docker_registry
- hosts: bar
roles: webserver issue_tracker
I would strongly prefer not having to explicitly list virtual hosts as parameters to webserver both on foo and bar; this would be too much duplication for me. Is it possible to have webserver autodetect names of "virtual hosts" from the other roles configured on the same server?
Or am I approaching the modularization in a wrong way, and the idiomatic approach in Ansible is to split it along some other axis when modularizing?
If not possible to do fully automatically, I'd like it to be done with minimal wiring; I could maybe grudgingly accept something like below, though it already looks much too long and ugly to me (but I still don't know how to do this in Ansible):
- hosts: foo
roles:
- docker_registry
- role: webserver
vars:
vhosts: [ "docker_registry" ]
I'm not sure if I understand your problem correctly. Why is it necessary to have a common variable virtual_hosts in all roles?
... Initially, I thought I could set a variable in each of service1 and service2, named e.g. virtual_hosts: and Ansible would merge the duplicated variables into one...
For example:
- hosts: foo
vars:
virtual_host_service1: docker.example.com
virtual_host_service2: issues.example.com
webserver_vm:
- "{{ virtual_host_service1 }}"
- "{{ virtual_host_service2 }}"
roles:
- webserver
- service1
- service2
You might want to set the variables in the playbook, or in the host_vars, or in many other places according to the precedens

Ansible - multiple roles

I am trying to run multiple roles using with_items command, however I am getting error:
"ERROR! 'item' is undefined"
role.yml:
---
- hosts: '{{ host }}'
become: yes
roles:
- role: "{{item}}"
with_items: "{{ roles }}"
Here is my command:
ansible-playbook -i ./inventory/Dev ./playbooks/role.yml --extra-vars='{"host": "db", "roles": ["mysql", "apache"]}'
You cannot do it this way. with_ loops are not valid for roles.
If anything, you need to provide a list of roles to the roles: directive, so the syntax would be just like for the list of host groups hosts: '{{ host }}'. The problem is: Ansible does not resolve the variable for roles, so roles: '{{ roles }}' does not work.
What you can do, however, is to use include_role module in which you can access the variables.
No, include_role module doesn't take {{ item }} from the with_items as a value for name either.
So the only workaround I can think of (assuming you don't want to process the JSON beforehand) is to the include the roles statically:
tasks:
- include_role:
name: "mysql"
when: "'mysql' in roles"
- include_role:
name: "apache"
when: "'apache' in roles"
The roles need to exist on the control machine anyway, so all their names are predefined.
You cannot use with_ loop with roles directive.
A simple solution would be to use normal yaml list like below:
---
- hosts: '{{ host }}'
become: yes
roles:
- myrole1
- myrole2
Another example where you could declare the roles as well as pass corresponding variables to the role would be like below:
---
- hosts: '{{ host }}'
become: yes
roles:
- role:
name: myrole1
vars:
host: "db1"
myroles:
- mysql1
- apache1
- role:
name: myrole2
vars:
host: "db2"
myroles:
- mysql2
- apache2
Also avoid using ansible keywords as your variable names
Finally your command to run your playbook will be as below:
ansible-playbook -i ./inventory/Dev ./playbooks/role.yml

Set remote_user for set of tasks in Ansible playbook without repeating it per task

I am creating a playbook which first creates a new username. I then want to run "moretasks.yml" as that new user that I just created. Currently, I'm setting remote_user for every task. Is there a way I can set it for the entire set of tasks once? I couldn't seem to find examples of this, nor did any of my attempts to move remote_user around help.
Below is main.yml:
---
- name: Configure Instance(s)
hosts: all
remote_user: root
gather_facts: true
tags:
- config
- configure
tasks:
- include: createuser.yml new_user=username
- include: moretasks.yml new_user=username
- include: roottasks.yml #some tasks unrelated to username.
moretasks.yml:
---
- name: Task1
copy:
src: /vagrant/FILE
dest: ~/FILE
remote_user: "{{newuser}}"
- name: Task2
copy:
src: /vagrant/FILE
dest: ~/FILE
remote_user: "{{newuser}}"
First of all you surely want to use sudo_user (remote user is the one that logs in, sudo_user is the one who executes the task).
In your case you want to execute the task as another user (the one previously created) just set:
- include: moretasks.yml
sudo: yes
sudo_user: "{{ newuser }}"
and those tasks will be executed as {{ newuser }} (Don't forget the quotes)
Remark: In most cases you should consider remote_user as a host parameter. It is the user that is allowed to login on the machine and that has sufficient rights to do things. For operational stuff you should use sudo / sudo_user
You could split this up into to separate plays? (playbooks can contain multiple plays)
---
- name: PLAY 1
hosts: all
remote_user: root
gather_facts: true
tasks:
- include: createuser.yml new_user=username
- include: roottasks.yml #some tasks unrelated to username.
- name: PLAY 2
hosts: all
remote_user: username
gather_facts: false
tasks:
- include: moretasks.yml new_user=username
There is a gotcha using separate plays: you can't use variables set with register: or set_fact: in the first play to do things in the second play (this statement is not entirely true, the variables are available in hostvars, but I recommend not using variables between roles). Defined variables like in group_vars and host_vars work just fine.
Another tip I'd like to give is to look into using roles http://docs.ansible.com/playbooks_roles.html. While it might seem more complicated at first, it's much easier to re-use them (as you seem to be doing with the "createuser.yml"). Looking at the type of things you are trying to achieve, the 'include all the things' path won't last much longer.
Kind of inline with your issue. Hope it helps. While updating my playbooks for Ansible 2.5 support for Cisco IOS network_cli connection
Credential file created with ansible-vault: auth/secrets.yml
---
creds:
username: 'ansible'
password: 'user_password'
Playbook:
---
- hosts: ios
gather_facts: yes
connection: network_cli
become: yes
become_method: enable
ignore_errors: yes
tasks:
- name: obtain login credentials
include_vars: auth/secrets.yml
- name: Set Username/ Password
set_fact:
remote_user: "{{ creds['username'] }}"
ansible_ssh_pass: "{{ creds['password'] }}"
- name: Find info for "{{ inventory_hostname }}" via ios_facts
ios_facts:
gather_subset: all
register: hardware_fact
Running playbook without auth/secrets.yml creds:
ansible-playbook -u ansible -k playbook.yml -l inventory_hostname

Resources