I have a playbook which contains more than one plays. One of the plays generates a variable and stores it using the set_stats module as an artifact. The subsequent plays need to access the variable, but an error occurs that the given variable is undefined. How can I access a variable in the artifacts? (Btw using a workflow which would result in saving the variable in the extra_variables instead of the artifacts container is no option in this scenario)
The Problem in detail:
I have the following playbook which includes 2 plays which get executed on different hosts:
---
- hosts: ansible
roles:
- role_parse_strings
- hosts: all, !ansible
roles:
- role_setup_basics
- role_create_accounts
The role "role_parse_strings" in the first play generates the variable "users" which gets stored because of the set_stats module as an artifact. The following content lands in the artifact section of ansible awx:
users:
- username: user1
admin: true
- username: user2
admin: false
When the role "role_create_accounts" gets executed which tries to access the variable "users" in the following way...
- user: name={{ item.username }}
shell=/bin/bash
createhome=yes
groups=user
state=present
with_items: "{{ users }}"
..this error gets displayed:
{
"msg": "'users' is undefined",
"_ansible_no_log": false
}
You can use set_fact to share variable between hosts. Below example show how to share a file content via set_fact.
- hosts: host1
pre_tasks:
- name: Slurp the public key
slurp:
src: /tmp/ssh_key.pub
register: my_key_pub
- name: Save the public key
set_fact:
my_slave_key: >-
{{ my_key_pub['content'] | b64decode }}
- hosts: host2
vars:
slave_key: "{{ my_slave_key }}"
pre_tasks:
- set_fact:
my_slave_key: >-
{{ hostvars[groups["host1"][0]].my_slave_key | trim }}
We saved the content of public key as a fact name called my_slave_key and
assgined it another variable as slave_key in host2 with:
hostvars[groups["host1"][0]].my_slave_key
Related
I have the following task set:
- name: Initialise inventory_data variable
set_fact:
inventory_data: ""
- name: Get Instance Inventory
remote_user: me
ansible.builtin.script: scripts/inventory.sh
register: inventory
- name: Set inventory variable
set_fact:
inventory_data: "{{ inventory_data }} {{ inventory.stdout_lines | join('\n')}}"
- name: Send to API
remote_user: me
ansible.builtin.uri:
url: https://myapi.com/endpoint
method: POST
body: "{{ inventory_data }}"
status_code: 200
The desired result is that i need to gather the results from inventory.sh and send them only once at the end of the run.
I've tried different variations, with run_once, delegate_to etc.. but i cannot seem to get this!
Edit:
I am trying to gather some data from my script which is ran on every host, however i wish to aggregate the results from all hosts, and send it once to an API.
First, if your play looks something like this:
- hosts: all
tasks:
- name: Initialise inventory_data variable
set_fact:
inventory_data: ""
- name: Get Instance Inventory
remote_user: me
ansible.builtin.script: scripts/inventory.sh
register: inventory
- name: Set inventory variable
set_fact:
inventory_data: "{{ inventory_data }} {{ inventory.stdout_lines | join('\n')}}"
It's not going to do you any good. Your inventory.sh script will run on each host, which will set the inventory variable for that host, and the subsequent task will append inventory.stdout_lines to inventory_data for that host. This won't collect the output from multiple hosts. You need to restructure your playbook. First, run the inventory script on each host:
- hosts: all
gather_facts: false
tasks:
- name: Get Instance Inventory
ansible.builtin.script: scripts/inventory.sh
register: inventory
Then in a second play targeting localhost, build your merged inventory variable and send the data to the API:
- hosts: localhost
gather_facts: false
tasks:
- name: create merged inventory
set_fact:
inventory_data: "{{ inventory_data + hostvars[item].inventory.stdout }}"
vars:
inventory_data: ""
loop: "{{ groups.all }}"
- name: Send to API
remote_user: me
ansible.builtin.uri:
url: https://myapi.com/endpoint
method: POST
body: "{{ inventory_data }}"
status_code: 200
This way, (a) you build the inventory_data variable correctly and (b) you only make a single API call.
I've made a complete, runnable example of this solution available here.
I created roles for getting different passwords from CyberArk. I noticed that everything in the roles was the same except for the query parameter to find the password, so I decided to make the query parameter a variable, and I send it to a new single CyberArk role as a variable.
Example from Playbook:
- name: Get AVI Dev Or Prod password
import_role:
name: /Users/n0118883/python/ansible/roles/cyberark_creds
vars:
query_parm: "Username=admin;Address=avi;Environment=Development"
- name: Get Venafi password
import_role:
name: /Users/n0118883/python/ansible/roles/cyberark_creds
vars:
query_parm: "Username=sahsp-avi-venafi;Address=LM"
I was hoping to call the role, pass the query parameter, and use multiple "set_fact" with a when clause to have the correct password assigned to the correct fact.
Example from Role:
- name: Get PW from cyberark
cyberark_credential:
api_base_url: "https://cyberark.lmig.com"
app_id: "AVI_Cyberark_Automation"
query: "{{ query_parm }}"
register: cyberark_command_output
- set_fact:
admin_password: "{{ cyberark_command_output | json_query('result.Content')}}"
when: '"Address=avi" in query_parm'
- set_fact:
venafi_password: "{{ cyberark_command_output | json_query('result.Content')}}"
when: '"sahsp-avi-venaf" in query_parm'
- name: "Return"
debug:
msg: "{{ admin_password }}"
- name: "Return"
debug:
msg: "{{ venafi_password }}"
No matter what I change when I run the playbook, I either get the first password twice or I get the password for the first and the error "The error was: 'venafi_password' is undefined" for the second.
In one playbook, I go to CyberArk to get three different passwords. I'm not sure if I'm trying to do too much with a role and should go back to three separate roles, or is this possible, but I'm just doing it wrong.
I can't figure this out. I have host_vars that I want to pass into my Ansible role. I can iterate through the items in my playbook, but it doesn't work if I give the item into my Ansible role. My host inventory looks like this:
hosts:
host_one:
domain: one
ip: 172.18.1.1
connection:
- connection_name: two
connection_ip: 172.18.1.2
- connection_index: three
local_hub_ip: 172.18.1.3
host_two:
domain: two
ip: 172.18.1.2
For instance,this works correctly:
tasks:
- debug:
msg: "{{item.connection_name}}"
with_items:
- "{{ connection }}"
will correctly print out the connections.connection_name for each connection I have, "two" and "three". However, if I try to pass it into a role:
tasks:
- name: Adding several connections
include_role:
name: connection-create
with_items:
- "{{ connection }}"
in which my role "connection-create" uses a variable called "connection_name" I get a:
FAILED! => {"msg": "'connection_name' is undefined"}
Why doesn't this work?
The loop with_items: "{{ connection }}" creates the loop variable item. The included role can use
item.connection_name
item.connection_ip
The loop variable can be renamed, if needed. See Loop control
This is code of my ansible script .
---
- hosts: "{{ host }}"
remote_user: "{{ user }}"
ansible_become_pass: "{{ pass }}"
tasks:
- name: Creates directory to keep files on the server
file: path=/home/{{ user }}/fabric_shell state=directory
- name: Move sh file to remote
copy:
src: /home/pankaj/my_ansible_scripts/normal_script/installation/install.sh
dest: /home/{{ user }}/fabric_shell/install.sh
- name: Execute the script
command: sh /home/{{ user }}/fabric_shell/install.sh
become: yes
I am running the ansible playbook using command>>>
ansible-playbook send_run_shell.yml --extra-vars "user=sakshi host=192.168.0.238 pass=Welcome01" .
But I don't know why am getting error
ERROR! 'ansible_become_pass' is not a valid attribute for a Play
The error appears to have been in '/home/pankaj/go/src/shell_code/send_run_shell.yml': line 2, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
---
- hosts: "{{ host }}"
^ here
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value. For instance:
with_items:
- {{ foo }}
Should be written as:
with_items:
- "{{ foo }}"
Please guide , what I am doing wrong.
Thanks in advance ...
ansible_become_pass is a connection parameter which you can set as variable:
---
- hosts: "{{ host }}"
remote_user: "{{ user }}"
vars:
ansible_become_pass: "{{ pass }}"
tasks:
# ...
That said, you can move remote_user to variables too (refer to the whole list of connection parameters), save it to a separate host_vars- or group_vars-file and encrypt with Ansible Vault.
Take a look on this thread thread and Ansible Page. I propose to use become_user in this way:
- hosts: all
tasks:
- include_tasks: task/java_tomcat_install.yml
when: activity == 'Install'
become: yes
become_user: "{{ aplication_user }}"
Try do not use pass=Welcome01,
When speaking with remote machines, Ansible by default assumes you are using SSH keys. SSH keys are encouraged but password authentication can also be used where needed by supplying the option --ask-pass. If using sudo features and when sudo requires a password, also supply --ask-become-pass (previously --ask-sudo-pass which has been deprecated).
I am trying to run multiple roles using with_items command, however I am getting error:
"ERROR! 'item' is undefined"
role.yml:
---
- hosts: '{{ host }}'
become: yes
roles:
- role: "{{item}}"
with_items: "{{ roles }}"
Here is my command:
ansible-playbook -i ./inventory/Dev ./playbooks/role.yml --extra-vars='{"host": "db", "roles": ["mysql", "apache"]}'
You cannot do it this way. with_ loops are not valid for roles.
If anything, you need to provide a list of roles to the roles: directive, so the syntax would be just like for the list of host groups hosts: '{{ host }}'. The problem is: Ansible does not resolve the variable for roles, so roles: '{{ roles }}' does not work.
What you can do, however, is to use include_role module in which you can access the variables.
No, include_role module doesn't take {{ item }} from the with_items as a value for name either.
So the only workaround I can think of (assuming you don't want to process the JSON beforehand) is to the include the roles statically:
tasks:
- include_role:
name: "mysql"
when: "'mysql' in roles"
- include_role:
name: "apache"
when: "'apache' in roles"
The roles need to exist on the control machine anyway, so all their names are predefined.
You cannot use with_ loop with roles directive.
A simple solution would be to use normal yaml list like below:
---
- hosts: '{{ host }}'
become: yes
roles:
- myrole1
- myrole2
Another example where you could declare the roles as well as pass corresponding variables to the role would be like below:
---
- hosts: '{{ host }}'
become: yes
roles:
- role:
name: myrole1
vars:
host: "db1"
myroles:
- mysql1
- apache1
- role:
name: myrole2
vars:
host: "db2"
myroles:
- mysql2
- apache2
Also avoid using ansible keywords as your variable names
Finally your command to run your playbook will be as below:
ansible-playbook -i ./inventory/Dev ./playbooks/role.yml