I have the following tasks to deploy a Windows 2016 VM from template. All my customization is done correctly except the runonce command that is not executed. While running the playbook even in debug mode there is no error or warning.
- name: "[DEEPLOY-INSTANCE] Create a VM Win From a template"
vmware_guest:
hostname: "{{ vmware.host }}"
username: "{{ vmware.username }}"
password: "{{ vmware.password }}"
validate_certs: False
datacenter: "{{ vm_datacenter }}"
cluster: "{{ vm_cluster }}"
name: "{{ vm_name }}"
template: "{{ vm_template_full }}"
folder: "{{ vm_folder }}"
datastore: "{{ vm_datastore }}"
hardware:
num_cpus: "{{ vm_cpu }}"
memory_mb: "{{ vm_ram }}"
networks:
- name: "{{ vm_netname }}"
start_connected: yes
ip: "{{ vm_ip }}"
netmask: "{{ vm_mask }}"
gateway: "{{ vm_gw }}"
type: static
customization:
fullname: "Windows Server"
orgname: "My Company"
timezone: 110
dns_servers:
- 10.100.100.10
- 10.100.110.10
domain: "{{ domain }}"
joindomain: "{{ domain }}"
password: "{{ template_pass }}"
domainadmin: "{{ vmware.username }}"
domainadminpassword: "{{ vmware.password }}"
runonce:
- 'powershell.exe -ExecutionPolicy Unrestricted -File C:\Admin\Scripts\RunOnce.bat'
register: vm_facts
delegate_to: localhost
I tried putting the whole runonce command between single and double quotes but the output is the same. If I check the log C:\WINDOWS\TEMP\vmware-imc\guestcust.log there is no mention to even trying to attempt the execution of the runone but there is information about some customization being performed.
Ansible version is 2.9
EDIT
Changed indexation of runonce to how I have it now although it does not work either, with or without the quotes. As a workaround, I managed to launch that .bat file on a separate task with the module vmware_vm_shell
Can you correct your indentation like below and try it once
runonce:
- powershell.exe -ExecutionPolicy Unrestricted -File C:\Admin\Scripts\RunOnce.bat
Refer this issue:
https://github.com/ansible/ansible/issues/28312
Related
I wrote 2 Ansible playbooks to create and destroy a vm inside an ESXi instance.
The create task is:
- name: Clone the template
delegate_to: localhost
community.vmware.vmware_guest:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
cluster: "{{ vcenter_cluster_name }}"
datacenter: "{{ vcenter_datacenter_name }}"
folder: "{{ vcenter_datacenter_folder }}"
datastore: "{{ vcenter_datastore }}"
validate_certs: False
name: "{{ inventory_hostname }}"
template: "{{ vm_template }}"
state: poweredon
wait_for_ip_address: yes
networks:
- name: "DSwitch_Dati-VM Network 869"
ip: "{{ ansible_host }}"
netmask: "{{ vm_netmask }}"
gateway: "{{ vm_gateway }}"
start_connected: yes
The delete playbook is:
- name: TMS Cleaner
hosts: all
remote_user: tms
tasks:
- name: Set powerstate of virtual machine to poweroff
delegate_to: localhost
community.vmware.vmware_guest:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: False
name: "{{ inventory_hostname }}"
state: poweredoff
- name: Remove virtual machine from inventory
delegate_to: localhost
community.vmware.vmware_guest:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
folder: "{{ vcenter_datacenter_folder }}"
datastore: "{{ vcenter_datastore }}"
validate_certs: False
name: "{{ inventory_hostname }}"
delete_from_inventory: True
state: absent
The creation is correct, while deletion can correctly stop and remove the vm BUT it doeas not remove the folder from the datastore.
What should I do to have a full deletion of all files related to a vm?
If you want to have the files deleted also from datastore you need to remove the following line:
delete_from_inventory: True
The ansible documentation for this module says:
delete_from_inventory:
Choices: Whether to delete Virtual machine from inventory or delete from disk.
no | yes
Only remove that line and files will be deleted from datastore.
I am trying to automate VMware builds using Ansible. I am expecting a workflow engine to output a file that would act as a var_file and have all of the objects that can be used to build the VM using the vmware_guest module. It works great until you get to the networks dictionary portion of the module then it falls apart.
I initially tried setting up a vars_file with all of the variables like this:
---
validate_certs: no
datacenter: this is the DC
cluster: this is the cluster
folder: "this is the folder"
name: some-server
template: template-name
datastore: "datastore-name"
netname: This is the network
ip: 10.6.6.10
netmask: 255.255.255.0
gateway: 10.6.6.1
mac: aa:bb:dd:aa:00:14
domain: domain.com
However, that returned:
argument networks is of type <type 'dict'> and we were unable to convert to list: <type 'dict'> cannot be converted to a list"}
Where the code fails is on this task:
- name: Clone a virtual machine from Windows template and customize
vmware_guest:
hostname: "{{ hostname }}"
username: "{{ username }}"
password: "{{ password }}"
validate_certs: "{{ validate_certs }}"
datacenter: "{{ datacenter }}"
cluster: "{{ cluster }}"
folder: "{{ folder }}"
name: "{{name }}"
template: "{{ template }}"
datastore: "{{ datastore}}"
networks:
name: "{{ netname }}"
ip: "{{ ip }}"
netmask: "{{ netmask }}"
gateway: "{{ gateway }}"
mac: "{{ mac }}"
domain: "{{ domain }}"```
I tried creating a dictionary in the variable file like this:
---
validate_certs: no
datacenter: this is the DC
cluster: this is the cluster
folder: "this is the folder"
name: some-server
template: template-name
datastore: "datastore-name"
bnetworks:
name: This is the network
ip: 10.6.6.10
netmask: 255.255.255.0
gateway: 10.6.6.1
mac: aa:bb:dd:aa:00:14
domain: americas.global-legal.com
And changed the task to include this:
networks:
name: "{{ item.value.name }}"
ip: "{{ item.value.ip }}"
netmask: "{{ item.value.netmask }}"
gateway: "{{ item.value.gateway }}"
mac: "{{ item.value.mac }}"
domain: "{{ item.value.domain }}"
with_dict: bnetworks```
And I get this error:
The task includes an option with an undefined variable. The error was: 'item' is undefined
Any help would be appreciated.
argument networks is of type <type 'dict'> and we were unable to convert to list: cannot be converted to a list"}
There might be more networks in one VM therefor a list is needed. The corect syntax is below
- name: Clone a virtual machine from Windows template and customize
vmware_guest:
hostname: "{{ hostname }}"
username: "{{ username }}"
password: "{{ password }}"
validate_certs: "{{ validate_certs }}"
datacenter: "{{ datacenter }}"
cluster: "{{ cluster }}"
folder: "{{ folder }}"
name: "{{name }}"
template: "{{ template }}"
datastore: "{{ datastore}}"
networks:
- name: "{{ netname }}"
ip: "{{ ip }}"
netmask: "{{ netmask }}"
gateway: "{{ gateway }}"
mac: "{{ mac }}"
domain: "{{ domain }}"```
This is a list
list:
- key: value
This is a dictionary
dictionary:
key: value
This is a list of dictionaries
dictionary:
- key1: value-1-1
key2: value-2-1
- key1: value-2-1
key2: value-2-2
The task includes an option with an undefined variable. The error was: 'item' is undefined
The indentation of with_dict is wrong. The correct syntax is below.
vmware_guest:
...
networks:
- name: "{{ item.value.name }}"
ip: "{{ item.value.ip }}"
netmask: "{{ item.value.netmask }}"
gateway: "{{ item.value.gateway }}"
mac: "{{ item.value.mac }}"
domain: "{{ item.value.domain }}"
with_dict: "{{ bnetworks }}"
I'm wondering if it is possible to perform a loop in the hostvars folder when using Ansible?
Here is what I've tried but haven't had success in making it work - or is it just not possible to do?
---
list_pool: 'list ltm pool {{ items }}'
with_items:
- 'abc123'
- 'def456'
I would use the "list_pool" variable in a playbook afterward:
- name: List pool
bigip_command:
server: "{{ some_server }}"
user: "{{ some_user }}"
password: "{{ some_password }}"
commands:
- "{{ list_pool }}"
validate_certs: no
delegate_to: localhost
Not sure what you mean when you say you want to loop over hostvars folder.
From what I can interpret from your tasks is: "You need to execute big-ip command list ltm <pool-name> for multiple pools in the list list_pool"
If that's what you're after, this should work:
- name: Set list_pool fact
set_fact:
list_pool: "{{ list_pool | default([]) + [item] }}"
with_items:
- 'abc123'
- 'def456'
- name: List pool
bigip_command:
server: "{{ some_server }}"
user: "{{ some_user }}"
password: "{{ some_password }}"
commands:
- "list ltm {{ item }}"
validate_certs: no
delegate_to: localhost
with_items: "{{ list_pool }}"
I got this working with the following solution:
hostvars file would look like this:
---
pre_checks:
checks:
pool:
- name: "pool_123"
- name: "pool_456"
...
And the play would look like this:
--output truncated---
- name: Fetch device host_vars
set_fact:
device_config: "{{ ((lookup('file','{{playbook_dir}}/host_vars/{{inventory_hostname}}.yml')) | from_yaml) }}"
- name: Check pool
bigip_command:
server: "{{ inventory_hostname }}"
user: "{{ remote_username }}"
password: "{{ remote_passwd }}"
commands:
- "list ltm pool {{ item }}"
validate_certs: no
with_items:
- "{{ device_config.pre_checks | json_query('checks.pool[*].name') }}"
delegate_to: localhost
when: "'active' in Active_LTM['stdout'][0]"
register: Pre_Checks
- name: Verify pool
debug: var=item
with_items: "{{ Pre_Checks.results | map(attribute='stdout_lines') | list }}"
I have the following dict:
endpoint:
esxi_hostname: servername.domain.com
I'm trying to use it as an option via jinja2 for the vmware_guest but have been unsuccessful. The reason I'm trying to do it this way is because the dict is dynamic...it can either be cluster: clustername or esxi_hostname: hostname, both mutually exclusive in the vmware_guest module.
Here is how I'm presenting it to the module:
- name: Create VM pysphere
vmware_guest:
hostname: "{{ vcenter_hostname }}"
username: "{{ username }}"
password: "{{ password }}"
validate_certs: no
datacenter: "{{ ansible_host_datacenter }}"
folder: "/DCC/{{ ansible_host_datacenter }}/vm"
"{{ endpoint }}"
name: "{{ guest }}"
state: present
guest_id: "{{ osid }}"
disk: "{{ disks }}"
networks: "{{ niclist }}"
hardware:
memory_mb: "{{ memory_gb|int * 1024 }}"
num_cpus: "{{ num_cpus|int }}"
scsi: "{{ scsi }}"
customvalues: "{{ customvalues }}"
cdrom:
type: client
delegate_to: localhost
And here is the error I'm getting when including the tasks file:
TASK [Preparation : Include VM tasks] *********************************************************************************************************************************************************************************
fatal: [10.10.10.10]: FAILED! => {"reason": "Syntax Error while loading YAML.
The error appears to have been in '/data01/home/hit/tools/ansible/playbooks/roles/Preparation/tasks/prepareVM.yml': line 36, column 4, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
"{{ endpoint }}"
hostname: "{{ vcenter_hostname }}"
^ 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 }}"
exception type: <class 'yaml.parser.ParserError'>
exception: while parsing a block mapping
in "<unicode string>", line 33, column 3
did not find expected key
in "<unicode string>", line 36, column 4"}
So in summary, I'm not sure how to format this or if it is even possible.
The post from techraf sums up your problem, but for a possible solution, in the docs, especially regarding Jinja filters, there is the following bit:
Omitting Parameters
As of Ansible 1.8, it is possible to use the default filter to omit
module parameters using the special omit variable:
- name: touch files with an optional mode
file: dest={{item.path}} state=touch mode={{item.mode|default(omit)}} > with_items:
- path: /tmp/foo
- path: /tmp/bar
- path: /tmp/baz
mode: "0444"
For the first two files in the list, the default mode will be
determined by the umask of the system as the mode= parameter will not
be sent to the file module while the final file will receive the
mode=0444 option.
So it looks like what should be tried is:
esxi_hostname: "{{ endpoint.esxi_hostname | default(omit) }}"
# however you want the alternative cluster settings done.
# I dont know this module.
cluster: "{{ cluster | default(omit) }}"
This is obviously reliant on the vars to only have one choice set.
There is no way you could ever use the syntax you tried in the question, because firstly and foremostly Ansible requires a valid YAML file.
The closest workaround would be to use a YAML anchor/alias although it would work only with literals:
# ...
vars:
endpoint: &endpoint
esxi_hostname: servername.domain.com
tasks:
- name: Create VM pysphere
vmware_guest:
hostname: "{{ vcenter_hostname }}"
username: "{{ username }}"
password: "{{ password }}"
validate_certs: no
datacenter: "{{ ansible_host_datacenter }}"
folder: "/DCC/{{ ansible_host_datacenter }}/vm"
<<: *endpoint
name: "{{ guest }}"
state: present
guest_id: "{{ osid }}"
disk: "{{ disks }}"
networks: "{{ niclist }}"
hardware:
memory_mb: "{{ memory_gb|int * 1024 }}"
num_cpus: "{{ num_cpus|int }}"
scsi: "{{ scsi }}"
customvalues: "{{ customvalues }}"
cdrom:
type: client
delegate_to: localhost
I have 3 task in my ansible yml file as below.
---
- name: Instance provisioning
local_action:
module: ec2
region: "{{ vpc_region }}"
key_name: "{{ ec2_keypair }}"
instance_type: "{{ instance_type }}"
image: "{{ ec2_image}}"
zone: "{{ public_az }}"
volumes:
- device_name: "{{ device }}"
volume_type: "{{ instance_volumetype }}"
volume_size: "{{ volume }}"
delete_on_termination: "{{ state }}"
instance_tags:
Name: "{{ instance_name }}_{{ release_name }}_APACHE"
environment: "{{ env_type }}"
vpc_subnet_id: "{{ public_id }}"
assign_public_ip: "{{ public_ip_assign }}"
group_id: "{{ sg_apache }},{{ sg_internal }}"
wait: "{{ wait_type }}"
register: ec2
- name: adding group to inventory file
lineinfile:
dest: "/etc/ansible/hosts"
regexp: "^\\[{{ release_name }}\\]"
line: "[{{ release_name }}]"
state: present
- name: adding apache ip to hosts
lineinfile:
dest: "/etc/ansible/hosts"
line: "{{ item.private_ip }} name=apache dns={{ item.public_dns_name }}
with_items: ec2.instances
Now i want to check the exit status of each task whether it is success or failure.
If any one of the task fails my other task should not execute.
Please advice how to write an ansible playbook
In your first task, you have register the output to ec2.
now use fail module to stop the play if the task fails.
Ex.
register: ec2
fail:
when: "ec2.rc == 1"
here rc is the return code of the command .. we are assuming 1 for fail and 0 for success.
use fail module after every task.
Let me know if it works for you ..
Register a variable in each task and then check it in the next task. See http://docs.ansible.com/ansible/playbooks_tests.html#task-results
This is already the default behavior in Ansible. If a task fails, the Playbook aborts and reports the failure. You don't need to build in any extra functionality around this.
Maybe playbook blocks and it's error handling is to help you?
Kumar
if You want to check each task output if it is success or failure do this,
---
- name: Instance provisioning
local_action:
module: ec2
region: "{{ vpc_region }}"
key_name: "{{ ec2_keypair }}"
instance_type: "{{ instance_type }}"
image: "{{ ec2_image}}"
zone: "{{ public_az }}"
volumes:
- device_name: "{{ device }}"
volume_type: "{{ instance_volumetype }}"
volume_size: "{{ volume }}"
delete_on_termination: "{{ state }}"
instance_tags:
Name: "{{ instance_name }}_{{ release_name }}_APACHE"
environment: "{{ env_type }}"
vpc_subnet_id: "{{ public_id }}"
assign_public_ip: "{{ public_ip_assign }}"
group_id: "{{ sg_apache }},{{ sg_internal }}"
wait: "{{ wait_type }}"
register: ec2
- name: adding group to inventory file
lineinfile:
dest: "/etc/ansible/hosts"
regexp: "^\\[{{ release_name }}\\]"
line: "[{{ release_name }}]"
state: present
when: ec2 | changed
register: fileoutput
- name: adding apache ip to hosts
lineinfile:
dest: "/etc/ansible/hosts"
line: "{{ item.private_ip }} name=apache dns={{ item.public_dns_name }}
with_items: ec2.instances
when: fileoutput | changed
In your code register a variable in each and every Task if The Task has Changed to True, The Followed Task will execute otherwise it will skip that Task.