Repository: ansible-collections/community.vmware

Issue:
vmware_content_deploy_template doesn't support creating VM from OVF templates
##### SUMMARY
vmware_content_deploy_template doesn't support creating VM from OVF templates

##### ISSUE TYPE
- Feature Idea


##### ADDITIONAL INFORMATION
```yaml
$ ansible --version
ansible 2.9.6.post0
  config file = None
  configured module search path = [u'/home/lgonchar/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /home/lgonchar/ansible/lib/ansible
  executable location = /home/lgonchar/ansible/bin/ansible
  python version = 2.7.5 (default, Aug  7 2019, 00:51:29) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]

$ ansible-playbook ../some_repo/ansible/playbooks/sandbox/create_vm_vcenter.yml

....

id='com.vmware.vdcs.vmtx-main.invalid_item_type',
default_message="The library item 'Packer_centos7' (ID: 4f31f87a-9ebd-4b64-97a3-2ed77ca87fcb) has type 'ovf', but needs to be of type 'vm-template'.",
```




Discussion:

Resolving patch:
diff --git a/plugins/modules/vmware_content_deploy_ovf_template.py b/plugins/modules/vmware_content_deploy_ovf_template.py
new file mode 100644
--- /dev/null
+++ b/plugins/modules/vmware_content_deploy_ovf_template.py
@@ -0,0 +1,235 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2020, Lev Goncharov <lev@goncharov.xyz>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+ANSIBLE_METADATA = {
+    'metadata_version': '1.1',
+    'status': ['preview'],
+    'supported_by': 'community'
+}
+
+DOCUMENTATION = r'''
+---
+module: vmware_content_deploy_ovf_template
+short_description: Deploy Virtual Machine from ovf template stored in content library.
+description:
+- Module to deploy virtual machine from ovf template in content library.
+- All variables and VMware object names are case sensitive.
+author:
+- Lev Goncharv (@ultral)
+notes:
+- Tested on vSphere 6.7
+requirements:
+- python >= 2.6
+- PyVmomi
+- vSphere Automation SDK
+options:
+    ovf_template:
+      description:
+      - The name of OVF template from which VM to be deployed.
+      type: str
+      required: True
+      aliases: ['ovf', 'template_src']
+    name:
+      description:
+      - The name of the VM to be deployed.
+      type: str
+      required: True
+      aliases: ['vm_name']
+    datacenter:
+      description:
+      - Name of the datacenter, where VM to be deployed.
+      type: str
+      required: True
+    datastore:
+      description:
+      - Name of the datastore to store deployed VM and disk.
+      type: str
+      required: True
+    folder:
+      description:
+      - Name of the folder in datacenter in which to place deployed VM.
+      type: str
+      required: True
+    host:
+      description:
+      - Name of the ESX Host in datacenter in which to place deployed VM.
+      type: str
+      required: True
+    resource_pool:
+      description:
+      - Name of the resourcepool in datacenter in which to place deployed VM.
+      type: str
+      required: False
+    cluster:
+      description:
+      - Name of the cluster in datacenter in which to place deployed VM.
+      type: str
+      required: False
+extends_documentation_fragment: vmware_rest_client.documentation
+'''
+
+EXAMPLES = r'''
+- name: Deploy Virtual Machine from OVF template in content library
+  vmware_content_deploy_ovf_template:
+    hostname: '{{ vcenter_hostname }}'
+    username: '{{ vcenter_username }}'
+    password: '{{ vcenter_password }}'
+    ovf_template: rhel_test_template
+    datastore: Shared_NFS_Volume
+    folder: vm
+    datacenter: Sample_DC_1
+    name: Sample_VM
+    resource_pool: test_rp
+    validate_certs: False
+  delegate_to: localhost
+'''
+
+RETURN = r'''
+vm_deploy_info:
+  description: Virtual machine deployment message and vm_id
+  returned: on success
+  type: dict
+  sample: {
+        "msg": "Deployed Virtual Machine 'Sample_VM'.",
+        "vm_id": "vm-1009"
+    }
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.vmware_rest_client import VmwareRestClient
+from ansible.module_utils.vmware import PyVmomi
+
+HAS_VAUTOMATION_PYTHON_SDK = False
+try:
+    from com.vmware.vcenter.ovf_client import LibraryItem
+    HAS_VAUTOMATION_PYTHON_SDK = True
+except ImportError:
+    pass
+
+
+class VmwareContentDeployOvfTemplate(VmwareRestClient):
+    def __init__(self, module):
+        """Constructor."""
+        super(VmwareContentDeployOvfTemplate, self).__init__(module)
+        self.ovf_template_name = self.params.get('ovf_template')
+        self.vm_name = self.params.get('name')
+        self.datacenter = self.params.get('datacenter')
+        self.datastore = self.params.get('datastore')
+        self.folder = self.params.get('folder')
+        self.resourcepool = self.params.get('resource_pool')
+        self.cluster = self.params.get('cluster')
+        self.host = self.params.get('host')
+
+    def deploy_vm_from_ovf_template(self):
+        # Find the datacenter by the given datacenter name
+        self.datacenter_id = self.get_datacenter_by_name(datacenter_name=self.datacenter)
+        if not self.datacenter_id:
+            self.module.fail_json(msg="Failed to find the datacenter %s" % self.datacenter)
+        # Find the datastore by the given datastore name
+        self.datastore_id = self.get_datastore_by_name(self.datacenter, self.datastore)
+        if not self.datastore_id:
+            self.module.fail_json(msg="Failed to find the datastore %s" % self.datastore)
+        # Find the LibraryItem (Template) by the given LibraryItem name
+        self.library_item_id = self.get_library_item_by_name(self.ovf_template_name)
+        if not self.library_item_id:
+            self.module.fail_json(msg="Failed to find the library Item %s" % self.ovf_template_name)
+        # Find the folder by the given folder name
+        self.folder_id = self.get_folder_by_name(self.datacenter, self.folder)
+        if not self.folder_id:
+            self.module.fail_json(msg="Failed to find the folder %s" % self.folder)
+        # Find the Host by given HostName
+        self.host_id = self.get_host_by_name(self.datacenter, self.host)
+        if not self.host_id:
+            self.module.fail_json(msg="Failed to find the Host %s" % self.host)
+        # Find the resourcepool by the given resourcepool name
+        self.resourcepool_id = None
+        if self.resourcepool:
+            self.resourcepool_id = self.get_resource_pool_by_name(self.datacenter, self.resourcepool)
+            if not self.resourcepool_id:
+                self.module.fail_json(msg="Failed to find the resource_pool %s" % self.resourcepool)
+        # Find the Cluster by the given Cluster name
+        self.cluster_id = None
+        if self.cluster:
+            self.cluster_id = self.get_cluster_by_name(self.datacenter, self.cluster)
+            if not self.cluster_id:
+                self.module.fail_json(msg="Failed to find the Cluster %s" % self.cluster)
+
+        deployment_target = LibraryItem.DeploymentTarget(
+            resource_pool_id=self.resourcepool_id,
+            folder_id=self.folder_id
+        )
+
+        self.ovf_summary = self.api_client.vcenter.ovf.LibraryItem.filter(
+            ovf_library_item_id=self.library_item_id,
+            target=deployment_target)
+
+        self.deploy_spec = LibraryItem.ResourcePoolDeploymentSpec(
+            name=self.vm_name,
+            annotation=self.ovf_summary.annotation,
+            accept_all_eula=True,
+            network_mappings=None,
+            storage_mappings=None,
+            storage_provisioning=None,
+            storage_profile_id=None,
+            locale=None,
+            flags=None,
+            additional_parameters=None,
+            default_datastore_id=None)
+
+        result = self.api_client.vcenter.ovf.LibraryItem.deploy(self.library_item_id, deployment_target, self.deploy_spec)
+
+        if result.succeeded:
+            self.module.exit_json(
+                changed=True,
+                vm_deploy_info=dict(
+                    msg="Deployed Virtual Machine '%s'." % self.vm_name,
+                    vm_id=result.resource_id.id,
+                )
+            )
+        self.module.exit_json(changed=False,
+                              vm_deploy_info=dict(msg="Virtual Machine deployment failed", vm_id=''))
+
+
+def main():
+    argument_spec = VmwareRestClient.vmware_client_argument_spec()
+    argument_spec.update(
+        ovf_template=dict(type='str', aliases=['template_src', 'ovf'], required=True),
+        name=dict(type='str', required=True, aliases=['vm_name']),
+        datacenter=dict(type='str', required=True),
+        datastore=dict(type='str', required=True),
+        folder=dict(type='str', required=True),
+        host=dict(type='str', required=True),
+        resource_pool=dict(type='str', required=False),
+        cluster=dict(type='str', required=False),
+    )
+    module = AnsibleModule(argument_spec=argument_spec,
+                           supports_check_mode=True)
+    result = {'failed': False, 'changed': False}
+    pyv = PyVmomi(module=module)
+    vm = pyv.get_vm()
+    if vm:
+        module.exit_json(
+            changed=False,
+            vm_deploy_info=dict(
+                msg="Virtual Machine '%s' already Exists." % module.params['name'],
+                vm_id=vm._moId,
+            )
+        )
+    vmware_contentlib_create = VmwareContentDeployOvfTemplate(module)
+    if module.check_mode:
+        result.update(
+            vm_name=module.params['name'],
+            changed=True,
+            desired_operation='Create VM with PowerOff State',
+        )
+        module.exit_json(**result)
+    vmware_contentlib_create.deploy_vm_from_ovf_template()
+
+
+if __name__ == '__main__':
+    main()


Test patch: