Ansible Dynamic Variables in Playbook - ansible

I'm provisioning a bunch of systems (VMs) on a physical host. I'm up to the step where the VM's are running. Now I need to ssh into the VM's via their DHCP addresses. I can pull the IP addresses from the server but I need a way to set these to host_vars. Here are my groups:
ok: [kvm01] => {
"msg": {
"all": [
"kvm01",
"dcos-master-1",
"dcos-agent-1",
"dcos-agent-2",
"dcos_bootstrap"
],
"dcos": [
"dcos-master-1",
"dcos-agent-1",
"dcos-agent-2",
"dcos_bootstrap"
],
"dcos-agents": [
"dcos-agent-1",
"dcos-agent-2"
],
"dcos-bootstraps": [
"dcos_bootstrap"
],
"dcos-masters": [
"dcos-master-1"
],
"kvm": [
"kvm01"
],
"ungrouped": []
}
}
Here's my command:
- name: Get the IP of the VM (DHCP)
command: "/getip.sh {{ item }}"
register: "result"
with_items: "{{ groups['dcos'] }}"
- name: List the output of the variables
debug: msg="{{item.stdout}}"
with_items: "{{result.results}}"
When I run the playbook I get results but they are the FULL JSON result of the command rather than the stdout. There's probably a way to pull out the stdout and assign it to a fact but it's a complex hash. Here's the last result:
TASK [vm_install : Get the IP of the VM (DHCP)] ***************************************************************************
changed: [kvm01] => (item=dcos-master-1)
changed: [kvm01] => (item=dcos-agent-1)
changed: [kvm01] => (item=dcos-agent-2)
changed: [kvm01] => (item=dcos_bootstrap)
TASK [vm_install : List the output of the variables] **********************************************************************
......
ok: [kvm01] => (item={'_ansible_parsed': True, 'stderr_lines': [u'] => {
"item": {
"changed": false,
"cmd": [
"/getip.sh",
"dcos_bootstrap"
],
"delta": "0:00:00.056193",
"end": "2017-09-18 15:45:45.409693",
"invocation": {
"module_args": {
"_raw_params": "/getip.sh dcos_bootstrap",
"_uses_shell": false,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"warn": true
}
},
"item": "dcos_bootstrap",
"rc": 0,
"start": "2017-09-18 15:45:45.353500",
"stderr": " ",
"stdout": "192.168.1.130",
"stdout_lines": [
"192.168.1.130"
]
},
"msg": "192.168.1.130"
}
How can I put the output of the command into an array so that I can use it later in my playbook?

So, like I said in my comment, you've already managed to extract the information you want into an array. You can iterate over those items using with_items as in the follow task that will create an ip_address for each host:
- set_fact:
ip_address: "{{ item.stdout }}"
with_items: "{{ results.results }}"
delegate_to: "{{ item.item }}"
delegate_facts: true
Or you can create a single array containing all of the addresses using Jinja filters:
- set_fact:
all_ip_addresses: "{{ results.results|map(attribute='stdout')|list }}"
Or you could create a dictionary with the same information:
- set_fact:
all_ip_addresses: >
{{ all_ip_addresses
|default({})
|combine({ item.item: item.stdout })}}
with_items: "{{ results.results }}"

Related

Ansible include task and loop register different result

I'm trying to use a module as an included task so I can loop several items and register the credentials in a variable.
Files:
main.yml:
---
- hosts: localhost
gather_facts: False
tasks:
- include: tasks/myvault/get-vault.yml
with_items:
- demo
register: output
- debug:
msg: "{{output}}"
get-vault.yml:
- name: Retrieve secret from Vault
community.hashi_vault.vault_kv2_get:
url: https://myvaul
path: "{{ item }}"
auth_method: token
token: '{{ mytoken }}'
namespace: MyNamespace
validate_certs: no
output:
ok: [localhost] => {
"msg": {
"changed": false,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"include": "tasks/myvault/get-vault.yml",
"include_args": {},
"item": "demo"
}
],
"skipped": false
}
}
The output doesn't display the "real" result of the task.
If I remove:
register: output and put it in get-vault.yml, here is the result:
ok: [localhost] => {
"msg": {
"changed": false,
"data": {
"data": {
"password": "Password",
"username": "Username"
}, ...
How can I get this result returned when I register the output from my included task?

ansible task doesn't resolve variable

I'm trying to get VG_Name using below code. I can see variable value using debug:var but it doesn't work inside actual task & print value as "vg": "hostvars[inventory_hostname].ansible_lvm.lvs.varlog.vg"
tasks:
- set_fact:
LV_name: "opt"
- name: Get VG Name
set_fact:
vg_command: "{{ 'hostvars[inventory_hostname].ansible_lvm.lvs.'+ LV_name +'.vg' }}"
- name: Show VG
debug:
var: "{{ vg_command }}"
- name: extend logical volume and file system
community.general.lvol:
vg: "{{ vg_command }}"
lv: "{{ LV_name }}"
size: +100%FREE
resizefs: yes
Output:
TASK [Get VG Name] *********************************************************************************************************************************************************************************
task path: /root/ansible_disk/disk_extend.yml:92
ok: [SERVER-NAME] => {
"ansible_facts": {
"vg_command": "hostvars[inventory_hostname].ansible_lvm.lvs.varlog.vg"
},
"changed": false
}
TASK [Show VG] *************************************************************************************************************************************************************************************
task path: /root/ansible_disk/disk_extend.yml:96
ok: [SERVER-NAME] => {
"hostvars[inventory_hostname].ansible_lvm.lvs.varlog.vg": "vg_00"
}
TASK [extend logical volume and file system] *******************************************************************************************************************************************************
task path: /root/ansible_disk/disk_extend.yml:109
fatal: [SERVER-NAME]: FAILED! => {
"changed": false,
"err": " Volume group name \"hostvars[inventory_hostname].ansible_lvm.lvs.varlog.vg\" has invalid characters.\n Cannot process volume group hostvars[inventory_hostname].ansible_lvm.lvs.varlog.vg\n",
"invocation": {
"module_args": {
"active": true,
"force": false,
"lv": "varlog",
"opts": null,
"pvs": null,
"resizefs": true,
"shrink": true,
"size": "+100%FREE",
"snapshot": null,
"state": "present",
"thinpool": null,
"vg": "hostvars[inventory_hostname].ansible_lvm.lvs.varlog.vg"
}
},
"msg": "Volume group hostvars[inventory_hostname].ansible_lvm.lvs.varlog.vg does not exist.",
"rc": 5
}
Tried all possible ways(lookup, vars etc) that I could think of but no luck, any help would be appreciated!
You are building your string incoorectly, leaving inventory[hostname] inside the single quotes, it will be treated as a literal; so:
vg_command: "{{ 'hostvars[inventory_hostname].ansible_lvm.lvs.'+ LV_name +'.vg' }}"
should instead be:
vg_command: "{{ hostvars[inventory_hostname].ansible_lvm.lvs[LV_name].vg }}"

add_host - add all host

I made a playbook that devid the hosts to windows and linux groups. this is the script (for now its just Linux and print the items that in the group):
---
- name: firstPlaybook
hosts: all
gather_facts: true
tasks:
- name: Linux Group
add_host:
name: "{{ item }}"
group: LinuxGroup
when: hostvars[item].ansible_system == 'Linux'
with_items: "{{ ansible_play_hosts }} "
run_once: yes
- name: dubug
debug:
msg: "{{ item }}"
with_items: LinuxGroup
and when I run the script with -vvv this is what I saw in the tasks itself:
TASK [Linux Group] *************************************************************
task path: /home/opc/playbooks/playbook.yml:7
skipping: [windowsserver] => (item=windowsserver) => {
"ansible_loop_var": "item",
"changed": false,
"item": "windowsserver",
"skip_reason": "Conditional result was False"
}
creating host via 'add_host': hostname=linuxserver
changed: [windowsserver] => (item=linuxserver) => {
"add_host": {
"groups": [
"LinuxGroup"
],
"host_name": "linuxserver",
"host_vars": {
"group": "LinuxGroup"
}
},
"ansible_loop_var": "item",
"changed": true,
"item": "linuxserver"
}
Perform task: TASK: dubug (N)o/(y)es/(c)ontinue: y
Perform task: TASK: dubug (N)o/(y)es/(c)ontinue: *******************************
TASK [dubug] *******************************************************************
task path: /home/opc/playbooks/playbook.yml:15
ok: [windowsserver] => (item=LinuxGroup) => {
"msg": "LinuxGroup"
}
ok: [linuxserver] => (item=LinuxGroup) => {
"msg": "LinuxGroup"
}
why its adding the windows server the the linux group even thought it suppose to be skipped (I changed the ips to windowsserver and linuxserver that I wont publish them)?
All is working as expected. Take a look at
- debug:
msg: "{{ groups.LinuxGroup }}"

Raise an error based on results of other task

I am using the postgresql_db module that is part of Ansible. For example with something like
- name: Database
postgresql_db:
name: "{{ vars[item + '_database_name_version'] }}"
login_host: "{{ vars[item + '_database_host'] }}"
login_password: "{{ vars[item + '_database_admin_password'] }}"
login_user: "{{ vars[item + '_database_admin_username'] }}"
port: "{{ vars[item + '_database_port'] }}"
state: restore
target: "{{ backup_restore[item]['db_tar'] }}"
when: backup_restore[item]['db_tar'] is defined
with_items: '{{ backup_restore }}'
register: db_restore
When I debug output db_restore I see
TASK [backup : db_restore] *****************************************************
ok: [myapp] => {
"db_restore": {
"changed": true,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"changed": true,
"cmd": "cmd: ****",
"failed": false,
"invocation": {
"module_args": {
"ca_cert": null,
"conn_limit": "",
"db": "myapp_0_1_0",
"encoding": "",
"lc_collate": "",
"lc_ctype": "",
"login_host": "1.1.1.2",
"login_password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"login_unix_socket": "",
"login_user": "ansible",
"maintenance_db": "postgres",
"name": "myapp_0_1_0",
"owner": "",
"port": 5432,
"session_role": null,
"ssl_mode": "prefer",
"state": "restore",
"target": "/backup/tmp/myapp-myapp/myapp_daily/databases/PostgreSQL.sql.gz",
"target_opts": "",
"template": ""
}
},
"item": "myapp",
"msg": "",
"rc": 0,
"stderr": "ERROR: relation \"myapp_table\" already exists\n",
"stderr_lines": [
"ERROR: relation \"myapp_table\" already exists"
]
}
]
}
}
Although there is an error during execution of this module as visible in stderr, the Ansible postgresql_db module also returns "failed": false. So it looks like it is ignoring any errors that might occur while running commands to restore the database.
Now I want to add a task to check db_restore for stderr attribute and if present, raise an error so that the user is made aware of the problems.
How can I raise an error? Is there an error module?
Yes, there is a module to raise errors which is called fail (https://docs.ansible.com/ansible/latest/modules/fail_module.html)
# Example playbook using fail and when together
- fail:
msg: The system may not be provisioned according to the CMDB status.
when: cmdb_status != "to-be-staged"
Another possible way is to use failed_when.
- name: Fail task when the command error output prints FAILED
command: /usr/bin/example-command -x -y -z
register: command_result
failed_when: "'FAILED' in command_result.stderr"
I would recommend you to read the ansible page on error handling (https://docs.ansible.com/ansible/latest/user_guide/playbooks_error_handling.html)
I strongly agree with #Stefan Wegener's answer, but want to add another possibility.
I'm curious if your problem only occurs because of the loop you wrapped around your task.
Another possible solution would be to add the any_errors_fatal: true clause on play level like described in the Ansible documentation:
- hosts: somehosts
any_errors_fatal: true
roles:
- myrole
See https://docs.ansible.com/ansible/latest/user_guide/playbooks_error_handling.html#aborting-the-play
for reference.

Ansible shows error: "One or more undefined variables: 'item' is undefined" when using 'with_items'

I am trying to count the instances inside an elb. This is my Ansible playbook:
- name: Get elb facts
local_action:
module: ec2_elb_facts
name: "{{elb}}"
region: "{{ansible_ec2_placement_region}}"
environment: creds
register: elb_facts
- debug:
var: elb_facts
verbosity: 2
- debug:
msg: "Instance: {{ item.instances }}"
with_items: "{{ elb_facts.elbs }}"
and my output (sensitive data removed):
TASK: [debug ] ****************************************************************
ok: [10.0.0.0] => {
"elb_facts": {
"changed": false,
"elbs": [
{
"availability_zones": [
"ap-southeast-2b",
"ap-southeast-2a"
],
"dns_name": "elbname123.ap-southeast-2.elb.amazonaws.com",
"health_check": {
"healthy_threshold": 2,
"interval": 10,
"target": "TCP:0000",
"timeout": 5,
"unhealthy_threshold": 2
},
"instances": [
{
"id": "i-000000000000000",
"state": null
}
],
"name": "accessgateway",
"scheme": "internal",
"security_groups": [
"sg-00000000"
],
"subnet": [
"subnet-0000000",
"subnet-1111111"
],
"vpc_id": "vpc-000000"
}
],
"invocation": {
"module_args": "",
"module_name": "ec2_elb_facts"
}
}
}
TASK: [debug ] ****************************************************************
fatal: [10.0.0.0] => One or more undefined variables: 'item' is undefined
FATAL: all hosts have already failed -- aborting
So what im trying to do is just loop through and print everything inside the elb_facts, instances variable. From what I can tell it's a hash, containing a list of hashes.
I am using http://docs.ansible.com/ansible/playbooks_loops.html#looping-over-subelements as a reference. I cannot for the life of mine figure out why this is not working.
with_items (and the whole family of with_ loops) is a dictionary key defined in a task, not as a parameter to the action.
Fix the indentation:
- debug:
msg: "Instance: {{ item.instances }}"
with_items: "{{ elb_facts.elbs }}"

Resources