Ansible: redefine variable value only if another variable defined - ansible

For some reasons I need from time to time run my playbook for same hosts with different users (different from hosts default ansible_user).
My problem is how to rewrite value of ansible_user and ansible_password **ONLY **if my temp vars from vars_prompt are defined. Instead it it should use host default ansible_user and ansible_password
I tried
- name: Some playbook
hosts: my_host_group
vars_prompt:
- name: custom_ssh_user
prompt: "Print SSH user login(Print blank if default)"
private: no
- name: custom_ssh_passw
prompt: "Print SSH user password(Print blank if default)"
private: yes
confirm: yes
unsafe: no
vars:
ansible_user: "{{ custom_ssh_user if custom_ssh_user|length > 0 else ansible_user }}"
ansible_password: "{{ custom_ssh_passw if custom_ssh_passw|length > 0 else ansible_password }}"
But this IF construct failed with error on loop defining vars when vars_prompt are empty.
How to do this defining properly?

Related

Ansible vars _prompt: playbook fails before value can be assigned to variable

As an example:
- name: SET_STEP1
prompt: "Set value for step 1"
default: "the value for step 1"
private: no
- name: SET_STEP2
prompt: "Set value for step 2"
default: "{{ SET_STEP2 }} depends on the value of {{ SET_STEP1 }}"
private: no
Result: ERROR! 'SET_STEP1' is undefined
Even though a default value is assigned, this will not run, because Ansible thinks SET_STEP1 is undefined. SET_STEP2 can only be correctly defined after SET_STEP1 is defined by the user, or the default value is assumed.
How can I achieve this?
https://docs.ansible.com/ansible/latest/user_guide/playbooks_prompts.html
Even though a default value is assigned, this will not run, because Ansible thinks SET_STEP1 is undefined
Unfortunately not. At the time Ansible is parsing through the vars_prompt section, the variable is undefined. Therefore such construct has to fail and produce an error.
SET_STEP2 can only be correctly defined after SET_STEP1 is defined by the user, or the default value is assumed. How can I achieve this?
According Interactive input: prompts
Prompts for individual vars_prompt variables will be skipped for any variable that is already defined through the command line --extra-vars option,
you could use an approach like
ansible-playbook vars_prompt.yml --extra-vars="username=TEST"
Provide password for TEST: _
or within the tasks section the pause module with parameter prompt
- name: Gather SET_STEP2
ansible.builtin.pause:
prompt: "Set value for step 2"
echo: no
register: result
- name: Show result
debug:
var: result
- name: SET_STEP2
set_fact:
SET_STEP: "{{ result.user_input }}"
or define variables.
Further Documentation
Defining variables at runtime
Variable precedence: Where should I put a variable?
Adding variables to inventory
include_vars module – Load variables from files, dynamically within a task
Furthermore, and using the example from Ansible documentation, a test like
---
- hosts: localhost
become: false
gather_facts: false
vars:
username: "root"
vars_prompt:
- name: username
prompt: "What is your username?"
private: false
- name: password
prompt: "Provide password for {{ username }}"
private: true
tasks:
- name: Show username and password
debug:
msg: "{{ username }}:{{ password }}"
will result into an output of
What is your username?: test
Provide password for root:
PLAY [localhost] ***********
TASK [Show username and password] *******
ok: [localhost] =>
msg: test:test
... see that the username defaults to root, interactive entered is test, printed is root and the value is set for test
So, parts of your given example or requirement are technically not possible, at least not in that way.

Making prompted vars usable across the playbook

I have one playbook with multiple tasks that have to run on different hosts.
In the beginning of the play I want to prompt the operator for their credentials which are the same for every host in the play. I want to have those credentials "stored" somewhere so they can be used across the tasks to log in on the provided host(s).
Playbook looks as followed,
---
- name: Ask for credentials
vars_prompt:
- name: username
prompt: "Username?"
- name: password
prompt: "Password?"
tasks:
- set_fact:
username: "{{username}}"
- set_fact:
password: "{{password}}"
- hosts: Host1
vars:
ansible_user: "{{ username }}"
ansible_password: "{{ password }}"
tasks:
- name: Do stuff
- hosts: Host2
vars:
ansible_user: "{{username}}"
ansible_password: "{{password}}"
tasks:
- name: Do stuff
...
From the moment the play hits the first task it will fail with the flowing error,
msg: 'The field ''remote_user'' has an invalid value, which includes an undefined variable. The error was: ''username'' is undefined'
Anyone that has experience in making prompted vars usable across the whole play and all tasks?
Q: "Make prompted vars usable across the whole play and all tasks>"
A: Run the first play with the group of all hosts that should be connected later. Run once the set_fact task. This will create the variables username and password for all hosts in the group.
For example if the group test_jails comprises hosts test_01, test_02, test_03 the play
- hosts: test_jails
vars_prompt:
- name: "username"
prompt: "Username?"
- name: "password"
prompt: "Password?"
tasks:
- set_fact:
username: "{{ username }}"
password: "{{ password }}"
run_once: true
- hosts: test_01
vars:
ansible_user: "{{ username }}"
ansible_password: "{{ password }}"
tasks:
- debug:
msg: "{{ ansible_user }} {{ ansible_password }}"
gives
ok: [test_01] => {
"msg": "admin 1234"
}

How to change userid and get the home directory for that user?

I want to be able to change the userid within a playbook and then get the home directory on a host for that userid. How would I do this?
I know I can change the userid with 'become:' and 'become_user:', but that works at the task level. I can get the home directory with the '{{ lookup('env', 'HOME) }}' variable, but that seems to work at the playbook level. This was my latest attempt:
---
- hosts: all
vars:
user_id: "{{ userid }}"
tasks:
- set_fact:
home_dir: "{{ lookup('env', 'HOME') }}"
become: yes
become_user: "{{ user_id }}"
- debug:
msg: "Variable values: user_id: {{ user_id }} home_dir: {{ home_dir }}"
My command looked like this:
$ ansible-playbook myplaybook.yaml --limit MYHOSTS --extra-vars "userid=myid"
This was the response:
ok: [host-01] => {
"msg": "Variable values: user_id: myid home_dir: /root"
}
ok: [host-02] => {
"msg": "Variable values: user_id: myid home_dir: /root"
}
ok: [host-03] => {
"msg": "Variable values: user_id: myid home_dir: /root"
}
Is there a way I can change the userid to "myid" and get the playbook to know that the home directory is "/home/myid"?
Thanks!
When you use become and become_user, you're actually telling Ansible to change the current user, from what it initially logged-in with.
As per Ansible Docs:
These Directives (become, become_user, etc.) can be set from play to task level, but are overridden by connection variables as they can be host specific.
You must also note that by using the above syntax, you are actually activating a privilege escalation method, to become a non-privileged user. This is rather risky with quite a few limitation. Please read here: Becoming an Unprivileged User.
I hope the following play, will give you clearer picture:
---
- hosts: all
gather_facts: no
remote_user: "{{ userid }}"
tasks:
- name: first_task
register: first_task
shell: "whoami"
- name: become-root
register: become-root
shell: "whoami"
become: yes
become_user: root
- name: final_task
register: final_task
shell: "whoami"
You should run the above, by executing this command:
# ansible-playbook playbook.yaml -i /path/to/inventory/hosts -e "userid=myid"
It will return:
first_task --> myid
become-root --> root
final_task --> myid
Note: You can also override remote_user, separately for each task.
But if you still require to do it your own way, you can try this one:
---
- hosts: all
vars:
user_id: "{{ userid }}"
tasks:
- set_fact:
home_dir: "{{ lookup('env', 'HOME') }}"
become: true
- debug:
msg: "Variable values: user_id: {{ userid }} home_dir: {{ home_dir }}"
when: userid is defined
Run with this command:
# ansible-playbook myplaybook.yaml -i path/to/inventory/hosts --limit MYHOSTS --become-user=myid --extra-vars "userid=myid"
And finally, if none of these did what you were after, try getting the home directory, by using the command module and then retrieve its output by first registering it, and then using {{ registered_name.stdout_lines }}:
- name: user_home
register: user_home
action: command eval echo "~{{ userid }}"
- debug:
msg: "Home path is {{ user_home.stdout_lines }}
Using this last workaround, you don't even need to change user as it prints out the home directory for user userid.
# ansible-playbook myplaybook.yaml -i path/to/inventory/hosts --limit MYHOSTS -e "userid=myid"
Hope it helped; but this very question was asked before. You might be able to find more details here: How to switch a user per task or set of tasks?.

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.

Ansible recursive checks in playbooks

We need to go through this structure
Zone spec
https://gist.github.com/git001/9230f041aaa34d22ec82eb17d444550c
I was able to run the following snipplet but now I'm stucked at the error checking.
playbook
--
- hosts: all
gather_facts: no
vars_files:
- "../doc/application-zone-spec.yml"
roles:
- { role: ingress_add, customers: "{{ application_zone_spec }}" }
role
- name: check if router exists
shell: "oc get dc -n default {{ customers.zone_name }}-{{ item.type }}"
with_items: "{{ customers.ingress }}"
ignore_errors: True
register: check_router
- name: Print ingress hostnames
debug: var=check_router
- name: create new router
shell: "echo 'I will create a router'"
with_items: "{{ customers.ingress }}"
when: check_router.rc == 1
Output of a ansible run
https://gist.github.com/git001/dab97d7d12a53edfcf2a69647ad543b7
The problem is that I need to go through the ingress items and I need to map the error of the differnt types from the "check_router" register.
It would be nice to make something like.
Pseudo code.
Iterate through the "customers.ingress"
check in "check_router" if the rc is ! 0
execute command.
We use.
ansible-playbook --version
ansible-playbook 2.1.0.0
config file = /etc/ansible/ansible.cfg
configured module search path = Default w/o overrides
You can replace the second loop with:
- name: create new router
shell: "echo 'I will create a router with type {{ item.item }}'"
with_items: "{{ check_router.results }}"
when: item.rc == 1
This will iterate over every step of check_route loop and you can access original items via item.item.

Resources