I need to role which adds records to my zones (bind9)
In hostvars I created vars like as below:
zones:
zone.name1:
- [ type: A, name: mike, ip: 192.168.1.10 ]
- [ type: A, name: bob, ip: 192.168.1.11 ]
zone.name2:
- [ type: A, name: alice, ip: 192.168.1.12 ]
- [ type: A, name: joanne, ip: 192.168.1.13 ]
In role/tasks/main.yaml
- lineinfile:
path: "/etc/bind/zones/{{ item }}.zone"
line: '# IN "{{ item.value.type }}" "{{ item.value.name }}" "{{ item.value.i }}"'
with_items: "{{ zones | dict2items }}"
How to get a result which adds a new record to the zone file?
There is couple of things wrong here.
You are using list notation for dict ([ type: A, name: mike, ip: 192.168.1.10 ] should be { type: A, name: mike, ip: 192.168.1.10 })
Your data structure requires two loops which you cannot do directly in the playbook.
You probably also want to have the freedom to remove records when they are not needed which doesn't work just like that when using lineinfile.
The following solution fixes all the above problems:
# main.yaml
---
- hosts: all
gather_facts: no
connection: local
vars:
zones:
zone.name1:
- { type: A, name: mike, ip: 192.168.1.10 }
# Remove this record
- { type: A, name: bob, ip: 192.168.1.11, state: absent }
zone.name2:
- { type: A, name: alice, ip: 192.168.1.12 }
- { type: A, name: joanne, ip: 192.168.1.13 }
tasks:
- include_tasks: lines.yaml
loop: "{{ zones | dict2items }}"
loop_control:
loop_var: records
Another task file which we loop through:
# lines.yaml
---
- lineinfile:
path: /tmp/{{ records.key }}.zone
line: >-
# IN "{{ item.type }}" "{{ item.name }}" "{{ item.ip }}"
regexp: >-
^#\s+IN\s+"{{ item.type }}"\s+"{{ item.name }}"\s+"{{ item.ip }}"$
state: >-
{{ 'present' if 'state' not in item or item.state == 'present' else 'absent' }}
loop: "{{ records.value }}"
Execute it with this command:
ansible-playbook -i localhost, --diff main.yaml
Related
shell> cat myfile.yml
"ABC":
"ABC-C01":
- host: "a1"
prefixlen: "19"
- host: "a2"
prefixlen: "19"
"DEF":
"DEF-C01":
- host: "d1"
prefixlen: "19"
- host: "d2"
prefixlen: "19"
My expected answer below from the intent file :
ABC-C01:
- a1.domain
- a2.domain
DEF-C01:
- d1.domain
- d2.domain
I was able to get the list individually.
- include_vars:
file: myfile.yml
name: myfile
- set_fact:
dc: "{{ myfile.keys()|list }}"
Gives: 'ABC' and 'DEF'
- set_fact:
cl: "{{ cl|default([]) + myfile[item].keys()|list }}"
with_items: "{{ dc}}"
Gives: 'ABC-C01' and 'DEF-C01'
Put the below declarations into the vars
myfile: "{{ lookup('file', 'myfile.yml')|from_yaml }}"
myfile_groups: "{{ dict(myfile.values()|
map('dict2items')|
json_query(_query)) }}"
_query: '[].[key, value[].join(``,[host, `.domain`])]'
gives what you want
myfile_groups:
ABC-C01:
- a1.domain
- a2.domain
DEF-C01:
- d1.domain
- d2.domain
I am trying to create a dictionary with lists of items for a VMWare details collection. I was able to create the list individually. But not sure how to merge this.
- name: Gather DC info
community.vmware.vmware_datacenter_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: false
register: datacenter_infor
- name: Set DC_name variable
set_fact:
# dc_name: "{{ item.name }}"
dc_name: >-
{{ (dc_name | default([]))
+ [item.name]
}}
loop: "{{ datacenter_infor.datacenter_info }}"
- name: Gather cluster info
vmware_cluster_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: false
datacenter: "{{ item }}"
register: cluster_info
loop: "{{ dc_name }}"
- name: Set Host_name variable
set_fact:
host_name_list: >-
{{ (host_name_list | default([]))
+ data
}}
vars:
data: "{{ item.clusters.values() }}"
loop: "{{ cluster_info.results }}"
This will result in below output:
TASK [Set Host_name variable] *********************************************************************************************************************************************************************************************
ok: [localhost] => (item={'changed': False, 'clusters': {'PQR-CLU01': {'hosts': [{'name': 'PQR-cn0001.myhost.com', 'folder': '/PQR/host/PQR-CLU01'}, {'name': 'PQR-cn0002.myhost.com', 'folder': '/PQR/host/PQR-CLU01'}})
ok: [localhost] => (item={'changed': False, 'clusters': {'ABC-CLU01': {'hosts': [{'name': 'ABC-cn0002.myhost.com', 'folder': '/ABC/host/ABC-CLU01'}, {'name': 'ABC-cn0001.myhost.com', 'folder': '/ABC/host/ABC-CLU01'}})
How can I create a dictionary with above items like using ansible:
{'PQR-CLU01': ['PQR-cn0001.myhost.com','PQR-cn0002.myhost.com'],'ABC-CLU01':['ABC-cn0002.myhost.com','ABC-cn0001.myhost.com']
Given the data
cluster_info:
results:
- changed: false
clusters:
PQR-CLU01:
hosts:
- folder: /PQR/host/PQR-CLU01
name: PQR-cn0001.myhost.com
- folder: /PQR/host/PQR-CLU01
name: PQR-cn0002.myhost.com
- changed: false
clusters:
ABC-CLU01:
hosts:
- folder: /ABC/host/ABC-CLU01
name: ABC-cn0002.myhost.com
- folder: /ABC/host/ABC-CLU01
name: ABC-cn0001.myhost.com
Q: "Create dictionary (below)."
cluster_dict:
ABC-CLU01:
- ABC-cn0002.myhost.com
- ABC-cn0001.myhost.com
PQR-CLU01:
- PQR-cn0001.myhost.com
- PQR-cn0002.myhost.com
A: Create the list of clusters
cluster_list: "{{ cluster_info.results|
map(attribute='clusters')|
map('dict2items')|
flatten }}"
gives
cluster_list:
- key: PQR-CLU01
value:
hosts:
- folder: /PQR/host/PQR-CLU01
name: PQR-cn0001.myhost.com
- folder: /PQR/host/PQR-CLU01
name: PQR-cn0002.myhost.com
- key: ABC-CLU01
value:
hosts:
- folder: /ABC/host/ABC-CLU01
name: ABC-cn0002.myhost.com
- folder: /ABC/host/ABC-CLU01
name: ABC-cn0001.myhost.com
Create a list of keys
cluster_keys: "{{ cluster_list|
map(attribute='key')|
list }}"
gives
cluster_keys:
- PQR-CLU01
- ABC-CLU01
Create a list of values
cluster_vals: "{{ cluster_list|
map(attribute='value.hosts')|
map('map', attribute='name')|
list }}"
gives
cluster_vals:
- - PQR-cn0001.myhost.com
- PQR-cn0002.myhost.com
- - ABC-cn0002.myhost.com
- ABC-cn0001.myhost.com
Crate the dictionary
cluster_dict: "{{ dict(cluster_keys|zip(cluster_vals)) }}"
gives
cluster_dict:
ABC-CLU01:
- ABC-cn0002.myhost.com
- ABC-cn0001.myhost.com
PQR-CLU01:
- PQR-cn0001.myhost.com
- PQR-cn0002.myhost.com
Example of a complete playbook
- hosts: localhost
vars:
cluster_info:
results:
- changed: false
clusters:
PQR-CLU01:
hosts:
- folder: /PQR/host/PQR-CLU01
name: PQR-cn0001.myhost.com
- folder: /PQR/host/PQR-CLU01
name: PQR-cn0002.myhost.com
- changed: false
clusters:
ABC-CLU01:
hosts:
- folder: /ABC/host/ABC-CLU01
name: ABC-cn0002.myhost.com
- folder: /ABC/host/ABC-CLU01
name: ABC-cn0001.myhost.com
cluster_list: "{{ cluster_info.results|
map(attribute='clusters')|
map('dict2items')|
flatten }}"
cluster_keys: "{{ cluster_list|
map(attribute='key')|
list }}"
cluster_vals: "{{ cluster_list|
map(attribute='value.hosts')|
map('map', attribute='name')|
list }}"
cluster_dict: "{{ dict(cluster_keys|zip(cluster_vals)) }}"
tasks:
- debug:
var: cluster_dict
I have a text file that needs to be parsed so i can use it via a REST API with ansible.
10.0.0.0/16 Building-A
10.1.0.0/16 Building-A
10.2.0.0/16 Building-B
10.3.0.0/16 Building-B
I need to convert this text to something like this:
{
"parsed":[
{
"Building-A":[
"10.0.0.0/16",
"10.1.0.0/16"
]
},
{
"Building-B":[
"10.2.0.0/16",
"10.3.0.0/16"
]
}
]
}
Currently i run the following playbook to test the textparsing, without success. The list created is not unique.
- name: Test
hosts: localhost
tasks:
- name: Combine
ansible.builtin.set_fact:
parsed: "{{ (parsed | default([])) | union( [{item.split()[1]: item.split()[0] }] ) }}"
loop: "{{ lookup('file','hostgroups.txt').strip().splitlines() }}"
- name: Debug
ansible.builtin.debug:
var: parsed
ok: [localhost] => {
"parsed": [
{
"Building-A": "10.0.0.0/16"
},
{
"Building-A": "10.1.0.0/16"
},
{
"Building-B": "10.2.0.0/16"
},
{
"Building-B": "10.3.0.0/16"
}
]
}
Thanks for the tip with the helper variables and the groupby filter. Here is the final playbook:
- name: Test
hosts: localhost
# strategy: free
tasks:
- name: Get List
ansible.builtin.set_fact:
parsed_list: "{{ parsed_list | default([]) + [_item] }}"
loop: "{{ lookup('file','hostgroups.txt').strip().splitlines() }}"
vars:
_list: "{{ item.split() }}"
_item: "{{ {'Name': _list[1], 'Subnet': _list[0] } }}"
- name: Debug parsed_list
ansible.builtin.debug:
var: parsed_list
- name: Group parsed_list
ansible.builtin.set_fact.set_fact:
parsed_group: "{{ parsed_group | default([]) + [{_key: _value}] }}"
loop: "{{ parsed_list | groupby('Name') }}"
vars:
_key: "{{ item.0 }}"
_value: "{{ item.1 | map(attribute='Subnet') | list }}"
- name: Debug parsed_group
ansible.builtin.debug:
var: parsed_group
Parse the content, e.g.
- set_fact:
parsed_list: "{{ parsed_list|d([]) + [_item] }}"
loop: "{{ lookup('file','hostgroups.txt').splitlines() }}"
vars:
_array: "{{ item.split() }}"
_item: "{{ {'building': _array.1, 'ip': _array.0} }}"
gives
parsed_list:
- building: Building-A
ip: 10.0.0.0/16
- building: Building-A
ip: 10.1.0.0/16
- building: Building-B
ip: 10.2.0.0/16
- building: Building-B
ip: 10.3.0.0/16
Then, use filter groupby and create the list
- set_fact:
parsed: "{{ parsed|d([]) + [{_key: _val}] }}"
loop: "{{ parsed_list|groupby('building') }}"
vars:
_key: "{{ item.0 }}"
_val: "{{ item.1|map(attribute='ip')|list }}"
gives
parsed:
- Building-A:
- 10.0.0.0/16
- 10.1.0.0/16
- Building-B:
- 10.2.0.0/16
- 10.3.0.0/16
In some cases, a dictionary might be a better structure, e.g.
- set_fact:
parsed_dict: "{{ parsed_dict|d({})|combine({_key: _val}) }}"
loop: "{{ parsed_list|groupby('building') }}"
vars:
_key: "{{ item.0 }}"
_val: "{{ item.1|map(attribute='ip')|list }}"
gives
parsed_dict:
Building-A:
- 10.0.0.0/16
- 10.1.0.0/16
Building-B:
- 10.2.0.0/16
- 10.3.0.0/16
In ansible, the vmware_guest_info module will give us a list of the tags on a vm, but does not include any information about those tags:
"tags": [
"10.16.3",
"dicky",
"develop"
],
The vmware_tag_info module gives us a dict withinfo on those tags, including description and Id, but NOT the tags name:
"10.16.3": {
"tag_category_id": "urn:vmomi:InventoryServiceCategory:6eb9d643-8fa3-42a1-8b50-78a1c6e99867:GLOBAL",
"tag_description": "10.16.3",
"tag_id": "urn:vmomi:InventoryServiceTag:ca46ab80-be91-4c3a-8f9f-019d163dd954:GLOBAL",
"tag_used_by": []
},
The vmware_category_info module gives us a list that includes ID and name of a tag.
"tag_category_info": [
{
"category_associable_types": [],
"category_cardinality": "SINGLE",
"category_description": "nodeVersion",
"category_id": "urn:vmomi:InventoryServiceCategory:6eb9d643-8fa3-42a1-8b50-78a1c6e99867:GLOBAL",
"category_name": "nodeVersion",
"category_used_by": []
},
]
So it seems I need to combine the output of three different lists to get the tag value, tag name and tag ID.
I really hope that someone has already done this. If not, can anyone shed some light on how to iterate over the output of vmware_tag_info and vmware_category_info, and find when tag_category_id matches category_id?
How about this?
---
- name: Example playbook
hosts: localhost
gather_facts: no
vars:
vcenter_hostname: change me
vcenter_username: administrator#vsphere.local
vcenter_password: change me
dc1: change me
vm_name: change me
tasks:
- name: Gather tags information
vmware_tag_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: no
register: tags_info_result
- name: Gather tags category information
vmware_category_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: no
register: tags_category_info_result
- name: Set tags_information variable
set_fact:
tags_information: >-
{{ tags_information | default({})
| combine({
item.key: tags_category_info_result.tag_category_info
| selectattr('category_id', '==', item.value.tag_category_id)
| list
| first
| combine(item.value)
| combine({'tag_name': item.key})
})
}}
with_dict: "{{ tags_info_result.tag_facts }}"
- name: Gather VM information
vmware_guest_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: no
datacenter: "{{ dc1 }}"
name: "{{ vm_name }}"
tags: true
register: vm_info_result
- name: Display VM tags information
debug:
msg:
- "tags information about {{ vm_name }}"
- "{{ tags_information[item] }}"
loop: "{{ vm_info_result.instance.tags }}"
when:
- "'tags' in vm_info_result.instance"
I'm currently building Ansible image playbook templates across AWS and VSphere and I'd like be able to define multiple additional disks but only when they're defined via the variables.
Playbook:
---
- hosts: localhost
connection: local
gather_facts: False
tasks:
- name: Launch Windows 2016 VM Instance
vmware_guest:
validate_certs: no
datacenter: "{{ vm_datacenter }}"
folder: "{{ vm_folder }}"
name: "{{ vm_servername }}"
state: poweredon
template: "{{ vm_template }}"
cluster: "{{ vm_cluster }}"
disk:
- size_gb: "{{ vm_disk_size0 | default(80) }}"
type: "{{ vm_disk_type0 | default(thin) }}"
datastore: "{{ vm_disk_datastore0 }}"
- size_gb: "{{ vm_disk_size1 }}"
type: "{{ vm_disk_type1 }}"
datastore: "{{ vm_disk_datastore1 }}"
- size_gb: "{{ vm_disk_size2 }}"
type: "{{ vm_disk_type2 }}"
datastore: "{{ vm_disk_datastore2 }}"
- size_gb: "{{ vm_disk_size3 }}"
type: "{{ vm_disk_type3 }}"
datastore: "{{ vm_disk_datastore3 }}"
hardware:
memory_mb: "{{ vm_memory_mb | default(8192) }}"
num_cpus: "{{ vm_num_cpus | default(4) }}"
networks:
- name: "{{ vm_network }}"
start_connected: yes
vlan: "{{ vm_network }}"
device_type: vmxnet3
type: dhcp
domain: "{{ vm_domain }}"
customization:
hostname: "{{ vm_servername }}"
orgname: Redacted
password: "{{ winlocal_admin_pass }}"
timezone: 255
wait_for_ip_address: yes
register: vm
Variables:
vm_disk_datastore0: C6200_T2_FCP_3Days
vm_disk_size0: 80
vm_disk_type0: thin
vm_disk_datastore1: "C6200_T2_FCP_3Days"
vm_disk_size1: "50"
vm_disk_type1: "thin"
vm_disk_datastore2: "C6200_T2_FCP_3Days"
vm_disk_size2: "20"
vm_disk_type2: "thin"
vm_disk_datastore3: ""
vm_disk_size3: ""
vm_disk_type3: ""
Error:
{
"_ansible_parsed": false,
"exception": "Traceback (most recent call last):\n File \"/var/lib/awx/.ansible/tmp/ansible-tmp-1568948672.23-135785453591577/AnsiballZ_vmware_guest.py\", line 113, in <module>\n _ansiballz_main()\n File \"/var/lib/awx/.ansible/tmp/ansible-tmp-1568948672.23-135785453591577/AnsiballZ_vmware_guest.py\", line 105, in _ansiballz_main\n invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n File \"/var/lib/awx/.ansible/tmp/ansible-tmp-1568948672.23-135785453591577/AnsiballZ_vmware_guest.py\", line 48, in invoke_module\n imp.load_module('__main__', mod, module, MOD_DESC)\n File \"/tmp/ansible_vmware_guest_payload_6vidiw/__main__.py\", line 2396, in <module>\n File \"/tmp/ansible_vmware_guest_payload_6vidiw/__main__.py\", line 2385, in main\n File \"/tmp/ansible_vmware_guest_payload_6vidiw/__main__.py\", line 2008, in deploy_vm\n File \"/tmp/ansible_vmware_guest_payload_6vidiw/__main__.py\", line 1690, in configure_disks\n File \"/tmp/ansible_vmware_guest_payload_6vidiw/__main__.py\", line 1608, in get_configured_disk_size\nValueError: invalid literal for int() with base 10: ''\n",
"_ansible_no_log": false,
"module_stderr": "Traceback (most recent call last):\n File \"/var/lib/awx/.ansible/tmp/ansible-tmp-1568948672.23-135785453591577/AnsiballZ_vmware_guest.py\", line 113, in <module>\n _ansiballz_main()\n File \"/var/lib/awx/.ansible/tmp/ansible-tmp-1568948672.23-135785453591577/AnsiballZ_vmware_guest.py\", line 105, in _ansiballz_main\n invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n File \"/var/lib/awx/.ansible/tmp/ansible-tmp-1568948672.23-135785453591577/AnsiballZ_vmware_guest.py\", line 48, in invoke_module\n imp.load_module('__main__', mod, module, MOD_DESC)\n File \"/tmp/ansible_vmware_guest_payload_6vidiw/__main__.py\", line 2396, in <module>\n File \"/tmp/ansible_vmware_guest_payload_6vidiw/__main__.py\", line 2385, in main\n File \"/tmp/ansible_vmware_guest_payload_6vidiw/__main__.py\", line 2008, in deploy_vm\n File \"/tmp/ansible_vmware_guest_payload_6vidiw/__main__.py\", line 1690, in configure_disks\n File \"/tmp/ansible_vmware_guest_payload_6vidiw/__main__.py\", line 1608, in get_configured_disk_size\nValueError: invalid literal for int() with base 10: ''\n",
"changed": false,
"module_stdout": "",
"rc": 1,
"msg": "MODULE FAILURE\nSee stdout/stderr for the exact error"
}
The idea being, if the variable isn't defined when the job is launched, it will be skipped by the vm_guest module.
Is this the best method? Is anyone able to suggest a successful way forward?
Update: Probably the best method would be to build the instance and then use vm_guest_disk to add additional disks using the method suggested below. This module is available in v2.8.
Our version of Tower is 2.7.9, so I'm going to use a more verbose method of multiple vm_guest calls via a disk_num variable:
Variables
num_disks: 0
vm_cluster: C6200_NPE_PC_ST
vm_datacenter: DC2
vm_disk_datastore0: C6200_T2_FCP_3Days
vm_disk_datastore1: ''
vm_disk_datastore2: ''
vm_disk_datastore3: ''
vm_disk_datastore4: ''
vm_disk_size0: 80
vm_disk_type0: thin
vm_disk_type1: ''
vm_disk_type2: ''
vm_disk_type3: ''
vm_disk_type4: ''
vm_domain: corp.local
vm_folder: /DC2/vm/ap-dev
vm_hostname: xyzvmserver.corp.local
vm_memory_mb: 16000
vm_network: C6200_10.110.64.0_24_VL1750
vm_num_cpus: 4
vm_servername: server05
vm_template: Windows2016_x64_AN_ESX_v1.1
Playbook:
---
- hosts: localhost
connection: local
gather_facts: False
tasks:
- name: Launch Windows 2016 VM Instance - No additional Disk
vmware_guest:
validate_certs: no
datacenter: "{{ vm_datacenter }}"
folder: "{{ vm_folder }}"
name: "{{ vm_servername }}"
state: poweredon
template: "{{ vm_template }}"
cluster: "{{ vm_cluster }}"
disk:
- size_gb: "{{ vm_disk_size0 }}"
type: "{{ vm_disk_type0 }}"
datastore: "{{ vm_disk_datastore0 }}"
hardware:
memory_mb: "{{ vm_memory_mb | default(8192) }}"
num_cpus: "{{ vm_num_cpus }}"
networks:
- name: "{{ vm_network }}"
start_connected: yes
vlan: "{{ vm_network }}"
device_type: vmxnet3
type: dhcp
domain: "{{ vm_domain }}"
customization:
hostname: "{{ vm_servername }}"
orgname: redacted
password: "{{ winlocal_admin_pass }}"
timezone: 255
wait_for_ip_address: yes
register: vm
when: num_disks == 0
- name: Launch Windows 2016 VM Instance 1 Additional Disk
vmware_guest:
validate_certs: no
datacenter: "{{ vm_datacenter }}"
folder: "{{ vm_folder }}"
name: "{{ vm_servername }}"
state: poweredon
template: "{{ vm_template }}"
cluster: "{{ vm_cluster }}"
disk:
- size_gb: "{{ vm_disk_size0 }}"
type: "{{ vm_disk_type0 }}"
datastore: "{{ vm_disk_datastore0 }}"
- size_gb: "{{ vm_disk_size1 }}"
type: "{{ vm_disk_type1 }}"
datastore: "{{ vm_disk_datastore1 }}"
hardware:
memory_mb: "{{ vm_memory_mb | default(8192) }}"
num_cpus: "{{ vm_num_cpus }}"
networks:
- name: "{{ vm_network }}"
start_connected: yes
vlan: "{{ vm_network }}"
device_type: vmxnet3
type: dhcp
domain: "{{ vm_domain }}"
customization:
hostname: "{{ vm_servername }}"
orgname: redacted
password: "{{ winlocal_admin_pass }}"
timezone: 255
wait_for_ip_address: yes
register: vm
when: num_disks == 1
- name: Launch Windows 2016 VM Instance 2 Additional Disks
vmware_guest:
validate_certs: no
datacenter: "{{ vm_datacenter }}"
folder: "{{ vm_folder }}"
name: "{{ vm_servername }}"
state: poweredon
template: "{{ vm_template }}"
cluster: "{{ vm_cluster }}"
disk:
- size_gb: "{{ vm_disk_size0 }}"
type: "{{ vm_disk_type0 }}"
datastore: "{{ vm_disk_datastore0 }}"
- size_gb: "{{ vm_disk_size1 }}"
type: "{{ vm_disk_type1 }}"
datastore: "{{ vm_disk_datastore1 }}"
- size_gb: "{{ vm_disk_size2 }}"
type: "{{ vm_disk_type2 }}"
datastore: "{{ vm_disk_datastore2 }}"
hardware:
memory_mb: "{{ vm_memory_mb | default(8192) }}"
num_cpus: "{{ vm_num_cpus }}"
networks:
- name: "{{ vm_network }}"
start_connected: yes
vlan: "{{ vm_network }}"
device_type: vmxnet3
type: dhcp
domain: "{{ vm_domain }}"
customization:
hostname: "{{ vm_servername }}"
orgname: redacted
password: "{{ winlocal_admin_pass }}"
timezone: 255
wait_for_ip_address: yes
register: vm
when: num_disks == 2
etc
It looks like you should build your vm with 1 disk, that you know for sure will always be there when you run the play, which you could make to be the first variable in this list:
vms:
0:
vm_disk_datastore: "C6200_T2_FCP_3Days"
vm_disk_size: "80"
vm_disk_type: "thin"
1:
vm_disk_datastore: "C6200_T2_FCP_3Days"
vm_disk_size: "50"
vm_disk_type: "thin"
2:
vm_disk_datastore: "C6200_T2_FCP_3Days"
vm_disk_size: "20"
vm_disk_type: "thin"
3:
vm_disk_datastore: ""
vm_disk_size: ""
vm_disk_type: ""
Since I don't know of a way that you can loop through sub module arguments to make them present or not, build your vm with the one disk you know for sure will be there. Which mentioned above, for consistency, should be the first one in your list. Or you will need to rebuild the logic to drill into the list looking for a specific value that you can key in on with jinja filters like selectattr and map.
- name: Launch Windows 2016 VM Instance
vmware_guest:
validate_certs: no
datacenter: "{{ vm_datacenter }}"
folder: "{{ vm_folder }}"
name: "{{ vm_servername }}"
state: poweredon
template: "{{ vm_template }}"
cluster: "{{ vm_cluster }}"
disk:
- size_gb: "{{ vms.0.vm_disk_size }}"
type: "{{ vms.0.vm_disk_type }}"
datastore: "{{ vms.0.vm_disk_datastore }}"
hardware:
memory_mb: "{{ vm_memory_mb | default(8192) }}"
num_cpus: "{{ vm_num_cpus | default(4) }}"
networks:
- name: "{{ vm_network }}"
start_connected: yes
vlan: "{{ vm_network }}"
device_type: vmxnet3
type: dhcp
domain: "{{ vm_domain }}"
customization:
hostname: "{{ vm_servername }}"
orgname: Redacted
password: "{{ winlocal_admin_pass }}"
timezone: 255
wait_for_ip_address: yes
register: vm
Once its built, then use this different module to drill into adding additional disks
- name: Add disks to virtual machine
vmware_guest_disk:
hostname: "{{ vm_servername }}"
datacenter: "{{ vm_datacenter }}"
disk:
- size_gb: "{{ item.vm_disk_size }}"
type: "{{ item.vm_disk_type }}"
datastore: "{{ item.vm_disk_datastore }}"
state: present
loop: "{{ vms }}"
loop_control:
label: "Disk {{ my_idx}} - {{ item.vm_disk_datastore }}"
index_var: my_idx
when:
- item.vm_disk_datastore != ""
- item.vm_disk_size != ""
- item.vm_disk_type != ""
- my_idx > 0
That will feed through a loop pulling that variable in from where ever you decided to define it, and then loop through that list of dictonaries pulling the values with the correct labels, but only doing so if they have a value (if leaving blanks in that list is something you plan on doing, if not, the when isn't even needed). This will also allow you to expand upward by just putting more items in the list without having to expand out your task. If you need to edit the first task because you can't rely on list order, but need to key into a variable, then you need to make sure you make a change to the disks on the second task as well to omit using the disk added in the first task.
Also, the documentation on this module states that existing disks called called with this module will be adjusted to that size, and can't be reduced using the module, so if you want to avoid that, put in some data checks to compare the configured disk size to what is in your variable, so you can skip the task if it is going to be reduced (or if you just don't want an existing vm to be altered).