Ansible How to read and manipulate settings based on an acronym - ansible

What strategy you use to control custom settings? Using ini files or save files in yaml format ?!
For example I have settings for acronym of my establishment.
How do I access from my playbook these variables or custom data?
I want to read the settings conditionally. Based on the referenced acronym.
Like that:
ansible-playbook -i hosts myplaybooy.yml my_acronym
Example structure where CAX, BAC and SIN are acronyms:
CAX
IP_SERVER = 200.253.167.1
IP_DB_MASTER = 200.253.167.20
IP_DB_SLAVE = 200.253.167.21
IP_MIRROR = 200.253.167.13
BAC
IP_SERVER = 200.253.168.1
IP_DB_MASTER = 200.253.168.20
IP_DB_SLAVE = 200.253.168.21
IP_MIRROR = 200.253.168.13
SIN
IP_SERVER = 200.253.169.1
IP_DB_MASTER = 200.253.169.20
IP_DB_SLAVE = 200.253.169.21
IP_MIRROR = 200.253.169.13
The reason for this change is to adapt my playbook for the ansible-pull. In ansible-pull conditional tests like these not work.
- name: Download pub key acesso
fetch:
src=~acesso/.ssh/id_rsa.pub
dest="{{role_path}}/../appserver/files/pub_keys/appservers/csa{{groupvar.server.acronym}}am/acesso.pub" flat=yes
sudo: yes
delegate_to: "{{hostvars['csa'+groupvar.server.acronym+'am'].ansible_ssh_host}}"
when: inventory_hostname in groups['pdv'+groupvar.server.acronym]
run_once: True
ignore_errors: yes
I think this is due to the fact that ansible-pull look at inventory subsets.

I think this will work for you. If you can post more from the playbook you're using, I can help you make this work for the actual tasks you're running.
This is the command-line syntax I used:
ansible-playbook -i hosts playbook.yml --extra-vars "ACRONYM=SIN"
playbook.yml:
---
- name: show vars
hosts: localhost
vars_files:
- var_file.yml
tasks:
- debug:
msg: IP_SERVER = {{Acronyms[ACRONYM].IP_SERVER}}
- debug:
msg: IP_DB_MASTER = {{Acronyms[ACRONYM].IP_DB_MASTER}}
- debug:
msg: IP_DB_SLAVE = {{Acronyms[ACRONYM].IP_DB_SLAVE}}
- debug:
msg: IP_MIRROR = {{Acronyms[ACRONYM].IP_MIRROR}}
var_file.yml:
Acronyms: {
CAX:
{ IP_SERVER: 200.253.167.1,
IP_DB_MASTER: 200.253.167.20,
IP_DB_SLAVE: 200.253.167.21,
IP_MIRROR: 200.253.167.13
},
BAC:
{ IP_SERVER: 200.253.168.1,
IP_DB_MASTER: 200.253.168.20,
IP_DB_SLAVE: 200.253.168.21,
IP_MIRROR: 200.253.168.13
},
SIN:
{ IP_SERVER: 200.253.169.1,
IP_DB_MASTER: 200.253.169.20,
IP_DB_SLAVE: 200.253.169.21,
IP_MIRROR: 200.253.169.13
}
}
This is my terminal output:
PLAY [show vars] ***************************************************************
TASK [setup] *******************************************************************
ok: [localhost]
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "IP_SERVER = 200.253.169.1"
}
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "IP_DB_MASTER = 200.253.169.20"
}
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "IP_DB_SLAVE = 200.253.169.21"
}
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "IP_MIRROR = 200.253.169.13"
}
PLAY RECAP *********************************************************************
localhost : ok=5 changed=0 unreachable=0 failed=0

Related

Ansible looping over main playbook

I created a "main" playbook that create and deploy a complex application let's say an entire house.
The "main" looks like this :
- hosts: host1
roles:
- { role: role1 }
- hosts: host2
roles:
- { role: role2 }
- hosts: localhost
roles:
- { role: role3 }
- { role: role4 }
- { role: role5 }
- { role: role6 }
I would like now to be able to deploy multiple houses using a CSV as input, containing my variables.
I was expecting to just import that "main" playbook, and loop over my CSV so that I can create multiple houses.
Based on the documentation I'm not able to loop using import_playbook, so I'm a stuck on how I could use this "main" for multiple creations in series.
Has anybody been in that situation or has an idea on how I could resolve this ?
You can use the csvfile lookup to read data from CSV files.
Example CSV file roles.csv:
host1,role1
host2,role2
localhost,role3 role4 role5 role6
Create mock-up roles:
for i in {1,2,3,4,5,6}; do
mkdir -p roles/role$i/tasks &&
echo "- debug: msg=\"role$i\"" > roles/role$i/tasks/main.yaml
done
And in the playbook you use the include_role syntax instead of roles, because include_role can be used in a loop. By this you can loop over the array read from the CSV file.
---
- hosts: localhost
connection: local
tasks:
- name: Use role in loop
include_role:
name: '{{ roleinputvar }}'
loop: "{{ lookup('csvfile', inventory_hostname + ' file=roles.csv delimiter=,').split(' ') }}"
loop_control:
loop_var: roleinputvar
connection: local is just for this example.
When run the roles 3 to 6 are executed:
$ ansible-playbook roles.yaml
[WARNING]: provided hosts list is empty, only localhost is available. Note that the
implicit localhost does not match 'all'
PLAY [localhost] **********************************************************************
TASK [Gathering Facts] ****************************************************************
ok: [localhost]
TASK [Use role in loop] ***************************************************************
TASK [role3 : debug] ******************************************************************
ok: [localhost] => {
"msg": "role3"
}
TASK [role4 : debug] ******************************************************************
ok: [localhost] => {
"msg": "role4"
}
TASK [role5 : debug] ******************************************************************
ok: [localhost] => {
"msg": "role5"
}
TASK [role6 : debug] ******************************************************************
ok: [localhost] => {
"msg": "role6"
}
PLAY RECAP ****************************************************************************
localhost : ok=5 changed=0 unreachable=0 failed=0

In Ansible, how can I iterate over stdout with an array?

Ansible v2.6.3
I have the simple task, which gets the AWS ARNs in my jenkins ECS cluster
tasks:
- command: aws ecs list-container-instances --cluster jenkins
register: jenkins_ecs_containers
- debug: var=jenkins_ecs_containers.stdout
and has the following output
TASK [debug] *******************************************************************
ok: [localhost] => {
"jenkins_ecs_containers.stdout": {
"containerInstanceArns": [
"arn:aws:ecs:us-east-1:arn0",
"arn:aws:ecs:us-east-1:arn1"
]
}
}
How can I iterate over the ARNs? I tried
- debug: var=item
with_items: jenkins_ecs_containers.stdout.containerInstanceArns
gives
TASK [debug] *******************************************************************
ok: [localhost] => (item=jenkins_ecs_containers.stdout.containerInstanceArns) => {
"item": "jenkins_ecs_containers.stdout.containerInstanceArns"
}
or
- debug: var=item
with_items: "{{ jenkins_ecs_containers.stdout.containerInstanceArns }}"
gives
TASK [debug] *******************************************************************
fatal: [localhost]: FAILED! => {"msg": "'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'containerInstanceArns'"}
to retry, use: --limit #/Users/cfouts/git-repos/ansible/playbooks/loop.retry
Thanks!
I created a file with your output. So I used set_fact. Otherwise, it's just a string, not a JSON object:
tasks:
- command: cat files/stdout.txt
register: result
- debug: var=result.stdout
- set_fact:
jenkins_ecs_containers: "{{ result.stdout }}"
- debug:
msg: "{{ item }}"
with_items: "{{ jenkins_ecs_containers.containerInstanceArns }}"
This gave me the following output:
PLAY [localhost] ***************************************************************
TASK [Gathering Facts] *********************************************************
ok: [localhost]
TASK [command] *****************************************************************
changed: [localhost]
TASK [debug] *******************************************************************
ok: [localhost] => {
"result.stdout": {
"containerInstanceArns": [
"arn:aws:ecs:us-east-1:arn0",
"arn:aws:ecs:us-east-1:arn1"
]
}
}
TASK [set_fact] ****************************************************************
ok: [localhost]
TASK [debug] *******************************************************************
ok: [localhost] => (item=None) => {
"msg": "arn:aws:ecs:us-east-1:arn0"
}
ok: [localhost] => (item=None) => {
"msg": "arn:aws:ecs:us-east-1:arn1"
}
PLAY RECAP *********************************************************************
localhost : ok=5 changed=1 unreachable=0 failed=0
You can iterate over like this:
- debug:
msg: "{{ item[1] }}"
with_subelements:
- "{{ jenkins_ecs_containers }}"
- containerInstanceArns
Go through this link, it will make it clearer.

How to pass vars in nested Ansible playbook?

I have a nested ansible playbook (master) file and I want to call included playbook (slave) with their own JSON vars.
Master.yaml
- name: this is a play at the top level of a file
hosts: local
connection: local
tasks:
- debug: msg=hello
- include: slave_first.yaml
- include: slave_second.yaml
slave_first.yaml should make use of "vars/slave_first_vars.json" file and slave_second.yaml should make use of "vars/slave_second_vars.json" file.
When including playbooks you can only override variables with vars statement, like:
- include: slave_first.yaml
vars:
myvar: foo
- include: slave_second.yaml
vars:
myvar: foo
There are no other options for PlaybookInclude.
If you need to load variables from files, you have to use vars_files or include_vars inside your slave playbooks.
In your scenario, I'll use like this, master.yml:
- hosts: localhost
connection: local
tasks:
- include: slave_first.yml
vars:
VAR_FILE: "slave_first_vars"
- include: slave_second.yml
vars:
VAR_FILE: "slave_second_vars"
While slave_first.yml and slave_second.yml are like this, in my case both are same but you get an idea that how you can use them:
slave_first.yml:
---
- include_vars: "{{ VAR_FILE }}.yml"
- debug:
msg: "{{ DOMAIN_NAME }}"
slave_second.yml:
---
- include_vars: "{{ VAR_FILE }}.yml"
- debug:
msg: "{{ DOMAIN_NAME }}"
Now come to the different variable part:
slave_first_vars.yml: in your case it will be json
---
DOMAIN_NAME: "first.com"
slave_second_vars.yml:
---
DOMAIN_NAME: "second.com"
Then you can run and verify that if work as expected:
➤ansible-playbook -i localhost, master.yml
PLAY [localhost] **********************************************************************************
TASK [Gathering Facts] **********************************************************************************
ok: [localhost]
TASK [include_vars] **********************************************************************************
ok: [localhost]
TASK [debug] **********************************************************************************
ok: [localhost] => {
"changed": false,
"msg": "first.com"
}
TASK [include_vars] **********************************************************************************
ok: [localhost]
TASK [debug] **********************************************************************************
ok: [localhost] => {
"changed": false,
"msg": "second.com"
}
PLAY RECAP **********************************************************************************
localhost : ok=5 changed=0 unreachable=0 failed=0
Hope that might help you!

Ansible hostvars undefined

I have a very simple play which saves vars and looks them up in hostvars.
- name: Set hostvars
hosts: localhost
vars:
var_one: "I am a"
var_two: "test"
tasks:
- debug: var=hostvars['localhost']['var_one']
- debug: var=hostvars['localhost']['var_two']
However, when I run this play the vars aren't defined:
PLAY [Set hostvars] ************************************************************
TASK [setup] *******************************************************************
ok: [localhost]
TASK [debug] *******************************************************************
ok: [localhost] => {
"hostvars['localhost']['var_one']": "VARIABLE IS NOT DEFINED!"
}
TASK [debug] *******************************************************************
ok: [localhost] => {
"hostvars['localhost']['var_two']": "VARIABLE IS NOT DEFINED!"
}
How can I save these vars in hostvars?
You can set host facts runtime using set_fact module:
---
- name: Set hostvars
hosts: localhost
tasks:
- set_fact: var_one="I am a"
- set_fact: var_two="test"
- debug: var=hostvars['localhost']['var_one']
- debug: var=hostvars['localhost']['var_two']
Quoting the documentation:
These variables will survive between plays during an Ansible run, but will not be saved across executions even if you use a fact cache.
This is where the difference between facts (variables bound to Ansible target hosts) and regular variables can be seen.
Variables are internally stored in vars structure, so you can access them with:
tasks:
- debug: var=vars['var_one']
- debug: var=vars['var_two']
Facts, on the other hand are stored in hostvars.
In either case, unless you were referring to a variable name with a dynamic name, or a fact bound to another host than the one executing the task, you can simply use the variable/fact name by using its name:
tasks:
- debug: var=var_one
- debug: var=var_two
Try using
msg=
instead of var=. As per help of debug module
var - A variable name to debug. Mutually exclusive with the 'msg' option.
- name: Set hostvars
hosts: localhost
vars:
var_one: I am a
var_two: est
tasks:
- debug: msg=hostvars['localhost']['var_one']
- debug: msg=hostvars['localhost']['var_two']
...
PLAY [Set hostvars] ************************************************************
TASK [setup] *******************************************************************
ok: [localhost]
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "hostvars['localhost']['var_one']"
}
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "hostvars['localhost']['var_two']"
}
PLAY RECAP *********************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0

ansible: accessing register variables from other plays within same playbook

I'm trying to access the variable called "count" from the first "play" in my playbook in the second playbook. I found some other posts here about the same issue and I thought I was following the right steps, but the code below is still failing.
The Code
- hosts: group1
tasks:
- name: count registrations on primary node
shell: psql -U widgets widgets -c 'SELECT COUNT(*) FROM location' -t
register: count
- debug: var=count.stdout
- hosts: group2
tasks:
#the line below works...
# - debug: msg={{ hostvars['myserver1.mydomain.com']['count']['stdout'] }}
# but this one fails
- debug: msg={{ hostvars['group1']['count']['stdout'] }}
This produces the following output:
PLAY ***************************************************************************
TASK [setup] *******************************************************************
ok: [myserver1.mydomain.com]
TASK [count registrations on node] **************************************
changed: [myserver1.mydomain.com]
TASK [debug] *******************************************************************
ok: [myserver1.mydomain.com] => {
"count.stdout": " 2"
}
PLAY ***************************************************************************
TASK [setup] *******************************************************************
ok: [myserver2.mydomain.com]
TASK [debug] *******************************************************************
fatal: [myserver1.mydomain.com]: FAILED! => {"failed": true, "msg": "'ansible.vars.hostvars.HostVars object' has no attribute 'can_sip1'"}
NO MORE HOSTS LEFT *************************************************************
[ERROR]: Could not create retry file 'playbooks/test.retry'. The error was: [Errno 13] Permission denied: 'playbooks/test.retry'
PLAY RECAP *********************************************************************
myserver1.mydomain.com : ok=3 changed=1 unreachable=0 failed=0
myserver2.mydomain.com : ok=1 changed=0 unreachable=0 failed=1
The other post that I referring to is found here:
How do I set register a variable to persist between plays in ansible?
It's probably something simple, but I can't see where the bug lies.
Thanks.
EDIT 1
I've also tried to use set_fact like this:
- hosts: group1
tasks:
- name: count registrations on primary node
shell: psql -U widget widget -c 'SELECT COUNT(*) FROM location' -t
register: result
- debug: var=result.stdout
- set_fact: the_count=result.stdout
- debug: var={{the_count}}
- hosts: group2
tasks:
- name: retrieve variable from previous play
shell: echo hello
- debug: var={{hostvars}}
The results I get are:
PLAY ***************************************************************************
TASK [setup] *******************************************************************
ok: [myserver1.mydomain.com]
TASK [count reg on primary] ****************************************************
changed: [myserver1.mydomain.com]
TASK [debug] *******************************************************************
ok: [myserver1.mydomain.com] => {
"result.stdout": " 2"
}
TASK [set_fact] ****************************************************************
ok: [myserver1.mydomain.com]
TASK [debug] *******************************************************************
ok: [myserver1.mydomain.com] => {
"result.stdout": " 2"
}
PLAY ***************************************************************************
TASK [setup] *******************************************************************
ok: [myserver2.mydomain.com]
TASK [retrieve variable from previous play] ************************************
changed: [myserver2.mydomain.com]
TASK [debug] *******************************************************************
ok: [myserver2.mydomain.com] => {
"<ansible.vars.hostvars.HostVars object at 0x7f3b6602b290>": "VARIABLE IS NOT DEFINED!"
}
PLAY RECAP *********************************************************************
myserver1.mydomain.com : ok=5 changed=1 unreachable=0 failed=0
myserver2.mydomain.com : ok=3 changed=1 unreachable=0 failed=0
So It looks like there are no objects in the hostvars...
EDIT 3
This is what the playbook looks like this morning.
- hosts: group1
tasks:
- name: count reg on primary
shell: psql -U widgets widgets -c 'SELECT COUNT(*) FROM location' -t
register: result
- debug: var=result.stdout
- set_fact: the_count={{result.stdout}}
- debug: var={{the_count}}
- hosts: group2
tasks:
- name: retrieve variable from previous play
shell: echo hello
- debug: var={{hostvars}}
The "debug: var={{the_count}}" line from the first play prints out the correct value for the count but it also says the VARIABLE IS NOT DEFINED... like so:
TASK [set_fact] ****************************************************************
task path: /etc/ansible/playbooks/test.yml:8
ok: [myserver1.mydomain.com] => {"ansible_facts": {"the_count": " 2"}, "changed": false, "invocation": {"module_args": {"the_count": " 2"}, "module_name": "set_fact"}}
TASK [debug] *******************************************************************
task path: /etc/ansible/playbooks/test.yml:10
ok: [myserver1.mydomain.com] => {
" 2": "VARIABLE IS NOT DEFINED!"
}
And then once I hit the second play, I still get the message
TASK [debug] *******************************************************************
task path: /etc/ansible/playbooks/test.yml:16
ok: [myserver2.mydomain.com] => {
"<ansible.vars.hostvars.HostVars object at 0x7fb077fdc310>": "VARIABLE IS NOT DEFINED!"
}
In your example, you are suggestion that I use "debug: var={{hostlers}}". If you can clarify that for me please. It looks like it's a typo.
EDIT 4:
If you take a look at Edit 3 carefully, you will see that I have implemented "debug:var={{hostvars}}" as you suggest in your answer. But it gives me the same error that the variable is not defined.
I'm not just trying to pass variables from one play to another.. but from one set of hosts to another. Notice how play 1 uses group1 and play two applies only to group2.
Register variables, like facts, are per host. The values can differ depending on the machine. So you can only use host/ip defined in the inventory as key, not the group name. I think you have already knowed this, as you marked this in code snippet 1.
In the code snippet 2, the set_fact line (- set_fact: the_count=result.stdout) actually set the key the_count to the text value result.stdout, since result.stdout is treated as plain text, not a variable. If you want to treat it as a variable, you'd better use {{ result.stdout }}. You can verify this via running the playbook with -v option.
Tasks:
set_fact: the_content1=content.stdout
set_fact: the_content2={{ content.stdout }}
Output:
TASK [set_fact] ****************************************************************
ok: [192.168.1.58] => {"ansible_facts": {"the_content1": "content.stdout"}, "changed": false}
TASK [set_fact] ****************************************************************
ok: [192.168.1.58] => {"ansible_facts": {"the_content2": "hello world"}, "changed": false}
The debug module has two possible parameter: var and msg. The var parameter expect a variable name.
debug: var={{hostvars}}
In this line, first of all, Ansible extracts the value of hostvars, since it is enclosed with two brackets. Secondly, it tries to find a variable whose name is the value of hostvars, since var parameter expects a variable name directly. That is why you see the following strange output. This means Ansible couldn't find a variable whose name is <ansible.vars.hostvars.HostVars object at 0x7f3b6602b290>.
"<ansible.vars.hostvars.HostVars object at 0x7f3b6602b290>": "VARIABLE IS NOT DEFINED!"
You can use the following:
debug: var=hostvars
debug: msg={{hostvars}}
References:
Register variables don't survive across plays with different hosts
set_fact - Set host facts from a task
debug - Print statements during execution

Resources