Launch a play in playbook based on gather facts - ansible

I have this Ansible Playbook with three different plays. What I want to do is to launch the two lasts plays based on a condition. How can I do this directly at playbook level (not using when clause in each role)?
- name: Base setup
hosts: all
roles :
- apt
- pip
# !!!!! SHUT DOWN IF NOT DEBIAN DIST
- name: dbserver setup
hosts: dbserver
remote_user: "{{ user }}"
become: true
roles:
- mariadb
- name: webserver and application setup
hosts: webserver
remote_user: "{{ user }}"
become: true
roles:
- php
- users
- openssh
- sshkey
- gitclone
- symfony

You could just end the play for the hosts you do not wish to continue with, with the help of the meta task in a pre_tasks:
- name: dbserver setup
hosts: dbserver
remote_user: "{{ user }}"
become: true
pre_tasks:
- meta: end_host
when: ansible_facts.os_family != 'Debian'
roles:
- mariadb
And do the same for the web servers.

Related

How to run some tasks locally in Ansible

I have a playbook with some roles/tasks to be executed on the remote host. There is a scenario where I want some tasks to execute locally like downloading artifacts from svn/nexus to local server.
Here is my main playbook where I am passing the target_env from the command line and dynamically loading the variables using group_vars directory
---
- name: Starting Deployment of Application to tomcat nodes
hosts: '{{ target_env }}'
become: yes
become_user: tomcat
become_method: sudo
gather_facts: yes
roles:
- role: repodownload
tags:
- repodownload
- role: stoptomcat
tags:
- stoptomcat
The first role repodownload actually download the artifacts from svn/nexus to the local server/controller. Here is the main.yml of this role -
- name: Downloading MyVM Artifacts on the local server
delegate_to: localhost
get_url: url="http://nexus.com/myrelease.war" dest=/tmp/releasename/
- name: Checkout latest application configuration templates from SVN repo to local server
delegate_to: localhost
subversion:
repo: svn://12.57.98.90/release-management/config
dest: ../templates
in_place: yes
But it's not working. Could it be because in my main yml file I am becoming the user using which I want to execute the commands on remote host.
Let me know if someone can help. It will be appreciated.
ERROR -
"changed": false,
"module_stderr": "sudo: a password is required\n",
"module_stdout": "",
"msg": "MODULE FAILURE\nSee stdout/stderr for the exact error",
"rc": 1
}
Given the scenario, you can do it multiple ways. One could be adding another play for repodownload role to your main playbook that runs only on localhost. Then remove delegate_to: localhost from the role tasks and move the variables accordingly.
---
- name: Download repo
hosts: localhost
gather_facts: yes
roles:
- role: repodownload
tags:
- repodownload
- name: Starting Deployment of Application to tomcat nodes
hosts: '{{ target_env }}'
become: yes
become_user: tomcat
become_method: sudo
gather_facts: yes
roles:
- role: stoptomcat
tags:
- stoptomcat
Another way could be removing become from play level and add to role stoptomcat. Something like below should work.
---
- name: Starting Deployment of Application to tomcat nodes
hosts: '{{ target_env }}'
gather_facts: yes
roles:
- role: repodownload
tags:
- repodownload
- role: stoptomcat
become: yes
become_user: tomcat
become_method: sudo
tags:
- stoptomcat
Haven't tested the code so apologies if any formatting issues.

Pass host as a variable from role to next role

I have a long playbook with a number of roles defined. Now i have a requirement for one role i need to pass the host as a variable which will be defined on an earlier role.
eg playbook
---
- name: task1
hosts: app1
gather_facts: no
any_errors_fatal: true
roles:
- role-1
- name: task2
hosts: "{{ host }}"
any_errors_fatal: true
gather_facts: no
roles:
- role-2
My role-1
---
- name: setting the var
set_fact:
host: "app2"
- debug:
var: host
My role-2
---
- debug:
var: host
- name: do something
file:
path: /home/ec2-user/dir1
state: directory
mode: '0755'
however, when I try to run my playbook my role-2 gets skipped because no hosts matched. can someone point me on how to get this setup working.
The thing you want is add_host: and then set the newly created or assigned group as the hosts: the 2nd play:
- hosts: app1
tasks:
- add_host:
name: app2
groups:
- my-group
- hosts: my-group
tasks:
- debug: var=ansible_host

How to run a task only once during entire Ansible Playbook?

I have a Ansible playbook which does multiple things as below -
Download artifacts fron nexus into local server (Ansible Master).
Copy those artifacts onto multiple remote machines let's say server1/2/3 etc..
And I have used roles in my playbook and the role (repodownload) which downloads the artifacts I want to run it only once because why would i want to download the same thing again. I have tried to use run_once: true but i guess that won't work because that only works for one playbook run but my playbook is running multiple times for multiple hosts.
---
- name: Deploy my Application to tomcat nodes
hosts: '{{ target_env }}'
serial: 1
roles:
- role: repodownload
tags:
- repodownload
- role: copyrepo
tags:
- copyrepo
- role: stoptomcat
tags:
- stoptomcat
- role: deploy
tags:
- deploy
Here target_env is being passed from the command line and it's the remote host group.
Any help is appreciated.
Below is the code from main.yml from repodownload role -
- connection: local
name: Downloading files from Nexus to local server
get_url: url="{{ nexus_url }}/{{item}}/{{ myvm_release_version }}/{{item}}-{{ release_ver }}.war" dest={{ local_server_location }}
with_items:
- "{{ temps }}"
This is a really simple one that I battled with too.
Try this:
- connection: local
name: Downloading files from Nexus to local server
get_url:
url: "{{ nexus_url }}/{{item}}/{{ myvm_release_version }}/{{item}}-{{ release_ver }}.war"
dest: "{{ local_server_location }}"
with_items:
- "{{ temps }}"
run_once: true
Just something else, unrelated to your main question;
When you run a module that has really long args, like in your example above, rather break the params into their own lines nested under the module. It makes for easier reading, and it makes it easier to spot any potential typos or syntax errors early.
Okay extending from your converstation with Zeitounator. The following workaround will work without changing your vars files. Just remember that this is a workaround, might not be the most efficient way to do the job.
---
- name: Download my repo to localhost
# Executes only for first host in target_env and has visibility to group vars of target_env
hosts: '{{ target_env }}[0]'
serial: 1
roles:
- role: repodownload
tags:
- repodownload
- name: Deploy my Application to tomcat nodes
# Executes for all hosts in target_env
hosts: '{{ target_env }}'
serial: 1
roles:
- role: copyrepo
tags:
- copyrepo
- role: stoptomcat
tags:
- stoptomcat
- role: deploy
tags:
- deploy

How to skip role when host is unreachable?

I have disable_root role, which creates admin user and disables root user from connecting to server via ssh.
When I rerun playbook with this role for second time I get unreachable error (which is ok, as I just disabled it).
I'd like to skip this role in such case and continue with other roles (which would run as a admin user). How can I do this?
This is my playbook (disbale_root uses ansible_user: root var)
- hosts: webservers
roles:
- disable_root
- common
- nginx
One playbook shall connect to the remote host as root, if necessary, or as admin if this user has already been created. This can be done with remote_user (see Ansible remote_user vs ansible_user
).
Let's create the playbook to disable root and enable admin
> cat play-disable-root.yml
- hosts: webservers
remote_user: 'root'
tasks:
- include_role:
name: disable_root
when: play_disable_root|default(false)
In the first play import this playbook if admin can't connect to the remote host
- hosts: webservers
remote_user: 'admin'
tasks:
- delegate_to: localhost
command: ping -c1 "{{ inventory_hostname }}"
register: result
ignore_errors: true
- set_fact:
play_disable_root: true
when: result.rc != 0
- import_playbook: play-disable-root.yml
in the second play proceed with remaining roles
- hosts: webservers
remote_user: 'admin'
roles:
- common
- nginx
Both first and second play may be put into one playbook.
(code not tested)
Updates:
it's not possible to conditionally import playbooks

Ansible Playbook skip some play on multiple play playbook file

I have an ansible playbook YAML file which contains 3 plays.
The first play and the third play run on localhost but the second play runs on remote machine as you can see an example below:
- name: Play1
hosts: localhost
connection: local
gather_facts: false
tasks:
- ... task here
- name: Play2
hosts: remote_host
tasks:
- ... task here
- name: Play3
hosts: localhost
connection: local
gather_facts: false
tasks:
- ... task here
I found that, on the first run, Ansible Playbook executes Play1 and Play3 and skips Play2. Then, I try to run again, it executes all of them correctly.
What is wrong here?
The problem is that, at Play2, I use ec2 inventor like tag_Name_my_machine but this instance was not created yet, because it would be created at Play1's task.
Once Play1 finished, it will run Play2 but no host found so it silently skip this play.
The solution is to create dynamic inventor and manually register at Play1's tasks:
Playbook may look like this:
- name: Play1
hosts: localhost
connection: local
gather_facts: false
tasks:
- name: Launch new ec2 instance
register: ec2
ec2: ...
- name: create dynamic group
add_host:
name: "{{ ec2.instances[0].private_ip }}"
group: host_dynamic_lastec2_created
- name: Play2
user: ...
hosts: host_dynamic_lastec2_created
become: yes
become_method: sudo
become_user: root
tasks:
- name: do something
shell: ...
- name: Play3
hosts: localhost
connection: local
gather_facts: false
tasks:
- ... task here

Resources