ansible playbook: print custom message if hosts variable is not defined - ansible

The simple playbook
---
- name: NTP client
# hosts: all:!ntpserver
hosts: "{{ target_hosts }}"
roles:
- ntp_client
can be executed using
ansible-playbook -i hosts.yml -e target_hosts=<target host> <path to playbook> --ask-become-pass
This works just fine when target_hosts is set. In case target_hosts isn't set it obviously fails with
ERROR! The field 'hosts' has an invalid value, which includes an undefined variable. The error was: 'target_hosts' is undefined
How do I print a custom message such as
The variable 'target_hosts' is not set, use e.g. '-e target_hosts=all:!ntpserver'.
Note, I am aware that a default can be set via a hosts line like
hosts: "{{ target_hosts | default('all:!ntpserver') }}"

Add a localhost play for validating input arguments as given below. This will fail with a custom message if the 'target_hosts' var is not defined.
---
- name: Validate target_host variable
hosts: localhost
tasks:
- fail:
msg: The variable 'target_hosts' is not set, use e.g. '-e target_hosts=all:!ntpserver'.
when: target_hosts is not defined
- name: NTP client
# hosts: all:!ntpserver
hosts: "{{ target_hosts }}"
roles:
- ntp_client

Use the filter mandatory. For example,
- hosts: "{{ target_hosts|mandatory('The variable target_hosts is mandatory') }}"
Example of a complete playbook
shell> cat pb.yml
- hosts: "{{ target_hosts|mandatory(err001) }}"
vars:
err001: >-
The variable 'target_hosts' is not set,
use e.g. '-e target_hosts=all:!ntpserver'
tasks:
- debug:
msg: hello
gives
shell> ansible-playbook pb.yml
ERROR! The variable 'target_hosts' is not set, use e.g. '-e target_hosts=all:!ntpserver'

Related

Ansible - Using a var from an inventory

I am using ansible inventory file my_inv.yml where I have something this:
all:
hosts:
localhost:
ansible_connection: local
children:
my_env1:
hosts:
my_env.app.domain.com
vars:
my_var_1: "True"
my_var_2: '123'
my_env2:
hosts:
my_env2.app.domain.com
vars:
my_var_1: "True"
my_var_2: '123'
...
I am running a script (myscript.sh env) to call a playbook. Inside that script, It's something like this:
ENVIR="${1}"
ansible-playbook -i inventory/my_inv.yml --extra-vars "ENV=$ENVIR MY_VAR={{hostvars[ENV]['my_var_1']}"
So I want to capture my_var_1 (value true) for my_env1, and send it in the extra-vars. This is not working. How can I do this?
you could launch this playbook by specifying the var ENV in the command:
- name: "tips4"
hosts: all
gather_facts: false
tasks:
- debug:
msg: "{{ hostvars[inventory_hostname].group_names.0}}"
- debug:
msg: "{{ hostvars[inventory_hostname]['my_var_1']}}"
when: "hostvars[inventory_hostname].group_names.0 == ENV"
- set_fact:
toto: "{{ hostvars[inventory_hostname]['my_var_1']}}"
when: "hostvars[inventory_hostname].group_names.0 == ENV"
- debug:
msg: "{{ toto}}"
when: toto is defined

Ansible how to set_fact with condition or var base on condition

I have 3 types of servers: dev, qa and prod. I need to send files to server specific home directories, i.e:
Dev Home Directory: /dev/home
QA Home Directory:/qa/home
PROD Home Directory: /prod/home
I have var set as boolean to determine the server type and think of using set_fact with condition to assign home directories for the servers. My playbook looks like this:
---
- hosts: localhost
var:
dev: "{{ True if <hostname matches dev> | else False }}"
qa: "{{ True if <hostname matches qa> | else False }}"
prod: "{{ True if <hostname matches prod> | else False }}"
tasks:
- set_facts:
home_dir: "{{'/dev/home/' if dev | '/qa/home' if qa | default('prod')}}"
However, then I ran the playbook, I was getting error about 'template expected token 'name', got string> Anyone know what I did wrong? Thanks!
Use match test. For example, the playbook
shell> cat pb.yml
- hosts: localhost
vars:
dev_pattern: '^dev_.+$'
qa_pattern: '^qa_.+$'
prod_pattern: '^prod_.+$'
dev: "{{ hostname is match(dev_pattern) }}"
qa: "{{ hostname is match(qa_pattern) }}"
prod: "{{ hostname is match(prod_pattern) }}"
tasks:
- set_fact:
home_dir: /prod/home
- set_fact:
home_dir: /dev/home
when: dev|bool
- set_fact:
home_dir: /qa/home
when: qa|bool
- debug:
var: home_dir
gives (abridged)
shell> ansible-playbook pb.yml -e hostname=dev_007
home_dir: /dev/home
Notes:
The variable prod is not used because /prod/home is default.
prod/home is assigned to home_dir first because it's the default. Next tasks can conditionally overwrite home_dirs.
Without the variable hostname defined the playbook will crash.
A simpler solution that gives the same result is creating a dictionary of 'pattern: home_dir'. For example
- hosts: localhost
vars:
home_dirs:
dev_: /dev/home
qa_: /qa/home
prod_: /prod/home
tasks:
- set_fact:
home_dir: "{{ home_dirs|
dict2items|
selectattr('key', 'in' , hostname)|
map(attribute='value')|
list|
first|default('/prod/home') }}"
- debug:
var: home_dir
Adding an alternative method to achieve this. This kind of extends the group_vars suggestion given by #mdaniel in his comment.
Ansible has a ready-made mechanism to build the variables based on the inventory hosts and groups. If you organize your inventory, you can avoid a lot of complication in trying to match host patterns.
Below is a simplified example, please go through the link above for more options.
Consider an inventory file /home/user/ansible/hosts:
[dev]
srv01.dev.example
srv02.dev.example
[qa]
srv01.qa.example
srv02.qa.example
[prod]
srv01.prod.example
srv02.prod.example
Using group_vars:
Then you can have below group_var files in /home/user/ansible/group_vars/ (matching inventory group names):
dev.yml
qa.yml
prod.yml
In dev.yml:
home_dir: "/dev/home"
In qa.yml:
home_dir: "/qa/home"
In prod.yml:
home_dir: "/prod/home"
Using host_vars:
Or you can have variables specific to hosts in host_vars directory /home/user/ansible/host_vars/:
srv01.dev.example.yml
srv01.prod.example.yml
# and so on
In srv01.dev.example.yml:
home_dir: "/dev/home"
In srv01.prod.example.yml:
home_dir: "/prod/home"
These variables will be picked based on which hosts you run the playbook, for example the below playbook:
---
- hosts: dev
tasks:
- debug:
var: home_dir
# will be "/dev/home"
- hosts: prod
tasks:
- debug:
var: home_dir
# will be "/prod/home"
- hosts: srv01.dev.example
tasks:
- debug:
var: home_dir
# will be "/dev/home"
- hosts: srv01.prod.example
tasks:
- debug:
var: home_dir
# will be "/prod/home"

How to use variable in different playbook?

How to use a variable in a different playbook? (ansible 2.7.10)
username.yml
- hosts: host
vars_prompt:
- name: username
prompt: 'Username...'
private: no
tasks:
- name: Show username
debug:
msg: "{{username}}"
- import_playbook: dns.yml
dns.yml
- hosts: DNS
tasks:
- name: Mesaj
debug:
msg: "{{username}}"
FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'username' is undefined
The scope of a variable declared by vars_prompt is the play. Use set_fact to share such variable in the whole playbook.
The set_fact module takes key=value pairs as variables to set in the playbook scope.
- hosts: host
vars_prompt:
- name: username
prompt: 'Username...'
private: no
tasks:
- name: Show username
debug:
msg: "{{ username }}"
- set_fact:
username: "{{ username }}"
In the second play (dns.yml) use hostvars to reference the variables cached by host in the first play.
- hosts: DNS
tasks:
- name: Mesaj
debug:
msg: "{{ hostvars['host'].username }}"
Without disturbing the dns.yml playbook. So, you can pass -e username=myuser and execute it separately if needed.
username.yml
Adding set fact with different variable name (uname) and passing it to the playbook (dns.yml)
- hosts: localhost
vars_prompt:
- name: username
prompt: 'Username...'
private: no
tasks:
- name: Show username
debug:
msg: "{{username}}"
- name: Set fact
set_fact:
uname: "{{ username }}"
- import_playbook: dns.yml
vars:
username: "{{ uname }}"
dns.yml
No change to this playbook.
- hosts: DNS
tasks:
- name: Mesaj
debug:
msg: "{{username}}"

Syntax to pass dynamic variables to include_tasks along with with_item in Ansible playbook

Executing parent.yml which in turn calls child.yml playbook for execution with dynamic variables.
Variables from parent.yml aren`t interpolated inside child.yml playbook. Correct me if I am using correct syntax?
Parent.yml
- name: Main playbook to call MySQL backup
hosts: localhost
gather_facts: no
tasks:
- include_task: child.yml
vars:
var1: "{{ item.name }}"
var2: "{{ item.db_name }}"
with_items:
- { name: '10.10.10.01', db_name: 'prod1' }
- { name: '10.10.10.02', db_name: 'prod2' }
child.yml (Takes mysqldump from managed DB)
- name: MySQL dump
hosts: localhost
#gather_facts: no
#vars:
# v1: "{{ var1 }}"
# v2: "{{ var2 }}"
tasks:
- name: Executing the shell script
shell: 'mysqldump -h "{{ var1 }}" -u"ansi" -p"*****" "{{ var2 }}"| gzip > /tmp/mysql_dump/"{{ var2 }}"_`date +%Y%m%d-%H%M`.gz'
fatal: [127.0.0.1]: FAILED! => {"reason": "no action detected in task. This often indicates a misspelled module name, or incorrect module path.\n\nThe error appears to be in '/home/ansible/playbooks/DBpatch/Linux/child.yml': line 1, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: MySQL dump\n ^ here\n"}
include_task expects a list of tasks but you give it a complete playbook.
Child.yml should only contain what is currently below the line "tasks:".
See also https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_includes.html

Return Variable from Included Ansible Playbook

I have seen how to register variables within tasks in an ansible playbook and then use those variables elsewhere in the same playbook, but can you register a variable in an included playbook and then access those variables back in the original playbook?
Here is what I am trying to accomplish:
This is my main playbook:
- include: sub-playbook.yml job_url="http://some-jenkins-job"
- hosts: localhost
roles:
- some_role
sub-playbook.yml:
---
- hosts: localhost
tasks:
- name: Collect info from Jenkins Job
script: whatever.py --url "{{ job_url }}"
register: jenkins_artifacts
I'd like to be able to access the jenkins_artifacts results back in main_playbook if possible. I know you can access it from other hosts in the same playbook like this: "{{ hostvars['localhost']['jenkins_artifacts'].stdout_lines }}"
Is it the same idea for sharing across playbooks?
I'm confused what this question is about. Just use the variable name jenkins_artifacts:
- include: sub-playbook.yml job_url="http://some-jenkins-job"
- hosts: localhost
debug:
var: jenkins_artifacts
This might seem complicated but I love doing this in my Playbooks:
rc defines the name of the variable which contains the return value
ar gives the arguments to the include tasks
master.yml:
- name: verify_os
include_tasks: "verify_os/main.yml"
vars:
verify_os:
rc: "isos_present"
ar:
image: "{{ os.ar.to_os }}"
verify_os/main.yml:
---
- name: check image on device
ios_command:
commands:
- "sh bootflash: | inc {{ verify_os.ar.image }}"
register: image_check
- name: check if available
shell: "printf '{{ image_check.stdout_lines[0][0] }}\n' | grep {{ verify_os.ar.image }} | wc -l"
register: image_available
delegate_to: localhost
- set_fact: { "{{ verify_os.rc }}": "{{ true if image_available.stdout == '1' else false }}" }
...
I can now use the isos_present variable anywhere in the master.yml to access the returned value.

Resources