I have a "master" ansible playbook in which I call multiple playbooks using "import_playbook". At this point all the variables which are needed in the "child" playbooks are retrieved from the inventory file and this is working.
#master_playbook.yml
- import_playbook: playbook1.yaml
- import_playbook: playbook2.yaml
What I'd like to do is instead of listing the passwords required in the "child" playbooks in the inventory file, I'd like the "master" playbook to prompt the user for the required passwords and then pass those passwords to the "child" playbooks. The "child" playbooks would still retrieve the remaining variables (non-passwords) needed from the inventory file.
I can get the usage of vars_prompt to work and be able to input the passwords, however the part I'm stuck on is being able to then pass those variables from the "master" playbook to the "child" playbooks as part of the "import_playbook" operation.
Looking to accomplish something equivalent to the below, but this does not work.
#master_playbook.yml
-vars_prompt:
- name: "password1"
prompt: "Enter password1"
- name: "password2"
prompt: "Enter password2"
- import_playbook: playbook1.yaml
vars:
password1: "{{ password1 }}"
password2: "{{ password2 }}"
- import_playbook: playbook2.yaml
vars:
password1: "{{ password1 }}"
password2: "{{ password2 }}"
I managed to work around this by using set_fact, like so:
# site.yml
---
- name: Prompt for password
hosts: all
vars_prompt:
- name: prompted_password
prompt: Enter password
tasks:
- name: Set password as host fact
set_fact:
password: "{{prompted_password}}"
- name: Import playbook 1
import_playbook: playbook1.yml
- name: Import playbook 1
import_playbook: playbook2.yml
You should now be able to use password within the imported playbooks. That said, I'm not sure this is the proper ansible way of doing things.
I created the following playbook
---
- hosts: localhost
roles:
- parent
tasks:
- name: Importing child01 playbook
import_playbook: child01.yml
...
I then ran the following command:
ansible-playbook -e "pass1=blah" ./import.yml
Both the parent role and child01 playbook just have a debug statement showing the value of pass1. The output I got is as follows:
PLAY [localhost] ***************************************************************
TASK [Gathering Facts] *********************************************************
ok: [localhost]
TASK [parent : Parent task 1] **************************************************
ok: [localhost] => {
"pass1": "blah"
}
PLAY [localhost] ***************************************************************
TASK [Gathering Facts] *********************************************************
ok: [localhost]
TASK [child01 : Child 1 first task] ********************************************
ok: [localhost] => {
"pass1": "blah"
}
PLAY RECAP *********************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0
So, cannot include using vars_prompt or vars, but can pass in on the command line. Maybe this can work for you?
Related
I have ansible installed on the Windows Subsystem for Linux. This version is 2.9.6.
I also have an ansible tower that is version 3.7.2 which has Ansible version 2.9.27.
I basically use the ansible installation on my WSL to play with and debug playbooks to get them working. Once they are working, I upload them to my Git Repository and pull them into the Ansible Tower for execution.
I am still fairly new to Ansible so perhaps this is a very simple issue. I have a playbook that runs just fine on my ansible (2.9.6) WLS environment.
When I run the same playbook in my Ansible Tower, it doesn't run any tasks.
The playbook is fairly simple. I want to use it to change the password on a local Windows account. The playbook is in a file named change_user_password.yml. The contents are shown below:
- name: Change user password
hosts: all
tasks:
- name: Include OS-specific variables.
include_vars: "{{ ansible_os_family }}.yml"
- name: Print OS Family
debug:
msg: "Ansible OS family is {{ ansible_os_family }}"
- name: Print uname
debug:
msg: "Uname variable is {{ uname }}"
- name: Print newpass
debug:
msg: "Newpass variable is {{ newpass }}"
- name: Change pwd (Redhat).
ping:
when: ansible_os_family == 'RedHat'
- name: Change pwd (Debian).
ping:
when: ansible_os_family == 'Debian'
- name: Change pwd (Windows).
win_user:
name: "{{ uname }}"
password: "{{ newpass }}"
when: ansible_os_family == 'Windows'
When run on the command line with ansible-playbook in my WSL environment I pass in the --extra-vars for uname and newpass variables as shown below:
ansible-playbook -i ../hosts.ini --limit cssvr-prod change_user_password.yml --extra-vars="uname=myadmin newpass=test1234TEST"
Output typically looks like this:
PLAY [Change user password] ***************************************************************************************************************************************************************************
TASK [Gathering Facts] ********************************************************************************************************************************************************************************
ok: [cssvr-prod]
TASK [Include OS-specific variables.] *****************************************************************************************************************************************************************
ok: [cssvr-prod]
TASK [Print OS Family] ********************************************************************************************************************************************************************************
ok: [cssvr-prod] => {
"msg": "Ansible OS family is Windows"
}
TASK [Print uname] ************************************************************************************************************************************************************************************
ok: [cssvr-prod] => {
"msg": "Uname variable is myadmin"
}
TASK [Print newpass] **********************************************************************************************************************************************************************************
ok: [cssvr-prod] => {
"msg": "Newpass variable is test1234TEST"
}
TASK [Change pwd (Redhat).] ***************************************************************************************************************************************************************************
skipping: [cssvr-prod]
TASK [Change pwd (Debian).] ***************************************************************************************************************************************************************************
skipping: [cssvr-prod]
TASK [Change pwd (Windows).] **************************************************************************************************************************************************************************
changed: [cssvr-prod]
PLAY RECAP ********************************************************************************************************************************************************************************************
cssvr-prod : ok=6 changed=1 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
When I run this playbook from Ansible Tower, I add uname and newpass as extra variables in the Extra Variables box on the Template for this play. I add the cssvr-prod host in the Limit box. When I run it, no tasks are run. NOTE: The warning below is expected, the inventory and groups are imported from Azure. Some of our Azure resource groups have hyphens in their name which apparently is illegal in the ansible hosts file as a group name.
Using /etc/ansible/ansible.cfg as config file
SSH password:
[WARNING]: Invalid characters were found in group names but not replaced, use
-vvvv to see details
PLAY [all] *********************************************************************
PLAY RECAP *********************************************************************
I'm pulling what little hair I have left out trying to figure out why the code behaves this way on Tower.
I am tasked with creating a playbook, where I perform the following operations:
Fetch information from a YAML file (the file contains details on VLANs)
Cycle through the YAML objects and verify which subnet contains the IP, then return the object
The object contains also a definition of the inventory_hostname where to run the Ansible playbook
At the moment, I have the following (snippet):
Playbook:
- name: "Playbook"
gather_facts: false
hosts: "localhost"
tasks:
- name: "add host"
add_host:
name: "{{ vlan_target }}"
- name: "debug"
debug:
msg: "{{ inventory_hostname }}"
The defaults file defines the target_host as an empty string "" and then it is evaluated within another role task, like so (snippet):
Role:
- set_fact:
vlan_object: "vlan | trim | from_json | first"
- name: "set facts based on IP address"
set_fact:
vlan_name: "{{ vlan_object.name }}"
vlan_target: "{{ vlan_object.target }}"
delegate_to: localhost
What I am trying to achieve is to change the hosts: variable so that I can use the right target, since the IP/VLAN should reside on a specific device.
I have tried to put the aforementioned task above the add_host, or even putting in the same playbook, like so:
- name: "Set_variables"
hosts: "localhost"
tasks:
- name: "Set target host"
import_role:
name: test
tasks_from: target_selector
- name: "Playbook"
gather_facts: false
hosts: "localhost"
Adding a debug clause to the above playbook sets the right target, but it is not re-used below, making me think that the variable is not set globally, but within that run.
I am looking for a way to set the target, based on a variable that I am passing to the playbook.
Does anyone have any experience with this?
Global facts are not a thing, at best you can assign a fact to all hosts in the play, but since you are looking to use the said fact to add an host, this won't be a solution for your use case.
You can access facts of another hosts via the hostvars special variable, though. It is a dictionary where the keys are the names of the hosts.
The usage of the role is not relevant to your issue at hand, so, in the demonstration below, let's put this aside.
Given the playbook:
- hosts: localhost
gather_facts: no
tasks:
- set_fact:
## fake object, since we don't have the structure of your JSON
vlan_object:
name: foo
target: bar
- set_fact:
vlan_name: "{{ vlan_object.name }}"
vlan_target: "{{ vlan_object.target }}"
run_once: true
- add_host:
name: "{{ vlan_target }}"
- hosts: "{{ hostvars.localhost.vlan_target }}"
gather_facts: no
tasks:
- debug:
var: ansible_play_hosts
This would yield
PLAY [localhost] *************************************************************
TASK [set_fact] **************************************************************
ok: [localhost]
TASK [set_fact] **************************************************************
ok: [localhost]
TASK [add_host] **************************************************************
changed: [localhost]
PLAY [bar] *******************************************************************
TASK [debug] *****************************************************************
ok: [bar] =>
ansible_play_hosts:
- bar
Lets say I have got inventory file like this
inventory.txt
abc
cde
def
[check1:children]
abc
[check2:children]
cde
[check3: children]
def
Now I will take input from user eg: check1,check3 separated by comma in a variable and then I want to run my next play on those groups check1,check3.
How can I achieve this?
A comma separated list of groups or hosts is a perfectly valid pattern for the hosts of a playbook.
So you can just pass it directly in the hosts attribute of your playbook:
- hosts: "{{ user_provider_hosts }}"
gather_facts: no
tasks:
- debug:
Then, you just have to add this values in the --extra-vars (or short, -e) flag of the playbook command:
ansible-playbook play.yml --extra-vars "user_provider_hosts=check1,check3"
This would yield:
TASK [debug] ******************************************************************
ok: [abc] =>
msg: Hello world!
ok: [def] =>
msg: Hello world!
Another option is to target all hosts:
- hosts: all
gather_facts: no
tasks:
- debug:
And use the purposed --limit flag:
ansible-playbook play.yml --limit check1,check3
A third option would be to use a play targeting localhost to prompt the user for the groups to target, then use a fact set by localhost to target those groups in another play:
- hosts: localhost
gather_facts: no
vars_prompt:
- name: _hosts
prompt: On which hosts do you want to act?
private: no
tasks:
- set_fact:
user_provider_hosts: "{{ _hosts }}"
- hosts: "{{ hostvars.localhost.user_provider_hosts }}"
gather_facts: no
tasks:
- debug:
Would interactively ask for hosts and act on the user provider hosts:
On which hosts do you want to act?: check1,check3
PLAY [localhost] **************************************************************
TASK [set_fact] ***************************************************************
ok: [localhost]
PLAY [check1,check3] **********************************************************
TASK [debug] ******************************************************************
ok: [abc] =>
msg: Hello world!
ok: [def] =>
msg: Hello world!
The Script, running on a Linux host, should call some Windows hosts holding Oracle Databases. Each Oracle Database is in DNS with its name "db-[ORACLE_SID]".
Lets say you have a database with ORACLE SID TEST02, it can be resolved as db-TEST02.
The complete script is doing some more stuff, but this example is sufficient to explain the problem.
The db-[SID] hostnames must be added as dynamic hosts to be able to parallelize the processing.
The problem is that oracle_databases is not passed to the new playbook. It works if I change the hosts from windows to localhost, but I need to analyze something first and get some data from the windows hosts, so this is not an option.
Here is the script:
---
# ansible-playbook parallel.yml -e "databases=TEST01,TEST02,TEST03"
- hosts: windows
gather_facts: false
vars:
ansible_connection: winrm
ansible_port: 5985
ansible_winrm_transport: kerberos
ansible_winrm_kerberos_delegation: true
tasks:
- set_fact:
database: "{{ databases.split(',') }}"
- name: Add databases as hosts, to parallelize the shutdown process
add_host:
name: "db-{{ item }}"
groups: oracle_databases
loop: "{{ database | list}}"
##### just to check, what is in oracle_databases
- name: show the content of oracle_databases
debug:
msg: "{{ item }}"
with_inventory_hostnames:
- oracle_databases
- hosts: oracle_databases
gather_facts: true
tasks:
- debug:
msg:
- "Hosts, on which the playbook is running: {{ ansible_play_hosts }}"
verbosity: 1
My inventory file is just small, but there will be more windows hosts in future:
[adminsw1#obelix oracle_change_home]$ cat inventory
[local]
localhost
[windows]
windows68
And the output
[adminsw1#obelix oracle_change_home]$ ansible-playbook para.yml -l windows68 -e "databases=TEST01,TEST02"
/usr/lib/python2.7/site-packages/ansible/parsing/vault/__init__.py:44: CryptographyDeprecationWarning: Python 2 is no longer supported by the Python core team. Support for it is now deprecated in cryptography, and will be removed in a future release.
from cryptography.exceptions import InvalidSignature
/usr/lib/python2.7/site-packages/requests/__init__.py:89: RequestsDependencyWarning: urllib3 (1.23) or chardet (2.2.1) doesn't match a supported version!
RequestsDependencyWarning)
PLAY [windows] *****************************************************************************************************************************
TASK [set_fact] ****************************************************************************************************************************
ok: [windows68]
TASK [Add databases as hosts, to parallelize the shutdown process] *************************************************************************
changed: [windows68] => (item=TEST01)
changed: [windows68] => (item=TEST02)
TASK [show the content of oracle_databases] ************************************************************************************************
ok: [windows68] => (item=db-TEST01) => {
"msg": "db-TEST01"
}
ok: [windows68] => (item=db-TEST02) => {
"msg": "db-TEST02"
}
PLAY [oracle_databases] ********************************************************************************************************************
skipping: no hosts matched
PLAY RECAP *********************************************************************************************************************************
windows68 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
It might be possible that Ansible is not parsing the updated inventory file file, or the hosts name is being malformed in as it updates the inventory.
In this scenario, you can use the -vv or -vvvv parameter in your Ansible command to get extra logging.
This will give you a complete picture into what Ansible is actually doing as it tries to parse hosts.
I found out, what the problem was. The playbook is restricted to a host "windows68" and therefore canĀ“t be run at the hosts added by the dynamic inventory.
It will work that way:
[adminsw1#obelix oracle_change_home]$ ansible-playbook para.yml -l windows68,oracle_databases -e "databases=TEST01,TEST02"
ansible inventory
# hosts
[groupA]
192.168.1.1
[groupB]
192.168.1.1
192.168.1.3
ansible tasks:
# site.yml
---
- name: test
hosts: all
tasks:
- name: taskA
command: echo testA
when: "'groupA' in group_names "
- name: taskB
command: echo testB
when: "'groupB' in group_names "
I want to perform the task of taskA through groupA, how to do it?I use the following command to execute
ansible-playbook site.yml -l groupA -i hosts
However, getting the following result is not what I want because it also executes taskB, but I only want to use groupA to execute taskA.
I don't want to use ansible tag because I have a lot roles, there will be a lot of work to add each tag to each role.
PLAY [test] *******************************************************************************************************************************************
TASK [Gathering Facts] ********************************************************************************************************************************
ok: [192.168.1.1]
TASK [taskA] ******************************************************************************************************************************************
changed: [192.168.1.1]
TASK [taskB] ******************************************************************************************************************************************
changed: [192.168.1.1]
PLAY RECAP ********************************************************************************************************************************************
192.168.1.1 : ok=3 changed=2 unreachable=0 failed=0
Q "I just want the machine inside group_B to execute Task of B...For example, the machine inside group_B only executes Task of B."
A: Test the membership of the hosts in the groups. See the code below.
- hosts: all
tasks:
- name: Task of group_A
command: bash groupA.sh
when: inventory_hostname in groups['group_A']
- name: Task of group_B
command: bash groupB.sh
when: inventory_hostname in groups['group_B']
Q: "I just want to run Task of group_B. What should my order be? ansible-playbook site.yml -l group_B -i ansible_inventory If group_A and group_B have a common ip, Task of group_A will be executed, but I only want to execute Task of group_B."
"I don't want to use ansible tag because I have a lot. roles, there will be a lot of work to add each tag to each role."
A: Use include_role and apply tags (added in 2.7). Such applied tags are inherited by all tasks in the included role. See the code below.
- name: Task of group_B
include_role:
name: role123
apply:
tags: group_B
when: inventory_hostname in groups['group_B']
tags: group_B
Run the play
ansible-playbook -t group_B -i ansible_inventory site.yml