Output of Ansibble task - ansible

I am using command hcloud to create cloud server in Hetzner. I get an output like this:
changed: [localhost] => (item={'name': 'TEST-VARIABLES', 'server_type': 'cx11', 'os_image': 'ubuntu-20.04', 'server_labels': 'Name=test-server', 'server_location': 'hel1'}) => {
"ansible_loop_var": "item",
"changed": true,
"hcloud_server": {
"backup_window": "None",
"datacenter": "hel1-dc2",
"delete_protection": false,
"id": "19461514",
"image": "ubuntu-20.04",
"ipv4_address": "11.111.111.111",
"ipv6": "1a71:7f9:c011:0b09::/64",
"labels": {
"Name": "test-server"
},
"location": "hel1",
"name": "TEST-VARIABLES",
"placement_group": null,
"rebuild_protection": false,
"rescue_enabled": false,
"server_type": "cx11",
"status": "running"
},
"invocation": {
"module_args": {
"allow_deprecated_image": false,
"api_token": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"backups": null,
"datacenter": null,
"delete_protection": null,
"endpoint": "https://api.SERVER.cloud/v1",
"firewalls": null,
"force": false,
"force_upgrade": false,
"id": null,
"image": "ubuntu-20.04",
"labels": {
"Name": "test-server"
},
"location": "hel1",
"name": "TEST-VARIABLES",
"placement_group": null,
"rebuild_protection": null,
"rescue_mode": null,
"server_type": "cx11",
"ssh_keys": null,
"state": "present",
"upgrade_disk": false,
"user_data": null,
"volumes": null
}
},
"item": {
"name": "TEST-VARIABLES",
"os_image": "ubuntu-20.04",
"server_labels": "Name=test-server",
"server_location": "hel1",
"server_type": "cx11"
},
"root_password": "DFLDJFLDFDLFKJDLFKJ"
}
I try to get line with "ipv4_address": "11.111.111.111", and "root_password": "DFLDJFLDFDLFKJDLFKJ", but when I use task:
---
- name: Create a basic server
hcloud_server:
api_token: "{{ token }}"
name: "{{ item.name }}"
server_type: "{{ item.server_type }}"
image: "{{ item.os_image }}"
labels: "{{ item.server_labels }}"
location: "{{ item.server_location }}"
state: present
register: server_info
with_items: "{{ server }}"
- name: Here IP
debug:
var: server_info.root_password
I got error:
TASK [/etc/ansible/roles/CREATE-server : Here IP.] *********************************************************************************************************
ok: [localhost] => {
"server_info.root_password": "VARIABLE IS NOT DEFINED!"
}
Could you please help, how I can get IP line and password line, to use them in the next task (for example to send via email). Thank you!

you register the content of loop, so your result is a list (results):
- name: display
debug:
msg: "ip: {{ item.hcloud_server.ipv4_address }} has password: {{ item.root_password }}"
loop: "{{ server_info.results }}"
result: here you have just one server declared, so just one item in the list results
"msg": "ip: 11.111.111.111 has password: DFDFDFDFDFDFDFDF"
if you want to limit the output of record, you could add loop_control parameter to loop with argument label:
loop: "{{ server_info.results }}"
loop_control:
label: "{{ item.hcloud_server.ipv4_address }}"
you could put another comment if you want with label or even empty string:
loop: "{{ server_info.results }}"
loop_control:
label: "result"

Related

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 }}"

Correct way to join a new Windows guest to a domain when deployed form a template

I am attempting to automate the deployment of a Windows guest in a VMware environment and I have been quite able to do so as long as I am happy with having to manually add it to the domain, which I am not. I have tried using the vmware_client customization and it does not appear to even be applied.
Ansible 2.9
Red Hat 8
YML
---
- hosts: all
gather_facts: false
vars_files:
- group_vars/all
- build_info/vars
tasks:
- debug:
var: "{{ dusername }}"
- name: Clone a virtual machine from Windows template and customize
vmware_guest:
annotation: "This machine was built from a template through a process triggered by an ansible playbook"
hostname: "{{ hostname }}"
username: "{{ username }}"
password: "{{ password }}"
validate_certs: "{{ validate_certs }}"
datacenter: "{{ datacenter }}"
cluster: "{{ cluster }}"
folder: "{{ folder }}"
name: "{{ bname }}"
template: "{{ template }}"
datastore: "{{ datastore}}"
state: poweredon
networks:
- name: "{{ bnetworks.netname }}"
ip: "{{ bnetworks.ip }}"
netmask: "{{ bnetworks.netmask }}"
gateway: "{{ bnetworks.gateway }}"
domain: "{{ bnetworks.domain }}"
start_connected: yes
dns_servers: "{{bnetworks.dns_servers}}"
dns_suffix: "{{bnetworks.dns_suffix}}"
customization:
hostname: "{{ bname }}"
domainadmin: "{{ dusername }}"
domainadminpassword: "{{ dpassword }}"
joindomain: "{{ bnetworks.domain }}"
fullname: "{{ ladminname }}"
wait_for_ip_address: yes
wait_for_customization: yes
with_dict: "{{ bnetworks }}"
delegate_to: localhost'
The result
{
"changed": true,
"instance": {
"module_hw": true,
"hw_name": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"hw_power_status": "poweredOn",
"hw_guest_full_name": "Microsoft Windows Server 2016 or later (64-bit)",
"hw_guest_id": "windows9Server64Guest",
"hw_product_uuid": "4227f81c-1b25-13fd-45f2-d9399408a5f6",
"hw_processor_count": 2,
"hw_cores_per_socket": 1,
"hw_memtotal_mb": 8192,
"hw_interfaces": [
"eth0"
],
"hw_datastores": [
"na2-tntr02-dat"
],
"hw_files": [
"[na2-tntr02-dat] ********/********.vmx",
"[na2-tntr02-dat] ********/********.nvram",
"[na2-tntr02-dat] ********/********.vmsd",
"[na2-tntr02-dat] ********/********.vmxf",
"[na2-tntr02-dat] ********/********.vmdk"
],
"hw_esxi_host": "na2-devesx01.********",
"hw_guest_ha_state": true,
"hw_is_template": false,
"hw_folder": "/NA2/vm/GLOBAL OPERATIONS/Storage",
"hw_version": "vmx-13",
"instance_uuid": "5027d0ef-7a89-73a6-1da9-6e15df083592",
"guest_tools_status": "guestToolsRunning",
"guest_tools_version": "11269",
"guest_question": null,
"guest_consolidation_needed": false,
"ipv4": "10.6.6.10",
"ipv6": null,
"annotation": "This machine was built from a template through a process triggered by an ansible playbook",
"customvalues": {},
"snapshots": [],
"current_snapshot": null,
"vnc": {},
"moid": "vm-21984",
"vimref": "vim.VirtualMachine:vm-21984",
"hw_cluster": "NA2-NonPROD",
"hw_eth0": {
"addresstype": "assigned",
"label": "Network adapter 1",
"macaddress": "00:50:56:a7:fc:87",
"ipaddresses": [
"fe80::4835:d689:f1a7:6dd4",
"10.6.6.10"
],
"macaddress_dash": "00-50-56-a7-fc-87",
"summary": "DVSwitch: 50 27 7a 36 e1 9e ae 1a-29 5a 4a 79 8d 2b 6f a3",
"portgroup_portkey": "191",
"portgroup_key": "dvportgroup-71"
}
},
"invocation": {
"module_args": {
"annotation": "This machine was built from a template through a process triggered by an ansible playbook",
"hostname": "na2-pdvcva01.********",
"username": "RP4VM#vsphere.local",
"password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"validate_certs": false,
"datacenter": "NA2",
"cluster": "NA2-NonPROD",
"folder": "/GLOBAL OPERATIONS/Storage",
"name": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"template": "NA2_ZWindows2016STD",
"datastore": "na2-tntr02-dat",
"state": "poweredon",
"networks": [
{
"name": "FIRM_NA|PROD_ap|STORAGE_epg",
"ip": "10.6.6.10",
"netmask": "255.255.255.0",
"gateway": "10.6.6.1",
"domain": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"start_connected": true,
"dns_servers": [
"10.6.2.16",
"10.2.2.17"
],
"dns_suffix": [
"VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"asia.global-legal.com",
"emea.global-legal.com"
],
"type": "static"
}
],
"customization": {
"hostname": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"domainadmin": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"domainadminpassword": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"joindomain": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"fullname": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER"
},
"wait_for_ip_address": true,
"wait_for_customization": true,
"port": 443,
"is_template": false,
"customvalues": [],
"name_match": "first",
"use_instance_uuid": false,
"disk": [],
"cdrom": [],
"hardware": {},
"force": false,
"state_change_timeout": 0,
"linked_clone": false,
"vapp_properties": [],
"proxy_host": null,
"proxy_port": null,
"uuid": null,
"guest_id": null,
"esxi_hostname": null,
"snapshot_src": null,
"resource_pool": null,
"customization_spec": null,
"convert": null
}
},
"_ansible_no_log": false,
"item": {
"key": "netname",
"value": "FIRM_NA|PROD_ap|STORAGE_epg"
},
"ansible_loop_var": "item",
"_ansible_item_label": {
"key": "netname",
"value": "FIRM_NA|PROD_ap|STORAGE_epg"
},
"_ansible_delegated_vars": {}
}
However the machine does not join the domain. I fully expect a fail because the domain credentials supplied do not have the authority to add the machine to the domain.

Parse / Loop ansible registered variable

Trying to figure out how to filter out the list of UserNames in the output of the following playbook.
- name: Get all users
ome_user_info:
hostname: "{{ dellome_hostname }}"
username: "{{ dellome_username }}"
password: "{{ dellome_password }}"
register: users
Now the output provides the following:
ok: [192.168.1.100] => {
"users": {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"deprecations": [
{
"msg": "Distribution Ubuntu 18.04 on host 192.168.1.100 should use /usr/bin/python3, but is using /usr/bin/python for backward compatibility with prior Ansible releases. A future Ansible release will default to using the discovered platform python for this host. See https://docs.ansible.com/ansible/2.8/reference_appendices/interpreter_discovery.html for more information",
"version": "2.12"
}
],
"failed": false,
"user_info": {
"192.168.1.100": {
"#odata.context": "/api/$metadata#Collection(AccountService.Account)",
"#odata.count": 3,
"value": [
{
"#odata.id": "/api/AccountService/Accounts('10066')",
"#odata.type": "#AccountService.Account",
"Description": "admin",
"DirectoryServiceId": 0,
"Enabled": true,
"Id": "10066",
"IsBuiltin": true,
"Locked": false,
"Name": "admin",
"Password": null,
"Permissions#odata.navigationLink": "/api/AccountService/Accounts('10066')/Permissions",
"RoleId": "10",
"UserName": "admin",
"UserTypeId": 1
},
{
"#odata.id": "/api/AccountService/Accounts('10102')",
"#odata.type": "#AccountService.Account",
"Description": null,
"DirectoryServiceId": 0,
"Enabled": true,
"Id": "10102",
"IsBuiltin": false,
"Locked": false,
"Name": "dell",
"Password": null,
"Permissions#odata.navigationLink": "/api/AccountService/Accounts('10102')/Permissions",
"RoleId": "10",
"UserName": "dell",
"UserTypeId": 1
},
{
"#odata.id": "/api/AccountService/Accounts('10233')",
"#odata.type": "#AccountService.Account",
"Description": null,
"DirectoryServiceId": 10232,
"Enabled": true,
"Id": "10233",
"IsBuiltin": false,
"Locked": false,
"Name": "Domain Users",
"Password": null,
"Permissions#odata.navigationLink": "/api/AccountService/Accounts('10233')/Permissions",
"RoleId": "10",
"UserName": "Domain Users",
"UserTypeId": 2
}
]
}
}
}
}
I was able to determine the following from the output of the registered variable users.
- debug:
var: "{{ users | length }}"
This provides me the length of 5. Which makes sense to an extent. If I start poking into the output I can then determine the following:
- debug:
var: "{{ users.user_info | length }}"
This shows me the length of 1 which makes sense. If I add the var using users.user_info I can then see the output below.
TASK [manage_users : debug] *************************************************************************************************************************************************************
ok: [192.168.1.100] => {
"users.user_info": {
"192.168.1.100": {
"#odata.context": "/api/$metadata#Collection(AccountService.Account)",
"#odata.count": 3,
"value": [
{
"#odata.id": "/api/AccountService/Accounts('10066')",
"#odata.type": "#AccountService.Account",
"Description": "admin",
"DirectoryServiceId": 0,
"Enabled": true,
"Id": "10066",
"IsBuiltin": true,
"Locked": false,
"Name": "admin",
"Password": null,
"Permissions#odata.navigationLink": "/api/AccountService/Accounts('10066')/Permissions",
"RoleId": "10",
"UserName": "admin",
"UserTypeId": 1
},
{
"#odata.id": "/api/AccountService/Accounts('10102')",
"#odata.type": "#AccountService.Account",
"Description": null,
"DirectoryServiceId": 0,
"Enabled": true,
"Id": "10102",
"IsBuiltin": false,
"Locked": false,
"Name": "dell",
"Password": null,
"Permissions#odata.navigationLink": "/api/AccountService/Accounts('10102')/Permissions",
"RoleId": "10",
"UserName": "dell",
"UserTypeId": 1
},
{
"#odata.id": "/api/AccountService/Accounts('10233')",
"#odata.type": "#AccountService.Account",
"Description": null,
"DirectoryServiceId": 10232,
"Enabled": true,
"Id": "10233",
"IsBuiltin": false,
"Locked": false,
"Name": "Domain Users",
"Password": null,
"Permissions#odata.navigationLink": "/api/AccountService/Accounts('10233')/Permissions",
"RoleId": "10",
"UserName": "Domain Users”,
"UserTypeId": 2
}
]
}
}
}
Trying to figure out how I can loop through and get an array of the following: value —> UserName. Essentially I am going to take the following value and loop through and delete users that don’t equal the following. Admin, dell, domain users.
Now one might say you would easily just say while not = to items - then that list would work - I first need to figure out how to search and get the values out. I have tried the following:
- debug:
var: users.user_info().value()
- debug:
var: users.user_info.find('UserName')
- debug:
msg: UserName
loop: users.user_info."192.168.1.100".value
#- debug:
# var: users.user_info."{{ dellome_hostname }}".UserName
#- debug:
# var: "(claims1 | from_json).value"
# msg: "{{ users.user_info.UserName | list }}"
# (output_text.stdout | from_json).ismaster
#- debug:
# msg: "{{ item }}"
#loop: "{{ users.user_info | from_json | list }}"
At the end of this once I understand how to get the data out i can then create a loop to execute the following:
---
- name: Delete a User in Dell OME
ome_user:
hostname: "{{ dellome_hostname }}"
username: "{{ dellome_username }}"
password: "{{ dellome_password }}"
state: "{{ requestedState }}"
name: "{{ requstedUserName }}"
This is where i can then add the loop to eliminate users that don't meet the list of names i provide. Any help would be greatly appreciated.
Here are some of the errors i have run into.
TASK [manage_users : debug] *************************************************************************************************************************************************************
fatal: [192.168.1.100]: FAILED! => {"msg": "Unexpected templating type error occurred on ({{users.user_info().value()}}): 'dict' object is not callable"}
TASK [manage_users : debug] *************************************************************************************************************************************************************
fatal: [192.168.1.100]: FAILED! => {"msg": "template error while templating string: expected name or number. String: {{users.user_info.\"192.168.1.100\".value()}}"}
Or as i am looking at this - if i can figure out a way to create a loop that looks for the roleID and when it is not equal to 10 then delete the user.
Use json_query. The tasks below
- set_fact:
users_rm: "{{ users.user_info|
json_query('*.value[].UserName') }}"
- debug:
var: users_rm
give
users_rm:
- admin
- dell
- Domain Users
You are running the query at the host 192.168.1.100 and the dictionary users comprises the users from this single host only. If there are more hosts in the dictionary the asterisk '*' in the query above would select them all. It would be better to select users for the particular host the query is running at. For example, the task below gives the same result
- set_fact:
users_rm: "{{ users.user_info[inventory_hostname].value|
map(attribute='UserName')|
list }}"

Is it possible to set/lookup Ansible facts when play returns multiple values

I have the following playbook in AWX that looks up Infoblox hosts based on their Mac Address and then outputs the information in a more user friendly format.
The current playbook works providing that a single host with that Mac address exists but fails if there are multiple.
---
- hosts: localhost
connection: local
vars:
niosip: ""
niosmac: ""
niosdhcp: ""
nioshostname: ""
niossearchcatagory: "{{ 'name' if searchcatagory == 'Hostname' else 'ipv4addr' if searchcatagory == 'IP Address' else 'mac' if searchcatagory == 'Mac Address'}}"
pre_tasks:
- include_vars:
file: creds.yml
tasks:
- name: fetch host record
set_fact:
host: "{{ lookup('nios', 'record:host', filter={niossearchcatagory: searchcriteria, 'view': 'Internal'}, provider=nios_provider) }}"
- name: Set niosip
set_fact:
niosip: "{{ host.ipv4addrs[0].ipv4addr }}"
nioshostname: "{{ host.name }}"
niosdhcp: "{{ host.ipv4addrs[0].configure_for_dhcp }}"
niosmac: "{{ host.ipv4addrs[0].mac }}"
when: host != [] and host.ipv4addrs[0].mac is defined
- name: Set niosip
set_fact:
niosip: "{{ host.ipv4addrs[0].ipv4addr }}"
nioshostname: "{{ host.name }}"
niosdhcp: "{{ host.ipv4addrs[0].configure_for_dhcp }}"
when: host != [] and host.ipv4addrs[0].mac is undefined
- name: Host not found
debug:
msg: 'Cant find related host'
when: host == []
- name: Display Display Registration Info
debug:
msg:
- Hostname = {{ nioshostname }}
- IP = {{ niosip }}
- Mac Address {{ niosmac }}
- Registered for DHCP = {{ niosdhcp }}
when: host != [] and host.ipv4addrs[0].mac is defined
Variables niossearchcatagory and searchcriteria are passed into the playbook via an AWX Survey.
I've searched possible options around using loops or splitting the output down but I'm really at a loss on the best way to process this.
If the output matches this then the playbook works as expected
{
"changed": false,
"ansible_facts": {
"host": [
{
"_ref": "record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LnVrLmFjLmJoYW0udGVzdC5zbmF0LWF3eHRlc3Q1:snat-awxtest5.test.com/Internal",
"ipv4addrs": [
{
"_ref": "record:host_ipv4addr/ZG5zLmhvc3RfYWRkcmVzcyQuX2RlZmF1bHQudWsuYWMuYmhhbS50ZXN0LnNuYXQtYXd4dGVzdDUuMTQ3LjE4OC4zMS40Lg:192.168.31.4/snat-awxtest5.test.com/Internal",
"configure_for_dhcp": false,
"host": "snat-awxtest5.test.com",
"ipv4addr": "192.168.31.4",
"mac": "10:20:30:40:50:60"
}
],
"name": "snat-awxtest5.test.com",
"view": "Internal"
},
]
},
"_ansible_no_log": false
}
And here's an example of the play returning multiple values
{
"changed": false,
"ansible_facts": {
"host": [
{
"_ref": "record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LnVrLmFjLmJoYW0udGVzdC5zbmF0LWF3eHRlc3Q1:snat-awxtest5.test.com/Internal",
"ipv4addrs": [
{
"_ref": "record:host_ipv4addr/ZG5zLmhvc3RfYWRkcmVzcyQuX2RlZmF1bHQudWsuYWMuYmhhbS50ZXN0LnNuYXQtYXd4dGVzdDUuMTQ3LjE4OC4zMS40Lg:192.168.31.4/snat-awxtest5.test.com/Internal",
"configure_for_dhcp": false,
"host": "snat-awxtest5.test.com",
"ipv4addr": "192.168.31.4",
"mac": "10:20:30:40:50:60"
}
],
"name": "snat-awxtest5.test.com",
"view": "Internal"
},
{
"_ref": "record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LnVrLmFjLmJoYW0udGVzdC5zbmF0LW15d2Vi:snat-myweb.test.com/Internal",
"ipv4addrs": [
{
"_ref": "record:host_ipv4addr/ZG5zLmhvc3RfYWRkcmVzcyQuX2RlZmF1bHQudWsuYWMuYmhhbS50ZXN0LnNuYXQtbXl3ZWIuMTQ3LjE4OC4zMS4yLg:192.168.31.2/snat-myweb.test.com/Internal",
"configure_for_dhcp": false,
"host": "snat-myweb.test.com",
"ipv4addr": "192.168.31.2",
"mac": "10:20:30:40:50:60"
}
],
"name": "snat-myweb.test.com",
"view": "Internal"
},
{
"_ref": "record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LnVrLmFjLmJoYW0udGVzdC5zbmF0LXdlYg:snat-web.test.com/Internal",
"ipv4addrs": [
{
"_ref": "record:host_ipv4addr/ZG5zLmhvc3RfYWRkcmVzcyQuX2RlZmF1bHQudWsuYWMuYmhhbS50ZXN0LnNuYXQtd2ViLjE0Ny4xODguMzEuMy4:192.168.31.3/snat-web.test.com/Internal",
"configure_for_dhcp": false,
"host": "snat-web.test.com",
"ipv4addr": "192.168.31.3",
"mac": "10:20:30:40:50:60"
}
],
"name": "snat-web.test.com",
"view": "Internal"
}
]
},
"_ansible_no_log": false
}
And this results in an error as the variables host.name, host.ipv4addrs etc.. don't exist which I presume is becasue there are multiples.
Any help on how to output each registration would be gratefully received.

Error when running this ansible play "ERROR! 'unicode object' hasno attribute 'comment'

Here is my playbook
- name: Add multiple users
user:
name: "{{ item[0].name }}"
comment: "{{ item[0].comment }}"
uid: "{{ item[0].uid }}"
groups: "{{ item[0].groups}}"
shell: /bin/bash
with_nested:
- "{{ name }}"
- "{{ comment }}"
- "{{ uid }}"
- "{{ groups }}"
Here is my vars file
---
name:
- test1
- test2
comment:
- "comment1"
- "comment2"
uid:
- 150
- 151
groups: "sudo, admin"
I'm not sure what is causing this, any ideas? I believe I may need to use with subelement instead of with nested? Am I on the right track there?
UPDATE:
Changed my code but am now experiencing the following. Updated code and error message
- name: Add new group if it doesn't exist already
group:
name: "{{ group }}"
when: group is defined
- name: Add multiple users
user:
name: "{{ item.0 }}"
comment: "{{item.1 }}"
uid: "{{ item.2 }}"
group: "{{ group }}"
groups: "{{ groups }}"
append: yes
with_together:
- "{{ name }}"
- "{{ comment }}"
- "{{ uid }}"
- "{{ group }}"
And variable file:
name:
- test1
- test2
comment:
- "comment1"
- "comment2"
uid:
- 150
- 151
group: sudo
groups:
- admin
- test
However, now I am receiving this error.
failed: [127.0.0.1] => (item=[u'test1', u'comment1', 150, u'sudo']) => {"failed": true, "invocation": {"module_args": {"append": true, "comment": "comment1", "createhome": true, "expires": null, "force": false, "generate_ssh_key": null, "group": "sudo", "groups": "{'ungrouped': ['127.0.0.1'], 'all': ['127.0.0.1']}", "home": null, "login_class": null, "move_home": false, "name": "test1", "non_unique": false, "password": null, "remove": false, "shell": null, "skeleton": null, "ssh_key_bits": "2048", "ssh_key_comment": "ansible-generated on ubuntu-512mb-sfo1-01", "ssh_key_file": null, "ssh_key_passphrase": null, "ssh_key_type": "rsa", "state": "present", "system": false, "uid": "150", "update_password": "always"}, "module_name": "user"}, "item": ["test1", "comment1", 150, "sudo"], "msg": "Group 'all': ['127.0.0.1']} does not exist"}
failed: [127.0.0.1] => (item=[u'test2', u'comment2', 151, None]) => {"failed": true, "invocation": {"module_args": {"append": true, "comment": "comment2", "createhome": true, "expires": null, "force": false, "generate_ssh_key": null, "group": "sudo", "groups": "{'ungrouped': ['127.0.0.1'], 'all': ['127.0.0.1']}", "home": null, "login_class": null, "move_home": false, "name": "test2", "non_unique": false, "password": null, "remove": false, "shell": null, "skeleton": null, "ssh_key_bits": "2048", "ssh_key_comment": "ansible-generated on ubuntu-512mb-sfo1-01", "ssh_key_file": null, "ssh_key_passphrase": null, "ssh_key_type": "rsa", "state": "present", "system": false, "uid": "151", "update_password": "always"}, "module_name": "user"}, "item": ["test2", "comment2", 151, null], "msg": "Group 'all': ['127.0.0.1']} does not exist"}
The problem is conflicting variable names. groups is a reserved variable and holds the groups from the inventory. And all is a automatically generated group which holds all the hosts of your inventory.
From the docs:
Even if you didn’t define them yourself, Ansible provides a few variables for you automatically. The most important of these are hostvars, group_names, and groups. Users should not use these names themselves as they are reserved. environment is also reserved.
and
groups is a list of all the groups (and hosts) in the inventory. This can be used to enumerate all hosts within a group.
Simply rename your variable and it should work. In general it's a good idea to prefix all variables of a role with the role name. This gets more important if you use 3rd party roles, e.g. from Ansible Galaxy, just to avoid conflicts. So instead of groups you could use myrole_groups and can be quite sure there never will be conflicts.

Resources