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
Related
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.
I've an inventory as follow:
[central]
central_1
central_2
[peripheral]
peripheral_1
peripheral_2
...
central_1 and central_2 share the same database and each peripheral_* has its own local database.
Connection parameters are all stored in group_vars/central.yml and multiple host_vars/peripheral_* files.
Then I have a bunch of roles that access the database and perform specific operation based on values in peripheral_conf hostvars.
My goal here is to run a subset of the roles on one central host using the right DB access parameter.
First attempt
First attempt was to have two different play in the playbook targeting different hosts
- hosts: peripheral
pre_tasks:
- name: Load Configuration
set_fact:
peripheral_conf: "{{ hostvars[inventory_hostname] }}"
roles:
- role1
- role2
- role3
- hosts: central
roles:
- role1
- role2
but, since I normally run the playbook limiting the inventory to a specific peripheral host, I'm not able to run it against the central hosts.
Second attempt
I then tried to include role using delegate_to and delegate_fact:
- hosts: peripheral
gather_facts: False
pre_tasks:
- name: Load Configuration
set_fact:
peripheral_conf: "{{ hostvars[inventory_hostname] }}"
tasks:
- name: Insert values into central DB based on peripheral configuration
include_role:
name: '{{ item }}'
apply:
delegate_to: central
delegate_facts: yes
run_once: yes
loop:
- role1
- role2
roles:
# insert values into peripheral DB
- role1
- role2
- role3
but it keeps using peripheral's DB connection parameters.
Working attempt
I finally was able to make it works adding an explicit overriding of the connection parameters:
- hosts: peripheral
gather_facts: False
pre_tasks:
- name: Load Configuration
set_fact:
peripheral_conf: "{{ hostvars[inventory_hostname] }}"
tasks:
- name: Insert values into central DB based on peripheral configuration
include_role:
name: '{{ item }}'
apply:
delegate_to: central
run_once: yes
vars:
db_host : "{{ hostvars['central_app1']['db_host'] }}"
db_database: "{{ hostvars['central_app1']['db_database'] }}"
db_username: "{{ hostvars['central_app1']['db_username'] }}"
db_password: "{{ hostvars['central_app1']['db_password'] }}"
loop:
- role1
- role2
roles:
# insert values into peripheral DB
- role1
- role2
- role3
And finally, the question
The playbook does the job but I'd like to know if this is the only way of do this or there is any better and/or more efficient way to do this.
I created 2 playbooks:
deploy VM(centos8) into ESX
join VM to AD
Inside of them are plenty variables which are useful only for one specific VM (beside info about ESX)
---
- name: create vm from template on ESX
hosts: localhost
gather_facts: no
become: yes
tasks:
- name: clone the template
vmware_guest:
hostname: "IP.."
username: "user"
password: "password"
validate_certs: false
name: ll-ansible
template: ll
datacenter: "LAB KE"
folder: "OIR"
resource_pool: "pool"
cluster: "PROD cluster"
networks:
- name: IS-VLAN1102
device_type: vmxnet3
vlan: IS-VLAN1102
ip: ip.ip.ip.ip
netmask: 255.255.255.0
gateway: gw.gw.gw.gw
customization:
hostname: ll-ansible
timezone: timezone
domain: domain
dns_servers:
- ip.ip.ip.ip
- ip.ip.ip.ip
disk:
- size: 60gb
type: default
datastore: Dell-OIR
- size: 10gb
type: default
datastore: Dell-OIR
hardware:
memory_mb: 4096
num_cpus: 4
num_cpu_cores_per_socket: 2
boot-firmware: efi
state: poweredon
wait_for_ip_address: yes
register: vm
- debug: msg "{{ vm }}"
My question is:
Is there a way to make a script which will read all necessary variables for deploying VM from command line and fill fields in playbook and then run playbook.
Or if is it possible make it only within ansible possibilities.
There are many ways to send variables to playbook.
Let's start from these 3 most popular options:
env vars
ansible-playbook CLI arguments
var files
See also all 22 options: Understanding variable precedence
Option 1: use lookup('env') in the playbook
Just use "{{ lookup('env', 'ENV_VAR_NAME')}}"
E.g., for your case:
---
- name: create vm from template on ESX
hosts: localhost
gather_facts: no
become: yes
tasks:
- name: clone the template
vmware_guest:
hostname: "{{ lookup('env', 'IP_ADDRESS')}}"
Option 2: pass var through CLI args
You may pass Env var value using --extra-vars CLI argument
For your case:
playbook.yml
...
- name: clone the template
vmware_guest:
hostname: "{{ ip_address }}"
user: "{{ user }}"
usage:
ansible-playbook playbook.yml --extra-vars="user={{ lookup('env', 'USER') }}, ip_address='10.10.10.10'"
Option 3: use vars_files in the playbook
For your case:
playbook.yml:
---
- name: create vm from template on ESX
hosts: localhost
gather_facts: no
become: yes
vars_files:
- "vars/vars.yml"
tasks:
- name: clone the template
vmware_guest:
hostname: "{{ hostname }}"
vars/vars.yml:
---
hostname: "host.example.com"
Let's combine them all:
Suppose you have two different environments:
staging for staging environment
prod for productive environment
Then we create two different vars files:
vars/staging.yml
---
hostname: "staging.domain.com"
vars/prod.yml
---
hostname: "prod.domain.com"
playbook.yml:
---
- name: create vm from template on ESX
y6uu hosts: localhost
gather_facts: no
become: yes
vars_files:
- "vars/{{ env }}.yml"
tasks:
- name: clone the template
vmware_guest:
hostname: "{{ hostname }}"
Usage
run playbook with staging vars:
ansible-playbook playbook.yml --extra-vars=staging
run playbook with prod vars:
ansible-playbook playbook.yml --extra-vars=prod
I have a silly ask. I am using roles and variables in my playbook but getting an error while executing. Here is the heriarchy of my plays
my-main.yml
---
- include: validation-and-download-tasks.yml
validation-and-download-tasks.yml
---
- hosts: localhost
connection: local
gather_facts: no
roles:
- role: valanddownload
tags:
- valanddownload
valanddownload main.yml
---
- name: Validating the input variables
#hosts: localhost
#gather_facts: false
vars:
_allowed_envs:
- dev
- preprod
- prod
- name: Ensuring that directory exists to download the artifacts
file:
path: "{{ release_location }}/{{my_release_version}}"
state: directory
Something wrong with my indentation? Error below
ERROR: vars is not a legal parameter in an Ansible task or handler
as written, you should define the vars on the play level, here's an example as you asked:
---
- hosts: localhost
connection: local
gather_facts: no
roles:
- role: valanddownload
tags:
- valanddownload
vars:
_allowed_envs:
- dev
- preprod
- prod
You can't attach the vars part to the task level. This should work for you.
Tip: I would use a vars file. Define all your variables there. It is much more comfortable and editable to control all your vars in one 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