VMware

VMware vCenter server is a centralized management application that lets you manage virtual machines and ESXi hosts centrally.

IT Services · VMware

Details

IDVMware
ProviderBroadcom
CategoryIT Services
From Version5.0.0
Docker Imagedemisto/vmware:2.0.0.10133006
Supported ModulesAgentix XSIAM

README

VMware vCenter server is a centralized management application that lets you manage virtual machines and ESXi hosts centrally.
This integration was integrated and tested with version 7.03 of VMware

Configure VMware in Cortex

Parameter Description Required
Server URL (i.e., 192.168.0.1:30022) The server URL of the VCenter. True
Credentials Username and password used to login into the system. True
insecure Trust any certificate (not secure). True
proxy Use system proxy settings. False

Use Cases

  • Create and revert to snapshot.
  • Get information regarding virtual machines.
  • Power-on, power-off, suspend, reboot, clone, create, delete, relocate, resigster and unregister virtual machines.

Commands

You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

vmware-get-vms


Returns all virtual machines on a system.

Base Command

vmware-get-vms

Input

Argument Name Description Required
ip List of IPs to filter VMs by. Optional
hostname Hostname to filter VMs by. Optional
name List of VM names to filter VMs by. Optional
uuid List of UUIDs to filter VMs by. Optional

Context Output

Path Type Description
VMWare.Name String VM name.
VMWare.Template bool true if template, else false.
VMWare.Path String Path to VM.
VMWare.Guest String Guest full name.
VMWare.UUID String VM instance UUID.
VMWare.IP String VM IP address.
VMWare.State String VM state (i.e., poweredOn, poweredOff, suspended, HardRebooted).
VMWare.HostName String Host name of VM.
VMWare.MACAddress String MAC address of VM.
VMWare.SnapshotCreateDate String Create date of the last snapshot of the VM.
VMWare.SnapshotUUID String UUID of the last snapshot of the VM.
VMWare.Deleted Boolean If set to true, the VM was deleted.

Command Example

!vmware-get-vms

Context Example

{
 "VMWare":[
   {
       "Guest": "Ubuntu Linux (64-bit)",
       "HostName": "ubuntu",
       "IP": "192.168.100.1",
       "MACAddress": "00:50:56:bc:86:ec",
       "Name": "UbuntuTest",
       "Path": "[datastore1] UbuntuTest/UbuntuTest.vmx",
       "State": "poweredOn",
       "Template": false,
       "UUID": "503ca58b-0821-cf21-fb56-459e55df6d19"
   }
 ]
}

Human Readable Output

Name Template Path Guest UUID IP State HostName MACAddress SnapshotCreateDate SnapshotUUID Deleted
VMware_vCenter_7_Integration_Test false [datastore1] VMware_vCenter_7_Integration_Test/VMware_vCenter_7_Integration_Test.vmx Other 3.x or later Linux (64-bit) 52535506-77f9-ac98-77d3-63bb537ad7d2 192.168.1.141 poweredOn localhost 00:0c:29:d4:ca:14     false
VMware_enhancement_test_vm false [datastore1] enhancement_test_3/enhancement_test_3.vmx Other (32-bit) 503d608e-92a1-358a-9674-2806326527f5   poweredOn         false
CentOS7_VMware_Integration_Test false [datastore1] CentOS7_VMware_Integration_Test/CentOS7_VMware_Integration_Test.vmx CentOS 7 (64-bit) 503dd190-3c64-493b-38ff-37db4e4ba2ab 192.168.1.142 poweredOn localhost.localdomain 00:50:56:bd:25:c4 2021-09-22 12:09:57.989388+00:00 423da02f-c7f4-3cda-a33e-1c85c10e048d false
TetsVM1 false [datastore1] TetsVM1/TetsVM1.vmx Other (32-bit) 503dae8b-afc2-d253-40d5-7e25e05d86f4   poweredOn         false
enhancement test 2 false [datastore1] enhancement test 2/enhancement test 2.vmx Other (32-bit) 503d4b98-8a62-c31e-239a-fc9480f55d30   poweredOff         false

vmware-poweron


Powers on a powered-off or suspended virtual machine.

Base Command

vmware-poweron

Input

Argument Name Description Required
vm-uuid VM UUID of virtual machine to be powered on. Required

Context Output

Path Type Description
VMWare.UUID String VM instance UUID.
VMWare.State String VM state (i.e., poweredOn, poweredOff, suspended, HardRebooted).

Command Example

!vmware-poweron vm-uuid="503ca58b-0821-cf21-fb56-459e55df6d19"

Context Example

{
 "VMWare": {
     "State": "poweredOn",
     "UUID": "503ca58b-0821-cf21-fb56-459e55df6d19"
   }
}

Human Readable Output

Virtual Machine was powered on successfully.

vmware-poweroff


Powers off a powered-on or suspended virtual machine.

Base Command

vmware-poweroff

Input

Argument Name Description Required
vm-uuid VM UUID of virtual machine to be powered on. Required

Context Output

Path Type Description
VMWare.UUID String VM instance UUID.
VMWare.State String VM state (i.e., poweredOn, poweredOff, suspended, HardRebooted).

Command Example

!vmware-poweroff vm-uuid="503ca58b-0821-cf21-fb56-459e55df6d19"

Context Example

{
 "VMWare": {
     "State": "poweredOff",
     "UUID": "503ca58b-0821-cf21-fb56-459e55df6d19"
   }
}

Human Readable Output

Virtual Machine was powered off successfully.

vmware-hard-reboot


Reboots a powered-on virtual machine.

Base Command

vmware-hard-reboot

Input

Argument Name Description Required
vm-uuid VM UUID of virtual machine to reboot. Required

Context Output

Path Type Description
VMWare.UUID String VM instance UUID.
VMWare.State String VM state (i.e., poweredOn, poweredOff, suspended, HardRebooted).

Command Example

!vmware-hard-reboot vm-uuid="503ca58b-0821-cf21-fb56-459e55df6d19"

Context Example

{
 "VMWare": {
     "State": "HardRebooted",
     "UUID": "503ca58b-0821-cf21-fb56-459e55df6d19"
   }
}

Human Readable Output

Virtual Machine was rebooted successfully.

vmware-suspend


Suspends a powered-on virtual machine.

Base Command

vmware-suspend

Input

Argument Name Description Required
vm-uuid VM UUID of virtual machine to be suspended. Required

Context Output

Path Type Description
VMWare.UUID String VM instance UUID.
VMWare.State String VM state (i.e., poweredOn, poweredOff, suspended, HardRebooted).

Command Example

!vmware-suspend vm-uuid="503ca58b-0821-cf21-fb56-459e55df6d19"

Context Example

{
 "VMWare": {
     "State": "suspended",
     "UUID": "503ca58b-0821-cf21-fb56-459e55df6d19"
   }
}

Human Readable Output

Virtual Machine was suspended successfully.

vmware-soft-reboot


Issues a command to the guest operating system asking it to perform a reboot.

Base Command

vmware-soft-reboot

Input

Argument Name Description Required
vm-uuid VM UUID of virtual machine to reboot. Required

Context Output

There is no context output for this command.

Command Example

!vmware-soft-reboot vm-uuid="503ca58b-0821-cf21-fb56-459e55df6d19"

Human Readable Output

A request to reboot the guest has been sent.

vmware-create-snapshot


Creates a VM snapshot.

Base Command

vmware-create-snapshot

Input

Argument Name Description Required
vm-uuid VM UUID of virtual machine to take snapshot of. Required
name Snapshot name. Optional
description Snapshot description. Optional
memory Snapshot the virtual machine’s memory. Possible values are: true, false. Default is True. Optional
quiesce Quiesce guest file system (needs VMWare Tools installed). Possible values are: true, false. Default is False. Optional

Context Output

There is no context output for this command.

Command Example

!vmware-create-snapshot vm-uuid="503ca58b-0821-cf21-fb56-459e55df6d19" name="SnapShotName" description="A daily snapshot of VM" memory=true quiesce=false

Human Readable Output

Snapshot SnapShotName completed.

vmware-revert-snapshot


Reverts VM to snapshot.

Base Command

vmware-revert-snapshot

Input

Argument Name Description Required
snapshot-name Snapshot name to revert to. Required
vm-uuid VM UUID of virtual machine to revert snapshot. Required

Context Output

Path Type Description
VMWare.UUID String VM instance UUID.
VMWare.Snapshot String Name of the snapshot reverted to.

Command Example

!vmware-revert-snapshot vm-uuid="503ca58b-0821-cf21-fb56-459e55df6d19" snapshot-name="SnapShotName"

Context Example

{
 "VMWare": {
     "Snapshot": "Reverted to SnapShotName",
     "UUID": "503ca58b-0821-cf21-fb56-459e55df6d19"
   }
}

Human Readable Output

Reverted to snapshot SnapShotName successfully.

vmware-get-events


Gets events of a VM.

Base Command

vmware-get-events

Input

Argument Name Description Required
vm-uuid VM UUID of virtual machine to get events of. Required
event-type Type of events to get, given in CSV (i.e., VmGuestRebootEvent,VmGuestShutdownEvent). Default is VmGuestRebootEvent,VmGuestShutdownEvent,VmPoweredOnEvent,VmPoweredOffEvent,VmSuspendedEvent. Optional
start-date Event’s start date. Optional
end-date Event’s end date. Optional
user User name. Optional
limit Number of events to return. Default is 50. Optional

Context Output

Path Type Description
VMWareEvents.id String The id of the event.
VMWareEvents.Event String Description of the event.
VMWareEvents.CreatedTime String Creation time of the event.
VMWareEvents.UserName Date The user name of the user who triggered the event.

Command Example

!vmware-get-events vm-uuid=525306-77f9-ac98-77d3-63bb537ad7d2 start-date="2021-12-14T00:00:00" end-date="2021-12-16T00:00:00" user="VSPHERE.TEST"

Context Example

{
  "VMWareEvenet": [
        {
            "CreatedTime": "2021-12-15 09:38:46", 
            "UserName": "VSPHERE.TEST", 
            "Event": "Guest OS reboot for Datacenter", 
            "id": 99973
        }
    ]
}

Human Readable Output

CreatedTime Event UserName id
2021-12-15 09:38:46 Guest OS reboot for Datacenter VSPHERE.TEST 999

vmware-change-nic-state


Changes the state of a VM NIC.

Base Command

vmware-change-nic-state

Input

Argument Name Description Required
vm-uuid VM UUID of virtual machine to change NIC state. Required
nic-state New state of the NIC to be changed. Possible values are: connect, disconnect, delete. Required
nic-number Number of the NIC to be changed. Required

Context Output

Path Type Description
VMWare.UUID String VM instance UUID.
VMWare.NICState String VM NIC state (i.e., connected, disconnected, delete).

Command Example

!vmware-change-nic-state vm-uuid="503ca58b-0821-cf21-fb56-459e55df6d19" nic-state="disconnected" nic-number=1

Context Example

{
 "VMWare": {
     "NICState": "disconnected",
     "UUID": "503ca58b-0821-cf21-fb56-459e55df6d19"
   }
}

Human Readable Output

Virtual Machine’s NIC was disconnected successfully.

vmware-list-vms-by-tag


Lists all virtual storage objects attached to the tag.

Base Command

vmware-list-vms-by-tag

Input

Argument Name Description Required
category The category to which the tag belongs. Required
tag The tag to be queried. Required

Context Output

Path Type Description
VMWareTag.TagName String The tag that was queried.
VMwareTag.Category String The category to which the tag belongs.
VMwareTag.VM String VM name which the tag attached to.

Command Example

!vmware-list-vms-by-tag category="Test Category" tag=test

Context Example

{
  "VMWareTag": [
        {
            "Category": "Test Category", 
            "VM": "CentOS7_VMware_Integration_Test", 
            "TagName": "test"
        }, 
        {
            "Category": "Test Category", 
            "VM": "enhancement test 2", 
            "TagName": "test"
        }, 
        {
            "Category": "Test Category", 
            "VM": "VMware_enhancement_test_vm", 
            "TagName": "test"
        }
    ]}

Human Readable Output

Category TagName VM
Test Category test Integration_Test
Test Category test enhancement test 2
Test Category test VMware_enhancement_test_vm

vmware-create-vm


Creates a new virtual machine in the current folder and attaches it to the specified resource pool. This operation creates a virtual machine, instead of cloning a virtual machine from an existing one.

Base Command

vmware-create-vm

Input

Argument Name Description Required
name Display name of the virtual machine. Required
cpu-num Number of virtual processors in a virtual machine. Required
cpu-allocation Resource limits for CPU. Required
memory Resource limits for memory. Required
virtual-memory Size of a virtual machine’s memory, in MB. Required
guestld Short guest operating system identifier. Optional
guest-os-familiy description. Required
guest-os-version description. Required
host The target host on which the virtual machine will run. Required
folder The target folder in which the virtual machine will be located. Required
pool The resource pool to which the virtual machine will be attached. Optional

Context Output

Path Type Description
VMWare.vName String VM name.
VMWare.Template bool true if template, else false.
VMWare.Path String Path to VM.
VMWare.Guest String Guest full name.
VMWare.UUID String VM instance UUID.
VMWare.IP String VM IP address.
VMWare.State String VM state (i.e., poweredOn, poweredOff, suspended, HardRebooted).
VMWare.HostName String Host name of VM.
VMWare.MACAddress String MAC address of VM.
VMWare.SnapshotCreateDate String Create date of the last snapshot of the VM.
VMWare.SnapshotUUID String UUID of the last snapshot of the VM.
VMWare.Deleted Boolean If set to true, the VM was deleted.

Command Example

!vmware-create-vm cpu-allocation=1 cpu-num=1 guest-os-familiy=Windows guest-os-version="Microsoft Windows Server 2019 (64-bit)" host=11.11.1.111 memory=32 name="test_vm_ui" virtual-memory=32 folder="vm"

Context Example

{
  "VMWare": [
        {
           "MACAddress": "", 
            "Name": "test_vm_ui", 
            "Deleted": false, 
            "IP": " ", 
            "HostName": " ", 
            "SnapshotCreateDate": "", 
            "UUID": "503d-11bc-834e-51a8-51b19a1b6924", 
            "State": "poweredOff", 
            "Snapshot": " ", 
            "Template": false, 
            "SnapshotUUID": "", 
            "Path": "[datastore1] test_vm_ui/test_vm_ui.vmx", 
            "Guest": "Other (32-bit)"
        }
    ]
}

Human Readable Output

Name Template Path Guest UUID State Deleted
test_vm_ui false [datastore1] test_vm_ui/test_vm_ui.vmx Other (32-bit) 503d9bc9-5c5a-603e-65d9-0fea7b29b9a7 poweredOff false

vmware-clone-vm


Creates a clone of this virtual machine. If the virtual machine is used as a template, this method corresponds to the deploy command.

Base Command

vmware-clone-vm

Input

Argument Name Description Required
uuid The UUID of the VM to clone. Required
name The name of the new virtual machine. Required
folder The location of the new virtual machine. Required
template Specifies whether or not the new virtual machine should be marked as a template. Optional
powerOn Specifies whether or not the new VirtualMachine should be powered on after creation. Possible values are: true, false. Default is False. Required
datastore The datastore where the virtual machine should be located. Optional
host The target host for the virtual machine. Optional
pool The resource pool to which this virtual machine should be attached. Optional

Context Output

Path Type Description
VMWare.Name String VM name.
VMWare.Template bool true if template, else false.
VMWare.Path String Path to VM.
VMWare.Guest String Guest full name.
VMWare.UUID String VM instance UUID.
VMWare.IP String VM IP address.
VMWare.State String VM state (i.e., poweredOn, poweredOff, suspended, HardRebooted).
VMWare.HostName String Host name of VM.
VMWare.MACAddress String MAC address of VM.
VMWare.SnapshotCreateDate String Create date of the last snapshot of the VM.
VMWare.SnapshotUUID String UUID of the last snapshot of the VM.
VMWare.Deleted Boolean If set to true, the VM was deleted.

Command Example

!vmware-clone-vm folder=vm name=cloned_vm powerOn=false uuid=603e-65d9-0fea7b29b9a7

Context Example

{
  "VMWare": [
        {
            "MACAddress": "", 
            "Name": "cloned_vm", 
            "Deleted": false, 
            "IP": " ", 
            "HostName": " ", 
            "SnapshotCreateDate": "", 
            "UUID": "e5aa-8fe8-a3faf1191395", 
            "State": "poweredOff", 
            "Template": false, 
            "SnapshotUUID": "", 
            "Path": "[datastore1] cloned_vm/cloned_vm.vmx", 
            "Guest": "Other (32-bit)"
        }
    ]
}

Human Readable Output

Name Template Path Guest UUID State Deleted
cloned_vm false [datastore1] cloned_vm/cloned_vm.vmx Other (32-bit) e5aa-8fe8-a3faf1191395 poweredOff false

vmware-relocate-vm


Relocates a virtual machine to a specified location.

Base Command

vmware-relocate-vm

Input

Argument Name Description Required
datastore The datastore where the virtual machine should be located. If not specified, the current datastore is used. Optional
folder The folder where the virtual machine should be located. Required
host The target host for the virtual machine. Optional
pool The resource pool to which this virtual machine should be attached. Optional
service The service endpoint of vCenter where the virtual machine should be located. If not specified, the current vCenter service is used. Optional
profile Storage profile requirement for Virtual Machine’s home directory. Optional
priority The task priority. Possible values are: defaultPriority, highPriority, lowPriority. Required
uuid The UUID of the VM to relocate. Required

Context Output

There is no context output for this command.

Command Example

!vmware-relocate-vm folder=enhancement_check priority=defaultPriority uuid=e5aa-8fe8-a3faf1191395

Human Readable Output

Virtual Machine was relocated successfully.

vmware-delete-vm


Destroys this object, deleting its contents and removing it from its parent folder (if any).

Base Command

vmware-delete-vm

Input

Argument Name Description Required
uuid The UUID of the VM to delete. Required

Context Output

Path Type Description
VMWare.UUID String VM instance UUID.
VMWare.Deleted String If set to true, the VM was deleted.

Command Example

!vmware-delete-vm uuid=503d537f-a8d0-e5aa-8fe8-a3faf1191395

Human Readable Output

Virtual Machine was deleted successfully.

vmware-register-vm


Adds an existing virtual machine to the folder.

Base Command

vmware-register-vm

Input

Argument Name Description Required
path A datastore path to the virtual machine. Required
name The name to be assigned to the virtual machine. Required
host The target host on which the virtual machine will run. Required
asTemplate Flag to specify whether or not the virtual machine should be marked as a template. Possible values are: true, false. Default is False. Optional
folder Folder to register the VM to. Required

Context Output

There is no context output for this command.

Command Example

!vmware-register-vm path="[datastore1] test_vm_ui/test_vm_ui.vmx" folder=vm host=192.168.1.140 name=rgistered_vm asTemplate=false

Human Readable Output

Virtual Machine was registered successfully.

vmware-unregister-vm


emoves this virtual machine from the inventory without removing any of the virtual machine’s files on disk.

Base Command

vmware-unregister-vm

Input

Argument Name Description Required
uuid The UUID of the VM to remove. Required

Context Output

There is no context output for this command.

Command Example

!vmware-unregister-vm uuid=503d9bc9-5c5a-603e-65d9-0fea7b29b9a7

Human Readable Output

Virtual Machine was unregistered successfully.

Configuration parameters

  • url — Server URL (i.e., 192.168.0.1:30022) (required)
  • credentials — Credentials (required)
  • redirect_std_out — Redirect stdout
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (17)

  • vmware-change-nic-state

    Changes the state of a VM NIC.

  • vmware-clone-vm

    Creates a clone of this virtual machine. If the virtual machine is used as a template, this method corresponds to the deploy command.

  • vmware-create-snapshot

    Creates VM snapshot.

  • vmware-create-vm

    Creates a new virtual machine in the current folder and attaches it to the specified resource pool. This operation creates a virtual machine, rather than cloning an existing one.

  • vmware-delete-vm

    Destroys this object, deleting its contents and removing it from its parent folder (if any).

  • vmware-get-events

    Gets events of VM.

  • vmware-get-vms

    Returns all virtual machines on a system.

  • vmware-hard-reboot

    Reboots a powered-on virtual machine.

  • vmware-list-vms-by-tag

    Lists all virtual storage objects attached to the tag.

  • vmware-poweroff

    Powers off a powered-on or suspended virtual machine.

  • vmware-poweron

    Powers on a powered-off or suspended virtual machine.

  • vmware-register-vm

    Adds an existing virtual machine to the folder.

  • vmware-relocate-vm

    Relocates a virtual machine to a specified location.

  • vmware-revert-snapshot

    Reverts VM to snapshot.

  • vmware-soft-reboot

    Issues a command to the guest operating system asking it to perform a reboot.

  • vmware-suspend

    Suspends a powered-on virtual machine.

  • vmware-unregister-vm

    Removes this virtual machine from the inventory without removing any of the virtual machine's files on disk.

# pylint: disable=no-member
# pylint: disable=no-name-in-module
import ssl
from io import StringIO

import dateparser  # type: ignore
import demistomock as demisto  # noqa: F401
import pyVim.task
from CommonServerPython import *  # noqa: F401
from pyVim.connect import Disconnect, SmartConnect
from pyVmomi import vim, vmodl  # type: ignore

REDIRECT_STD_OUT = argToBoolean(demisto.params().get("redirect_std_out", "false"))
real_demisto_info = demisto.info
real_demisto_debug = demisto.debug


def use_demisto_debug(msg):  # pragma: no cover
    if REDIRECT_STD_OUT:
        temp = sys.stdout
        sys.stdout = sys.__stdout__
        real_demisto_debug(msg)
        sys.stdout = temp
    else:
        real_demisto_debug(msg)


def use_demisto_info(msg):  # pragma: no cover
    if REDIRECT_STD_OUT:
        temp = sys.stdout
        sys.stdout = sys.__stdout__
        real_demisto_info(msg)
        sys.stdout = temp
    else:
        real_demisto_info(msg)


demisto.info = use_demisto_info  # type: ignore
demisto.debug = use_demisto_debug  # type: ignore


def parse_params(params):
    full_url = params["url"]
    url_arr = full_url.rsplit(":", 1)
    url = url_arr[0]
    port = str(url_arr[1])
    user_name = params.get("credentials").get("identifier")
    password = params.get("credentials").get("password")
    return full_url, url, port, user_name, password


class Client(BaseClient):
    def __init__(self, full_url: str, user_name: str, password: str, verify: bool = False, proxy: bool = False):
        self.user_name = user_name
        self.password = password
        super().__init__(base_url=f"https://{full_url}", verify=verify, proxy=proxy)
        demisto.debug("Getting session ID using username and password.")
        self.session_id = self._get_session_id()
        self._headers = {"vmware-api-session-id": self.session_id, "Content-Type": "application/json"}
        demisto.debug(f"Created new client instance using {full_url=}, {verify=}, {proxy=}.")

    def _get_session_id(self) -> str:
        """Authenticates and returns a session ID."""
        # https://developer.broadcom.com/xapis/vsphere-automation-api/latest/api/session/post/
        auth = (self.user_name, self.password)
        session_id_response = self._http_request(method="POST", url_suffix="/api/session", auth=auth, resp_type="text")
        demisto.debug(f"Got {session_id_response=}.")
        session_id = session_id_response.replace('"', "")
        if not session_id:
            raise DemistoException("Failed to obtain session ID. Check credentials and VCENTER_HOST.")
        return session_id

    def logout(self):
        """Closes the session and returns HTTP 204 with empty response object."""
        # https://developer.broadcom.com/xapis/vsphere-automation-api/latest/api/session/delete/
        self._http_request(method="DELETE", url_suffix="/api/session", resp_type="response")

    def get_category_id(self, category_name: str) -> str:
        """Get the ID of the category by its name."""
        # https://developer.broadcom.com/xapis/vsphere-automation-api/latest/api/cis/tagging/category/get/
        category_list_response = self._http_request(method="GET", url_suffix="/api/cis/tagging/category")
        # The response is a list of category IDs, not objects with names. We must iterate and get details for each.
        demisto.debug(f"Got {category_list_response=}.")
        for category_id in category_list_response:
            # https://developer.broadcom.com/xapis/vsphere-automation-api/latest/api/cis/tagging/category/categoryId/get/
            category_details_response = self._http_request(method="GET", url_suffix=f"/api/cis/tagging/category/{category_id}")
            demisto.debug(f"For {category_id=}, got {category_details_response=}.")
            if category_details_response.get("name") == category_name:
                return category_id
        raise DemistoException(f"Category '{category_name}' not found.")

    def get_tag_id(self, tag_name: str, category_id: str) -> str:
        """Get the ID of the tag by its name, filtered by category ID."""
        # https://developer.broadcom.com/xapis/vsphere-automation-api/latest/api/cis/tagging/tag__action=list-tags-for-category/post/
        tag_list_response = self._http_request(
            method="POST",
            url_suffix="/api/cis/tagging/tag?action=list-tags-for-category",
            json_data={"category_id": category_id},
        )
        # The response is a list of tag IDs, not objects with names. We must iterate and get details for each.
        demisto.debug(f"Got {tag_list_response=}.")
        for tag_id in tag_list_response:
            # https://developer.broadcom.com/xapis/vsphere-automation-api/latest/api/cis/tagging/tag/tagId/get/
            tag_details_response = self._http_request(method="GET", url_suffix=f"/api/cis/tagging/tag/{tag_id}")
            demisto.debug(f"For {tag_id=}, got {tag_details_response=}.")
            if tag_details_response.get("name") == tag_name:
                return tag_id
        raise DemistoException(f"Tag '{tag_name}' not found in the specified category.")

    def list_associated_objects(self, tag_id: str) -> list:
        """Get all objects associated with the Tag ID."""
        # https://developer.broadcom.com/xapis/vsphere-automation-api/9.0/api/cis/tagging/tag-association/tagId__action=list-attached-objects/post/
        tag_associated_objects_response = self._http_request(
            method="POST",
            url_suffix=f"/api/cis/tagging/tag-association/{tag_id}?action=list-attached-objects",
        )
        demisto.debug(f"Got {tag_associated_objects_response=}.")
        return tag_associated_objects_response

    def list_vms(self, vm_ids: list) -> list:
        """List VMs filtered by VM IDs."""
        # https://developer.broadcom.com/xapis/vsphere-automation-api/v7.0u3/vcenter/api/vcenter/vm/get/index
        filtered_vms_response = self._http_request(method="GET", url_suffix="/api/vcenter/vm", params={"vms": vm_ids})
        demisto.debug(f"Got {filtered_vms_response=}.")
        return filtered_vms_response


def get_limit(args):
    """
    Args:
        args: Args input for command, this function uses limit, page and page_size

    Returns:
        - limit - how many items to request from AWS - IAM API.
        - is_manual - whether manual pagination is active (using page and page_size)
        - page_size - used when manual pagination is active, to bring the relevant number of results from the data.

    """
    limit = arg_to_number(str(args.get("limit"))) if "limit" in args else None
    page = arg_to_number(str(args.get("page"))) if "page" in args else None
    page_size = arg_to_number(str(args.get("page_size"))) if "page_size" in args else None

    if limit is None:
        if page is not None and page_size is not None:
            if page <= 0:
                raise Exception("Chosen page number must be greater than 0")
            limit = page_size * page
            return limit, True, page_size
        else:
            limit = 50
    return limit, False, page_size


def login(url: str, port: int, user_name: str, password: str):  # pragma: no cover
    # Preparations for SDKs connections
    s = ssl.SSLContext(ssl.PROTOCOL_TLS)
    s.verify_mode = ssl.CERT_NONE

    # Connect to a vCenter Server using username and password
    try:
        si = SmartConnect(host=url, user=user_name, pwd=password, port=port)
    except Exception:
        si = SmartConnect(host=url, user=user_name, pwd=password, port=port, sslContext=s)

    return si


def logout(si):  # pragma: no cover
    Disconnect(si)


def get_vm(si, uuid):
    vm = si.content.searchIndex.FindByUuid(None, uuid, True, True)  # type: ignore
    if vm is None:
        raise Exception("Unable to locate Virtual Machine.")
    return vm


def search_for_obj(content, vim_type, name, folder=None, recurse=True):
    if folder is None:
        folder = content.rootFolder
    if not name:
        return None
    obj = None
    container = content.viewManager.CreateContainerView(folder, vim_type, recurse)

    for managed_object_ref in container.view:
        if managed_object_ref.name == name:
            obj = managed_object_ref
            break
    container.Destroy()
    if not obj:
        raise RuntimeError("Managed Object " + name + " not found.")
    return obj


def create_vm_config_creator(host, args):
    spec = vim.vm.ConfigSpec()  # type: ignore
    files = vim.vm.FileInfo()  # type: ignore
    files.vmPathName = "[" + host.datastore[0].name + "]" + args.get("name")
    resource_allocation_spec = vim.ResourceAllocationInfo()  # type: ignore
    resource_allocation_info = vim.ResourceAllocationInfo()  # type: ignore
    resource_allocation_spec.limit = arg_to_number(args.get("cpu-allocation"))
    resource_allocation_info.limit = arg_to_number(args.get("memory"))
    spec.name = args.get("name")
    spec.numCPUs = arg_to_number(args.get("cpu-num"))  # type: ignore[assignment]
    spec.cpuAllocation = resource_allocation_spec
    spec.memoryAllocation = resource_allocation_info
    spec.memoryMB = arg_to_number(args.get("virtual-memory"))
    spec.files = files
    if args.get("guest_id"):
        spec.guestId = args.get("guest_id")
    return spec


def create_rellocation_locator_spec(vm, datastore):
    template_disks = []
    disk_locators = []
    # collect template disks
    for device in vm.config.hardware.device:
        if type(device).__name__ == "vim.vm.device.VirtualDisk" and hasattr(device.backing, "fileName"):
            template_disks.append(device)

    # construct locator for the disks
    for disk in template_disks:
        locator = vim.vm.RelocateSpec.DiskLocator()  # type: ignore
        locator.diskBackingInfo = disk.backing  # Backing information for the virtual disk at the destination
        locator.diskId = int(disk.key)
        locator.datastore = datastore  # Destination datastore
        disk_locators.append(locator)

    return disk_locators


def apply_get_vms_filters(args, vm_summery):
    ips = argToList(args.get("ip"))
    names = argToList(args.get("vm_name"))
    uuids = argToList(args.get("uuid"))

    ip = not args.get("ip") or (vm_summery.guest.ipAddress and vm_summery.guest.ipAddress in ips)
    hostname = not args.get("hostname") or (vm_summery.guest.hostName and vm_summery.guest.hostName == args.get("hostname"))
    name = not args.get("vm_name") or vm_summery.config.name in names
    uuid = not args.get("uuid") or vm_summery.config.instanceUuid in uuids

    return ip and hostname and name and uuid


def get_priority(priority):
    if priority == "highPriority":
        return vim.VirtualMachine.MovePriority().highPriority  # type: ignore[call-arg] # pylint: disable=no-value-for-parameter
    elif priority == "lowPriority":
        return vim.VirtualMachine.MovePriority().lowPriority  # type: ignore[call-arg] # pylint: disable=no-value-for-parameter
    else:
        return vim.VirtualMachine.MovePriority().defaultPriority  # type: ignore # pylint: disable=no-value-for-parameter


def get_vms(si, args):
    data = []
    content = si.RetrieveContent()  # type: ignore
    container = content.rootFolder
    view_type = [vim.VirtualMachine]
    recursive = True
    container_view = content.viewManager.CreateContainerView(container, view_type, recursive)
    children = container_view.view
    limit, is_manual, page_size = get_limit(args)

    for child in children:
        summary = child.summary
        snapshot_create_date = datetime_to_string(child.snapshot.currentSnapshot.config.createDate) if child.snapshot else None
        snapshot_uuid = child.snapshot.currentSnapshot.config.uuid if child.snapshot else None
        if apply_get_vms_filters(args, summary):
            mac_address: str | None = ""
            try:
                for dev in child.config.hardware.device:
                    if isinstance(dev, vim.vm.device.VirtualEthernetCard):
                        mac_address = dev.macAddress
                        break
            except Exception:  # noqa
                pass

            data.append(
                {
                    "Name": summary.config.name,
                    "Template": summary.config.template,
                    "Path": summary.config.vmPathName,
                    "Guest": summary.config.guestFullName,
                    "UUID": summary.config.instanceUuid,
                    "IP": summary.guest.ipAddress if summary.guest.ipAddress else None,
                    "State": summary.runtime.powerState,
                    "HostName": summary.guest.hostName if summary.guest.hostName else None,
                    "MACAddress": mac_address,
                    "SnapshotCreateDate": snapshot_create_date,
                    "SnapshotUUID": snapshot_uuid,
                    "Deleted": False,
                }
            )
            if len(data) == limit:
                break

    # Return the correct amount of data
    if is_manual and page_size and len(data) > page_size:
        data = data[-1 * page_size :]

    ec = {"VMWare(val.UUID && val.UUID === obj.UUID)": data}
    return create_entry(data, ec)


def create_entry(data, ec):
    return {
        "ContentsFormat": formats["json"],
        "Type": entryTypes["note"],
        "Contents": createContext(data, removeNull=True),
        "ReadableContentsFormat": formats["markdown"],
        "HumanReadable": tableToMarkdown(
            "Virtual Machines",
            data,
            headers=[
                "Name",
                "Template",
                "Path",
                "Guest",
                "UUID",
                "IP",
                "State",
                "HostName",
                "MACAddress",
                "SnapshotCreateDate",
                "SnapshotUUID",
                "Deleted",
            ],
            removeNull=True,
        )
        if data
        else "No result were found",
        "EntryContext": ec,
    }


def power_on(si, uuid):
    vm = get_vm(si, uuid)

    if vm.runtime.powerState == "poweredOn":
        raise Exception("Virtual Machine is already powered on.")
    task = vm.PowerOn()
    while task.info.state not in [vim.TaskInfo.State.success, vim.TaskInfo.State.error]:  # type: ignore
        time.sleep(1)
    if task.info.state == "success":
        ec = {"VMWare(val.UUID && val.UUID === obj.UUID)": {"UUID": uuid, "State": "poweredOn"}}
        return {
            "ContentsFormat": formats["json"],
            "Type": entryTypes["note"],
            "Contents": ec,
            "ReadableContentsFormat": formats["text"],
            "HumanReadable": "Virtual Machine was powered on successfully.",
            "EntryContext": ec,
        }
    elif task.info.state == "error":
        raise Exception("Error occurred while trying to power on Virtual Machine.")
    return None


def power_off(si, uuid):
    vm = get_vm(si, uuid)
    if vm.runtime.powerState == "poweredOff":
        raise Exception("Virtual Machine is already powered off.")
    task = vm.PowerOff()
    while task.info.state not in [vim.TaskInfo.State.success, vim.TaskInfo.State.error]:  # type: ignore
        time.sleep(1)
    if task.info.state == "success":
        ec = {"VMWare(val.UUID && val.UUID === obj.UUID)": {"UUID": uuid, "State": "poweredOff"}}
        return {
            "ContentsFormat": formats["json"],
            "Type": entryTypes["note"],
            "Contents": ec,
            "ReadableContentsFormat": formats["text"],
            "HumanReadable": "Virtual Machine was powered off successfully.",
            "EntryContext": ec,
        }
    elif task.info.state == "error":
        raise Exception("Error occured while trying to power off Virtual Machine.")
    return None


def suspend(si, uuid):
    vm = get_vm(si, uuid)
    if vm.runtime.powerState == "suspended":
        raise Exception("Virtual Machine is already suspended.")
    task = vm.Suspend()
    while task.info.state not in [vim.TaskInfo.State.success, vim.TaskInfo.State.error]:  # type: ignore
        time.sleep(1)
    if task.info.state == "success":
        ec = {"VMWare(val.UUID && val.UUID === obj.UUID)": {"UUID": uuid, "State": "suspended"}}
        return {
            "ContentsFormat": formats["json"],
            "Type": entryTypes["note"],
            "Contents": ec,
            "ReadableContentsFormat": formats["text"],
            "HumanReadable": "Virtual Machine was suspended successfully.",
            "EntryContext": ec,
        }
    elif task.info.state == "error":
        raise Exception("Error occured while trying to power on Virtual Machine.")
    return None


def hard_reboot(si, uuid):
    vm = get_vm(si, uuid)
    task = vm.ResetVM_Task()
    wait_for_tasks(si, [task])
    if task.info.state == "success":
        ec = {"VMWare(val.UUID && val.UUID === obj.UUID)": {"UUID": uuid, "State": "HardRebooted"}}
        return {
            "ContentsFormat": formats["json"],
            "Type": entryTypes["note"],
            "Contents": ec,
            "ReadableContentsFormat": formats["text"],
            "HumanReadable": "Virtual Machine was rebooted successfully.",
            "EntryContext": ec,
        }
    elif task.info.state == "error":
        raise Exception("Error occured while trying to reboot Virtual Machine.")
    return None


def wait_for_tasks(si, tasks):
    propertyCollector = si.content.propertyCollector
    taskList = [str(task) for task in tasks]
    objSpecs = [vmodl.query.PropertyCollector.ObjectSpec(obj=task) for task in tasks]  # type: ignore
    propertySpec = vmodl.query.PropertyCollector.PropertySpec(type=vim.Task, pathSet=[], all=True)  # type: ignore
    filterSpec = vmodl.query.PropertyCollector.FilterSpec()
    filterSpec.objectSet = objSpecs
    filterSpec.propSet = [propertySpec]
    pcfilter = propertyCollector.CreateFilter(filterSpec, True)
    try:
        version, state = None, None
        while len(taskList):
            update = propertyCollector.WaitForUpdates(version)
            for filter_set in update.filterSet:
                for obj_set in filter_set.objectSet:
                    task = obj_set.obj
                    for change in obj_set.changeSet:
                        if change.name == "info":
                            state = change.val.state
                        elif change.name == "info.state":
                            state = change.val
                        else:
                            continue
                        if str(task) not in taskList:
                            continue
                        if state == vim.TaskInfo.State.success:  # type: ignore
                            taskList.remove(str(task))
                        elif state == vim.TaskInfo.State.error:  # type: ignore
                            raise task.info.error
            version = update.version
    finally:
        if pcfilter:
            pcfilter.Destroy()


def soft_reboot(si, uuid):
    vm = get_vm(si, uuid)
    vm.RebootGuest()
    return "A request to reboot the guest has been sent."


def create_snapshot(si, args):
    uuid = args["vm-uuid"]
    vm = get_vm(si, uuid)
    d = str(datetime.now())
    if args["memory"] == "True":
        mem = True
    else:
        mem = False
    if args["quiesce"] == "True":
        qui = True
    else:
        qui = False
    name = args.get("name", uuid + " snapshot " + d)
    desc = args.get("description", "Snapshot of VM UUID " + uuid + " taken on " + d)
    pyVim.task.WaitForTask(vm.CreateSnapshot(name=name, description=desc, memory=mem, quiesce=qui))
    return "Snapshot " + name + " completed."


def revert_snapshot(si, name, uuid):
    vm = get_vm(si, uuid)
    snapObj = get_snapshots(vm.snapshot.rootSnapshotList, name)
    if len(snapObj) == 1:
        snapObj = snapObj[0].snapshot
        pyVim.task.WaitForTask(snapObj.RevertToSnapshot_Task())
        ec = {"VMWare(val.UUID && val.UUID === obj.UUID)": {"UUID": uuid, "Snapshot": "Reverted to " + name}}
        return {
            "ContentsFormat": formats["json"],
            "Type": entryTypes["note"],
            "Contents": ec,
            "ReadableContentsFormat": formats["text"],
            "HumanReadable": "Reverted to snapshot " + name + " successfully.",
            "EntryContext": ec,
        }
    else:
        return "No snapshots found with name: " + name + " on VM: " + uuid


def get_snapshots(snapshots, snapname):
    snapObj = []
    for snapshot in snapshots:
        if snapshot.name == snapname:
            snapObj.append(snapshot)
        else:
            snapObj = snapObj + get_snapshots(snapshot.childSnapshotList, snapname)
    return snapObj


def get_events(si, args):
    vm = get_vm(si, args.get("vm-uuid"))
    hr = []
    content = si.RetrieveServiceContent()  # type: ignore
    eventManager = content.eventManager
    limit, is_manual, page_size = get_limit(args)

    time = vim.event.EventFilterSpec.ByTime()  # type: ignore
    time.beginTime = dateparser.parse(args.get("start_date", ""))  # type: ignore
    time.endTime = dateparser.parse(args.get("end_date", ""))  # type: ignore
    if (args.get("start_date") and not time.beginTime) or (args.get("end_date") and not time.endTime):  # type: ignore
        raise Exception("Dates given in a wrong format.")
    by_user_name = vim.event.EventFilterSpec.ByUsername()  # type: ignore
    by_user_name.userList = argToList(args.get("user")) or None  # type: ignore[assignment]
    filter = vim.event.EventFilterSpec.ByEntity(entity=vm, recursion="self")  # type: ignore
    ids = args.get("event-type").split(",")

    filterSpec = vim.event.EventFilterSpec()
    filterSpec.eventTypeId = ids  # type: ignore
    filterSpec.entity = filter  # type: ignore
    filterSpec.time = time
    filterSpec.userName = by_user_name  # type: ignore
    filterSpec.maxCount = limit  # type: ignore

    eventRes = eventManager.QueryEvents(filterSpec)
    for e in eventRes:
        hr.append(
            {
                "id": e.key,
                "Event": e.fullFormattedMessage,
                "CreatedTime": e.createdTime.strftime("%Y-%m-%d %H:%M:%S"),
                "UserName": e.userName,
            }
        )
    # Return the correct amount of data
    if is_manual and page_size and len(hr) > page_size:
        hr = hr[-1 * page_size :]
    ec = {"VMWareEvenet(val.UUID && val.UUID === obj.UUID)": hr}
    return {
        "ContentsFormat": formats["json"],
        "Type": entryTypes["note"],
        "Contents": hr,
        "ReadableContentsFormat": formats["markdown"],
        "HumanReadable": tableToMarkdown("VM " + vm.summary.config.name + " Events", hr, removeNull=True)
        if hr
        else "No result were found",
        "EntryContext": ec,
    }


def change_nic_state(si, args):  # pragma: no cover
    uuid = args["vm-uuid"]
    new_nic_state = args["nic-state"]
    nic_number = args["nic-number"]
    vm = get_vm(si, uuid)
    nic_prefix_header = "Network adapter "
    nic_label = nic_prefix_header + str(nic_number)
    virtual_nic_device = None
    for dev in vm.config.hardware.device:
        if isinstance(dev, vim.vm.device.VirtualEthernetCard) and dev.deviceInfo.label == nic_label:  # type: ignore
            virtual_nic_device = dev
    if not virtual_nic_device:
        raise Exception(f"Virtual {nic_label} could not be found.")

    virtual_nic_spec = vim.vm.device.VirtualDeviceSpec()  # type: ignore
    if new_nic_state == "delete":
        virtual_nic_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.remove  # type: ignore
    else:
        virtual_nic_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit  # type: ignore
    virtual_nic_spec.device = virtual_nic_device
    virtual_nic_spec.device.key = virtual_nic_device.key
    virtual_nic_spec.device.macAddress = virtual_nic_device.macAddress
    virtual_nic_spec.device.backing = virtual_nic_device.backing
    virtual_nic_spec.device.wakeOnLanEnabled = virtual_nic_device.wakeOnLanEnabled
    connectable = vim.vm.device.VirtualDevice.ConnectInfo()  # type: ignore
    if new_nic_state == "connect":
        connectable.connected = True
        connectable.startConnected = True
    elif new_nic_state == "disconnect":
        connectable.connected = False
        connectable.startConnected = False
    else:
        connectable = virtual_nic_device.connectable  # type: ignore[assignment]
    virtual_nic_spec.device.connectable = connectable
    dev_changes = []
    dev_changes.append(virtual_nic_spec)
    spec = vim.vm.ConfigSpec()  # type: ignore
    spec.deviceChange = dev_changes
    task = vm.ReconfigVM_Task(spec=spec)
    wait_for_tasks(si, [task])

    res_new_nic_state = (new_nic_state + "ed").replace("eed", "ed")

    if task.info.state == "success":
        ec = {"VMWare(val.UUID && val.UUID === obj.UUID)": {"UUID": uuid, "NICState": res_new_nic_state}}
        return {
            "ContentsFormat": formats["json"],
            "Type": entryTypes["note"],
            "Contents": ec,
            "ReadableContentsFormat": formats["text"],
            "HumanReadable": f"Virtual Machine's NIC was {res_new_nic_state} successfully.",
            "EntryContext": ec,
        }
    elif task.info.state == "error":
        raise Exception("Error occurred while trying to clone VM.")
    return None


def list_vms_by_tag(client: Client, args: dict) -> CommandResults:
    """Lists VMs by tag using the REST API Client."""
    category_name = args["category"]
    tag_name = args["tag"]

    # STEP 1 - Get category ID by category name
    category_id = client.get_category_id(category_name)
    demisto.debug(f"Got {category_id=} using {category_name=}.")

    # STEP 2 - Get tag ID by tag name and category ID
    tag_id = client.get_tag_id(tag_name, category_id)
    demisto.debug(f"Got {tag_id=} using {tag_name=} and {category_id=}.")

    # STEP 3 - Get objects associated with tag ID
    associated_objects = client.list_associated_objects(tag_id)
    demisto.debug(f"Got {len(associated_objects)} objects associated with {tag_id=}.")

    # STEP 4 - Filter VMs for list of associated objects
    vm_ids = [vm.get("id") for vm in associated_objects if vm.get("type") == "VirtualMachine"]
    demisto.debug(f"Got {len(vm_ids)} VM IDs associated with {tag_id=}.")

    # STEP 5 - Get VM names by VM IDs
    associated_vms = client.list_vms(vm_ids) if vm_ids else []
    demisto.debug(f"Got {len(associated_vms)} VMs associated with {tag_id=}.")

    context_output = [{"VM": vm.get("name"), "TagName": tag_name, "Category": category_name} for vm in associated_vms]

    return CommandResults(
        readable_output=tableToMarkdown(f"VMs with category: {category_name!r} and tag: {tag_name!r}", associated_vms),
        outputs=context_output,
        outputs_prefix="VMwareTag",
        outputs_key_field=["VM", "TagName", "Category"],
    )


def create_vm(si, args):
    content = si.RetrieveContent()
    folder = search_for_obj(content, [vim.Folder], args.get("folder"))
    host = search_for_obj(content, [vim.HostSystem], args.get("host"))
    if not host:
        raise Exception("The host provided is not valid.")
    pool = host.parent.resourcePool
    spec = create_vm_config_creator(host, args)

    task = folder.CreateVM_Task(config=spec, pool=pool, host=host)
    wait_for_tasks(si, [task])

    if task.info.state == "success":
        mac_address: str | None = ""
        summary = task.info.result.summary

        try:
            for dev in task.info.result.config.hardware.device:
                if isinstance(dev, vim.vm.device.VirtualEthernetCard):
                    mac_address = dev.macAddress
                    break
        except Exception:  # noqa
            pass

        data = {
            "Name": summary.config.name,
            "Template": summary.config.template,
            "Path": summary.config.vmPathName,
            "Guest": summary.config.guestFullName,
            "UUID": summary.config.instanceUuid,
            "IP": summary.guest.ipAddress if summary.guest.ipAddress else None,
            "State": summary.runtime.powerState,
            "HostName": summary.guest.hostName if summary.guest.hostName else None,
            "MACAddress": mac_address,
            "Snapshot": task.info.result.snapshot.currentSnapshot if task.info.result.snapshot else None,
            "SnapshotCreateDate": "",
            "SnapshotUUID": "",
            "Deleted": False,
        }
        data = createContext(data, removeNull=True)
        ec = {"VMWare(val.UUID && val.UUID === obj.UUID)": data}
        return {
            "ContentsFormat": formats["json"],
            "Type": entryTypes["note"],
            "Contents": data,
            "ReadableContentsFormat": formats["markdown"],
            "HumanReadable": tableToMarkdown(
                "Virtual Machine",
                data,
                headers=[
                    "Name",
                    "Template",
                    "Path",
                    "Guest",
                    "UUID",
                    "IP",
                    "State",
                    "HostName",
                    "MACAddress",
                    "SnapshotCreateDate",
                    "SnapshotUUID",
                    "Deleted",
                ],
                removeNull=True,
            ),
            "EntryContext": ec,
        }
    elif task.info.state == "error":
        raise Exception("Error occurred while trying to create a VM.")
    return None


def clone_vm(si, args):
    vm = get_vm(si, args.get("uuid"))
    content = si.RetrieveContent()
    spec = vim.vm.CloneSpec()  # type: ignore
    relocate_spec = vim.vm.RelocateSpec()  # type: ignore
    relocate_spec.datastore = search_for_obj(content, [vim.Datastore], args.get("datastore"))
    relocate_spec.host = search_for_obj(content, [vim.HostSystem], args.get("host"))
    relocate_spec.pool = search_for_obj(content, [vim.ResourcePool], args.get("pool"))  # type: ignore
    spec.location = relocate_spec
    spec.template = argToBoolean(args.get("template", False))
    spec.powerOn = argToBoolean(args.get("powerOn"))

    folder = search_for_obj(content, [vim.Folder], args.get("folder"))
    task = vm.CloneVM_Task(folder=folder, name=args.get("name"), spec=spec)
    wait_for_tasks(si, [task])

    if task.info.state == "success":
        mac_address: str | None = ""
        summary = task.info.result.summary
        try:
            for dev in task.info.result.config.hardware.device:
                if isinstance(dev, vim.vm.device.VirtualEthernetCard):
                    mac_address = dev.macAddress
                    break
        except Exception:  # noqa
            pass

        data = {
            "Name": summary.config.name,
            "Template": summary.config.template,
            "Path": summary.config.vmPathName,
            "Guest": summary.config.guestFullName,
            "UUID": summary.config.instanceUuid,
            "IP": summary.guest.ipAddress if summary.guest.ipAddress else None,
            "State": summary.runtime.powerState,
            "HostName": summary.guest.hostName if summary.guest.hostName else None,
            "MACAddress": mac_address,
            "SnapshotCreateDate": "",
            "SnapshotUUID": "",
            "Deleted": False,
        }
        data = createContext(data, removeNull=True)
        ec = {"VMWare(val.UUID && val.UUID === obj.UUID)": data}
        return {
            "ContentsFormat": formats["json"],
            "Type": entryTypes["note"],
            "Contents": data,
            "ReadableContentsFormat": formats["markdown"],
            "HumanReadable": tableToMarkdown(
                "Virtual Machine",
                data,
                headers=[
                    "Name",
                    "Template",
                    "Path",
                    "Guest",
                    "UUID",
                    "IP",
                    "State",
                    "HostName",
                    "MACAddress",
                    "SnapshotCreateDate",
                    "SnapshotUUID",
                    "Deleted",
                ],
                removeNull=True,
            ),
            "EntryContext": ec,
        }
    elif task.info.state == "error":
        raise Exception("Error occurred while trying to clone VM.")
    return None


def relocate_vm(si, args):
    content = si.RetrieveContent()
    vm = get_vm(si, args.get("uuid"))

    priority = get_priority(args.get("priority"))
    spec = vim.VirtualMachineRelocateSpec()  # type: ignore
    spec.folder = search_for_obj(content, [vim.Folder], args.get("folder"))
    spec.host = search_for_obj(content, [vim.HostSystem], args.get("host"))
    spec.pool = search_for_obj(content, [vim.ResourcePool], args.get("pool"))  # type: ignore
    datastore = search_for_obj(content, [vim.Datastore], args.get("datastore"))

    if datastore:
        spec.datastore = datastore
        spec.disks = create_rellocation_locator_spec(vm, datastore)
    task = vm.RelocateVM_Task(spec, priority)
    wait_for_tasks(si, [task])

    if task.info.state == "success":
        return {
            "ContentsFormat": formats["json"],
            "Type": entryTypes["note"],
            "Contents": {},
            "ReadableContentsFormat": formats["text"],
            "HumanReadable": "Virtual Machine was relocated successfully.",
            "EntryContext": {},
        }
    elif task.info.state == "error":
        raise Exception("Error occurred while trying to relocate VM.")
    return None


def delete_vm(si, args):
    vm = get_vm(si, args.get("uuid"))
    if vm.runtime.powerState == "poweredOn":
        raise Exception("Virtual Machine should be powered off before deleting.")
    task = vm.Destroy_Task()
    wait_for_tasks(si, [task])
    if task.info.state == "success":
        data = {"UUID": args.get("uuid"), "Deleted": True}
        ec = {"VMWare(val.UUID && val.UUID === obj.UUID)": data}
        return {
            "ContentsFormat": formats["json"],
            "Type": entryTypes["note"],
            "Contents": {},
            "ReadableContentsFormat": formats["text"],
            "HumanReadable": "Virtual Machine was deleted successfully.",
            "EntryContext": ec,
        }
    elif task.info.state == "error":
        raise Exception("Error occurred while trying to delete VM.")
    return None


def register_vm(si, args):
    content = si.RetrieveContent()
    folder = search_for_obj(content, [vim.Folder], args.get("folder"))
    host = search_for_obj(content, [vim.HostSystem], args.get("host"))
    pool = host.parent.resourcePool

    task = folder.RegisterVM_Task(
        path=args.get("path"), name=args.get("name"), asTemplate=argToBoolean(args.get("as_template")), pool=pool, host=host
    )
    wait_for_tasks(si, [task])
    if task.info.state == "success":
        return {
            "ContentsFormat": formats["json"],
            "Type": entryTypes["note"],
            "Contents": {},
            "ReadableContentsFormat": formats["text"],
            "HumanReadable": "Virtual Machine was registered successfully.",
            "EntryContext": {},
        }
    elif task.info.state == "error":
        raise Exception("Error occurred while trying to register VM.")
    return None


def unregister_vm(si, args):
    vm = get_vm(si, args.get("uuid"))
    vm.UnregisterVM()
    return {
        "ContentsFormat": formats["json"],
        "Type": entryTypes["note"],
        "Contents": {},
        "ReadableContentsFormat": formats["text"],
        "HumanReadable": "Virtual Machine was unregistered successfully.",
        "EntryContext": {},
    }


def test_module(si):
    get_vms(si, {"limit": "1"})
    return "ok"


def main():  # pragma: no cover
    params = demisto.params()
    command = demisto.command()
    args = demisto.args()

    full_url, url, port, user_name, password = parse_params(params)
    verify = not params.get("insecure", False)
    proxy = params.get("proxy", False)

    if REDIRECT_STD_OUT:
        sys.stdout = StringIO()
    res = []
    si = None
    client = None
    result: Any = None
    try:
        si = login(url, port, user_name, password)
        if command == "test-module":
            result = test_module(si)
        if command == "vmware-get-vms":
            result = get_vms(si, args)
        if command == "vmware-poweron":
            result = power_on(si, args["vm-uuid"])
        if command == "vmware-poweroff":
            result = power_off(si, args["vm-uuid"])
        if command == "vmware-hard-reboot":
            result = hard_reboot(si, args["vm-uuid"])
        if command == "vmware-suspend":
            result = suspend(si, args["vm-uuid"])
        if command == "vmware-soft-reboot":
            result = soft_reboot(si, args["vm-uuid"])
        if command == "vmware-create-snapshot":
            result = create_snapshot(si, args)
        if command == "vmware-revert-snapshot":
            result = revert_snapshot(si, args["snapshot-name"], args["vm-uuid"])
        if command == "vmware-get-events":
            result = get_events(si, args)
        if command == "vmware-change-nic-state":
            result = change_nic_state(si, args)
        if command == "vmware-list-vms-by-tag":
            client = Client(full_url, user_name, password, verify=verify, proxy=proxy)
            return_results(list_vms_by_tag(client, args))
        if command == "vmware-create-vm":
            result = create_vm(si, args)
        if command == "vmware-clone-vm":
            result = clone_vm(si, args)
        if command == "vmware-relocate-vm":
            result = relocate_vm(si, args)
        if command == "vmware-delete-vm":
            result = delete_vm(si, args)
        if command == "vmware-register-vm":
            result = register_vm(si, args)
        if command == "vmware-unregister-vm":
            result = unregister_vm(si, args)
        if result is not None:
            res.append(result)
    except Exception as ex:
        if hasattr(ex, "msg") and ex.msg:  # type: ignore
            message = ex.msg  # type: ignore
        else:
            message = ex
        res.append({"Type": entryTypes["error"], "ContentsFormat": formats["text"], "Contents": str(message)})  # type: ignore

    try:
        logout(si)
        if client:
            client.logout()
    except Exception as ex:
        res.append(
            {  # type: ignore
                "Type": entryTypes["error"],
                "ContentsFormat": formats["text"],
                "Contents": "Logout failed. " + str(ex),
            }
        )

    sys.stdout = sys.__stdout__
    demisto.results(res)


if __name__ in ("__main__", "__builtin__", "builtins"):
    main()