diff --git a/lib/ansible/module_utils/vmware_httpapi/VmwareRestModule.py b/lib/ansible/module_utils/vmware_httpapi/VmwareRestModule.py
new file mode 100644
index 00000000000..b4949e9bac6
--- /dev/null
+++ b/lib/ansible/module_utils/vmware_httpapi/VmwareRestModule.py
@@ -0,0 +1,708 @@
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Knight <paul.knight@delaware.gov>
+# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import re
+import sys
+
+from ansible.module_utils.connection import Connection
+from ansible.module_utils.basic import AnsibleModule, env_fallback
+
+# VMware ReST APIs
+#
+# Describes each supported VMware ReST APIs and lists its base URL. All
+# vSphere ReST APIs begin with '/rest'.
+API = dict(
+    appliance=dict(base='/rest/appliance'),
+    cis=dict(base='/rest/com/vmware/cis'),
+    content=dict(base='/rest/com/vmware/content'),
+    vapi=dict(base='/rest'),
+    vcenter=dict(base='/rest/vcenter'),
+    vrops=dict(base='/suiteapi')
+)
+
+# Query Filters
+#
+# This dictionary identifies every valid filter that can be applied to a
+# vSphere ReST API query. Each filter has a name, which may be the same
+# depending on the type object; an id of the value specified; a type,
+# which is typically either a string or a list.  If it is a string, the
+# format of the expected values is provided as a regex.
+FILTER = dict(
+    clusters=dict(
+        name='clusters',
+        id='id',
+        type='str',
+        format=r'domain\-[0-9a-fA-F]+',
+    ),
+    connection_states=dict(
+        name='connection_states',
+        id='connection state',
+        type='list',
+        choices=[
+            'CONNECTED',
+            'DISCONNECTED',
+            'NOT_RESPONDING',
+        ],
+    ),
+    datacenters=dict(
+        name='datacenters',
+        id='id',
+        type='str',
+        format=r'datacenter\-[0-9a-fA-F]+',
+    ),
+    datastore_types=dict(
+        name='types',
+        id='type',
+        type='list',
+        choices=[
+            '',
+            'CIFS',
+            'NFS',
+            'NFS41',
+            'VFFS',
+            'VMFS',
+            'VSAN',
+            'VVOL',
+        ]
+    ),
+    datastores=dict(
+        name='datastores',
+        id='id',
+        type='str',
+        format=r'datastore\-[0-9a-fA-F]+',
+    ),
+    folder_types=dict(
+        name='type',
+        id='type',
+        type='list',
+        choices=[
+            '',
+            'DATACENTER',
+            'DATASTORE',
+            'HOST',
+            'NETWORK',
+            'VIRTUAL_MACHINE',
+        ]
+    ),
+    folders=dict(
+        name='folders',
+        id='id',
+        type='str',
+        format=r'group\-[hnv][0-9a-fA-F]+',
+    ),
+    hosts=dict(
+        name='hosts',
+        id='id',
+        type='str',
+        format=r'host\-[0-9a-fA-F]+',
+    ),
+    names=dict(
+        name='names',
+        id='name',
+        type='str',
+        format=r'.+',
+    ),
+    network_types=dict(
+        name='types',
+        id='type',
+        type='list',
+        choices=[
+            'DISTRIBUTED_PORTGROUP',
+            'OPAQUE_NETWORK',
+            'STANDARD_PORTGROUP',
+        ],
+    ),
+    networks=dict(
+        name='networks',
+        id='id',
+        type='str',
+        format=r'[dvportgroup|network]\-[0-9a-fA-F]+',
+    ),
+    parent_folders=dict(
+        name='parent_folders',
+        id='id',
+        type='str',
+        format=r'group\-[hnv][0-9a-fA-F]+',
+    ),
+    parent_resource_pools=dict(
+        name='parent_resource_pools',
+        id='id',
+        type='str',
+        format=r'resgroup\-[0-9a-fA-F]+',
+    ),
+    policies=dict(
+        name='policies',
+        id='GUID',
+        type='str',
+        format=(r'[0-9a-fA-F]{8}'
+                r'\-[0-9a-fA-F]{4}'
+                r'\-[0-9a-fA-F]{4}'
+                r'\-[0-9a-fA-F]{4}'
+                r'\-[0-9a-fA-F]{12}'),
+    ),
+    power_states=dict(
+        name='power_states',
+        id='power state',
+        type='list',
+        choices=[
+            '',
+            'POWERED_OFF',
+            'POWERED_ON',
+            'SUSPENDED',
+        ],
+    ),
+    resource_pools=dict(
+        name='resource_pools',
+        id='id',
+        type='str',
+        format=r'resgroup\-[0-9a-fA-F]+',
+    ),
+    status=dict(
+        name='status',
+        id='status',
+        type='list',
+        choices=[
+            'COMPLIANT',
+            'NON_COMPLIANT',
+            'NOT_APPLICABLE',
+            'UNKNOWN',
+            'UNKNOWN_COMPLIANCE',
+            'OUT_OF_DATE',
+        ],
+    ),
+    vms=dict(
+        name='vms',
+        id='id',
+        type='str',
+        format=r'vm\-[0-9a-fA-F]+',
+    ),
+)
+
+# vSphere Inventory Objects
+#
+# This dictionary lists the queryable vSphere inventory objects.  Each
+# object identifies the API it is managed through, its URL off of the
+# API's base, and a list of filters that are valid for this particular
+# object.
+#
+# NOTE:  This will be replaced with a class factory pattern as get_id()
+# and the get_url() family are tied to this structure.
+INVENTORY = dict(
+    category=dict(
+        api='cis',
+        url='/tagging/category',
+        filters=[],
+    ),
+    cluster=dict(
+        api='vcenter',
+        url='/cluster',
+        filters=[
+            'clusters',
+            'datacenters',
+            'folders',
+            'names',
+        ],
+    ),
+    content_library=dict(
+        api='content',
+        url='/library',
+        filters=[],
+    ),
+    content_type=dict(
+        api='content',
+        url='/type',
+        filters=[],
+    ),
+    datacenter=dict(
+        api='vcenter',
+        url='/datacenter',
+        filters=[
+            'datacenters',
+            'folders',
+            'names',
+        ],
+    ),
+    datastore=dict(
+        api='vcenter',
+        url='/datastore',
+        filters=[
+            'datacenters',
+            'datastore_types',
+            'datastores',
+            'folders',
+            'names',
+        ],
+    ),
+    folder=dict(
+        api='vcenter',
+        url='/folder',
+        filters=[
+            'datacenters',
+            'folder_types',
+            'folders',
+            'names',
+            'parent_folders',
+        ],
+    ),
+    host=dict(
+        api='vcenter',
+        url='/host',
+        filters=[
+            'clusters',
+            'connection_states',
+            'datacenters',
+            'folders',
+            'hosts',
+            'names',
+        ],
+    ),
+    local_library=dict(
+        api='content',
+        url='/local-library',
+        filters=[],
+    ),
+    network=dict(
+        api='vcenter',
+        url='/network',
+        filters=[
+            'datacenters',
+            'folders',
+            'names',
+            'network_types',
+            'networks',
+        ],
+    ),
+    resource_pool=dict(
+        api='vcenter',
+        url='/resource-pool',
+        filters=[
+            'clusters',
+            'datacenters',
+            'hosts',
+            'names',
+            'parent_resource_pools',
+            'resource_pools',
+        ]
+    ),
+    storage_policy=dict(
+        api='vcenter',
+        url='/storage/policies',
+        filters=[
+            'policies',
+            'status',
+            'vms',
+        ],
+    ),
+    subscribed_library=dict(
+        api='content',
+        url='/subscribed-library',
+        filters=[],
+    ),
+    tag=dict(
+        api='cis',
+        url='/tagging/tag',
+        filters=[],
+    ),
+    vm=dict(
+        api='vcenter',
+        url='/vm',
+        filters=[
+            'clusters',
+            'datacenters',
+            'folders',
+            'hosts',
+            'names',
+            'power_states',
+            'resource_pools',
+            'vms',
+        ],
+    ),
+)
+
+
+class VmwareRestModule(AnsibleModule):
+
+    def __init__(self, is_multipart=False, use_object_handler=False, *args, **kwargs):
+        '''Constructor - This module mediates interactions with the
+        VMware httpapi connector plugin, implementing VMware's ReST API.
+
+        :module: VmwareRestModule extended from AnsibleModule.
+        :kw is_multipart: Indicates whether module makes multiple API calls.
+            Default False
+        :kw use_object_handler: Indicates whether module supports
+            multiple object types.  Default False
+        '''
+        # Initialize instance arguments
+        self.is_multipart = is_multipart
+        self.use_object_handler = use_object_handler
+
+        # Output of module
+        self.result = {}
+
+        # Current key of output
+        self.key = None
+
+        # Current information going to httpapi
+        self.request = dict(
+            url=None,
+            filter=None,
+            data={},
+            method=None,
+        )
+
+        # Last response from httpapi
+        self.response = dict(
+            status=None,
+            data={},
+        )
+
+        # Initialize AnsibleModule superclass before params
+        super(VmwareRestModule, self).__init__(*args, **kwargs)
+
+        # Params
+        #
+        # REQUIRED: Their absence will chuck a rod
+        self.allow_multiples = self.params['allow_multiples']
+        self.status_code = self.params['status_code']
+        # OPTIONAL: Use params.get() to gracefully fail
+        self.filters = self.params.get('filters')
+        self.state = self.params.get('state')
+
+        # Initialize connection via httpapi connector. See "REST API Calls"
+        self._connection = Connection(self._socket_path)
+
+        # Register default status handlers. See "Dynamic Status Handlers"
+        self._status_handlers = {
+            'success': self.handle_default_success,
+            '401': self.handle_default_401,
+            '404': self.handle_default_404,
+            'default': self.handle_default_generic,
+        }
+        if self.use_object_handler:
+            self._status_handlers['default'] = self.handle_default_object
+
+        # Turn on debug if not specified, but ANSIBLE_DEBUG is set
+        self.module_debug = {}
+        if self._debug:
+            self.warn('Enable debug output because ANSIBLE_DEBUG was set.')
+            self.params['log_level'] = 'debug'
+        self.log_level = self.params['log_level']
+
+    # Debugging
+    #
+    # Tools to handle debugging output from the APIs.
+    def _mod_debug(self, key, **kwargs):
+        self.module_debug[key] = kwargs
+        if 'module_debug' not in self.module_debug:
+            self.module_debug = dict(key=kwargs)
+        else:
+            self.module_debug.update(key=kwargs)
+
+    def _api_debug(self):
+        '''Route debugging output to the module output.
+
+        NOTE: Adding self.path to result['path'] causes an absent in
+        output.  Adding response['data'] causes infinite loop.
+        '''
+        return dict(
+            url=self.request['url'],
+            filter=self.request['filter'],
+            data=self.request['data'],
+            method=self.request['method'],
+            status=self.response['status'],
+            state=self.state,
+        )
+
+    # Dynamic Status Handlers
+    #
+    # A handler is registered by adding its key, either a module-
+    # generated value, or the string representation of the status code;
+    # and the name of the handler function.  The provided handlers are
+    #   success     defined, by default, as a status code of 200, but
+    #               can be redefined, per module, using the status_code
+    #               parameter in that module's argument_spec.
+    #   401         Unauthorized access to the API.
+    #   404         Requested object or API was not found.
+    #   default     Any status code not otherwise identified.
+    # The default handlers are named 'handle_default_[status_code]'.
+    # User defined handlers should use 'handle_[status_code]' as a
+    # convention.  Note that if the module expects to handle more than
+    # one type of object, a default object handler replaces the default
+    # generic handler.
+    #
+    # Handlers do not take any arguments, instead using the instance's
+    # variables to determine the status code and any additional data,
+    # like object_type.  To create or replace a handler, extend this
+    # class, define the new handler and use the provided 'set_handler'
+    # method.  User handlers can also chain to the default handlers if
+    # desired.
+    def set_handler(self, status_key, handler):
+        '''Registers the handler to the status_key'''
+        self._status_handlers[status_key] = handler
+
+    def _use_handler(self):
+        '''Invokes the appropriate handler based on status_code'''
+        if self.response['status'] in self.status_code:
+            status_key = 'success'
+        else:
+            status_key = str(self.response['status'])
+        if status_key in self._status_handlers.keys():
+            self._status_handlers[status_key]()
+        else:
+            self._status_handlers['default']()
+
+    def handle_default_success(self):
+        '''Default handler for all successful status codes'''
+        self.result[self.key] = self.response['data']
+        if self.log_level == 'debug':
+            self.result[self.key].update(
+                debug=self._api_debug()
+            )
+        if not self.is_multipart:
+            self.exit()
+
+    def handle_default_401(self):
+        '''Default handler for Unauthorized (401) errors'''
+        self.fail(msg="Unable to authenticate. Provided credentials are not valid.")
+
+    def handle_default_404(self):
+        '''Default handler for Not-Found (404) errors'''
+        self.fail(msg="Requested object was not found.")
+
+    def handle_default_generic(self):
+        '''Catch-all handler for all other status codes'''
+        msg = self.response['data']['value']['messages'][0]['default_message']
+        self.fail(msg=msg)
+
+    def handle_default_object(self):
+        '''Catch-all handler capable of distinguishing multiple objects'''
+        try:
+            msg = self.response['data']['value']['messages'][0]['default_message']
+        except (KeyError, TypeError):
+            msg = 'Unable to find the %s object specified due to %s' % (self.key, self.response)
+        self.fail(msg=msg)
+
+    def handle_object_key_error(self):
+        '''Lazy exception handler'''
+        msg = ('Please specify correct object type to get information, '
+               'choices are [%s].' % ", ".join(list(INVENTORY.keys())))
+        self.fail(msg=msg)
+
+    # REST API Calls
+    #
+    # VMware's REST API uses GET, POST, PUT, PATCH and DELETE http
+    # calls to read, create, update and delete objects and their
+    # attributes.  These calls are implemented as functions here.
+    def get(self, url='/rest', key='result'):
+        '''Sends a GET request to the httpapi plugin connection to the
+        specified URL. If successful, the returned data will be placed
+        in the output under the specified key.
+        '''
+        self.request.update(
+            url=url,
+            data={},
+            method='GET',
+        )
+        self.key = key
+        self.response['status'], self.response['data'] = self._connection.send_request(url, {}, method='GET')
+        self._use_handler()
+
+    def post(self, url='/rest', data=None, key='result'):
+        '''Sends a POST request to the httpapi plugin connection to the
+        specified URL, with the supplied data.  If successful, any
+        returned data will be placed in the output under the specified
+        key.
+        '''
+        self.request.update(
+            url=url,
+            data=data,
+            method='POST',
+        )
+        self.key = key
+        self.response['status'], self.response['data'] = self._connection.send_request(url, data, method='POST')
+        self._use_handler()
+
+    def put(self, url='/rest', data=None, key='result'):
+        '''Sends a PUT request to the httpapi plugin connection to the
+        specified URL, with the supplied data.  If successful, any
+        returned data will be placed in the output under the specified
+        key.
+        '''
+        self.request.update(
+            url=url,
+            data=data,
+            method='PUT',
+        )
+        self.key = key
+        self.response['status'], self.response['data'] = self._connection.send_request(url, data, method='PUT')
+        self._use_handler()
+
+    def delete(self, url='/rest', data='result', key='result'):
+        '''Sends a DELETE request to the httpapi plugin connection to
+        the specified URL, with the supplied data.  If successful, any
+        returned data will be placed in the output under the specified
+        key.
+        '''
+        self.request.update(
+            url=url,
+            data=data,
+            method='DELETE',
+        )
+        self.key = key
+        self.response['status'], self.response['data'] = self._connection.send_request(url, data, method='DELETE')
+        self._use_handler()
+
+    def get_id(self, object_type, name):
+        '''Find id(s) of object(s) with given name.  allow_multiples
+        determines whether multiple IDs are returned or not.
+
+        :kw object_type: The inventory object type whose id is desired.
+        :kw name: The name of the object(s) to be retrieved.
+        :returns: a list of strings representing the IDs of the objects.
+        '''
+
+        try:
+            url = (API[INVENTORY[object_type]['api']]['base']
+                   + INVENTORY[object_type]['url'])
+            if '/' in name:
+                name.replace('/', '%2F')
+            url += '&filter.names=' + name
+        except KeyError:
+            self.fail(msg='object_type must be one of [%s].'
+                      % ", ".join(list(INVENTORY.keys())))
+
+        status, data = self._connection.send_request(url, {}, method='GET')
+        if status != 200:
+            self.request.update(url=url, data={}, method='GET')
+            self.response.update(status=status, data=data)
+            self.handle_default_generic()
+
+        num_items = len(data['value'])
+        if not self.allow_multiples and num_items > 1:
+            msg = ('Found %d objects of type %s with name %s. '
+                   'Set allow_multiples to True if this is expected.'
+                   % (num_items, object_type, name))
+            self.fail(msg=msg)
+
+        ids = []
+        for i in range(num_items):
+            ids += data[i][object_type]
+        return ids
+
+    def _build_filter(self, object_type):
+        '''Builds a filter from the optionally supplied params'''
+        if self.filters:
+            try:
+                first = True
+                for filter in self.filters:
+                    for key in list(filter.keys()):
+                        filter_key = key.lower()
+                        # Check if filter is valid for current object type or not
+                        if filter_key not in INVENTORY[object_type]['filters']:
+                            msg = ('%s is not a valid %s filter, choices are [%s].'
+                                   % (key, object_type, ", ".join(INVENTORY[object_type]['filters'])))
+                            self.fail(msg=msg)
+                        # Check if value is valid for the current filter
+                        if ((FILTER[filter_key]['type'] == 'str' and not re.match(FILTER[filter_key]['format'], filter[key])) or
+                                (FILTER[filter_key]['type'] == 'list' and filter[key] not in FILTER[filter_key]['choices'])):
+                            msg = ('%s is not a valid %s %s' % (filter[key], object_type, FILTER[filter_key]['name']))
+                            self.fail(msg=msg)
+                        if first:
+                            self.request['filter'] = '?'
+                            first = False
+                        else:
+                            self.request['filter'] += '&'
+                        # Escape characters
+                        if '/' in filter[key]:
+                            filter[key].replace('/', '%2F')
+                        self.request['filter'] += ('filter.%s=%s'
+                                                   % (FILTER[filter_key]['name'], filter[key]))
+            except KeyError:
+                self.handle_object_key_error()
+        else:
+            self.request['filter'] = None
+        return self.request['filter']
+
+    def get_url(self, object_type, with_filter=False):
+        '''Retrieves the URL of a particular inventory object with or without filter'''
+        try:
+            self.url = (API[INVENTORY[object_type]['api']]['base']
+                        + INVENTORY[object_type]['url'])
+            if with_filter:
+                self.url += self._build_filter(object_type)
+        except KeyError:
+            self.handle_object_key_error
+        return self.url
+
+    def get_url_with_filter(self, object_type):
+        '''Same as get_url, only with_filter is explicitly set'''
+        return self.get_url(object_type, with_filter=True)
+
+    def reset(self):
+        '''Clears the decks for next request'''
+        self.request.update(
+            url=None,
+            data={},
+            method=None,
+        )
+        self.response.update(
+            status=None,
+            data={},
+        )
+
+    def fail(self, msg):
+        if self.log_level == 'debug':
+            if self.request['url'] is not None:
+                self.result['debug'] = self._api_debug()
+        AnsibleModule.fail_json(self, msg=msg, **self.result)
+
+    def exit(self):
+        '''Called to end client interaction'''
+        if 'invocation' not in self.result:
+            self.result['invocation'] = {
+                'module_args': self.params,
+                'module_kwargs': {
+                    'is_multipart': self.is_multipart,
+                    'use_object_handler': self.use_object_handler,
+                }
+            }
+        if self.log_level == 'debug':
+            if not self.is_multipart:
+                self.result['invocation'].update(debug=self._api_debug())
+            if self.module_debug:
+                self.result['invocation'].update(module_debug=self.module_debug)
+
+        AnsibleModule.exit_json(self, **self.result)
+
+    def _merge_dictionaries(self, a, b):
+        new = a.copy()
+        new.update(b)
+        return new
+
+    @staticmethod
+    def create_argument_spec(use_filters=False, use_state=False):
+        '''Provide a default argument spec for this module.  Filters and
+        state are optional parameters dependinf on the module's needs.
+        Additional parameters can be added.  The supplied parameters can
+        have defaults changed or choices pared down, but should not be
+        removed.
+        '''
+
+        argument_spec = dict(
+            allow_multiples=dict(type='bool', default=False),
+            log_level=dict(type='str',
+                           choices=['debug', 'info', 'normal'],
+                           default='normal'),
+            status_code=dict(type='list', default=[200]),
+        )
+        if use_filters:
+            argument_spec.update(filters=dict(type='list', default=[]))
+        if use_state:
+            argument_spec.update(state=dict(type='list',
+                                            choices=['absent', 'present', 'query'],
+                                            default='query'))
+        return argument_spec
diff --git a/lib/ansible/module_utils/vmware_httpapi/__init__.py b/lib/ansible/module_utils/vmware_httpapi/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/lib/ansible/modules/cloud/vmware_httpapi/__init__.py b/lib/ansible/modules/cloud/vmware_httpapi/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/lib/ansible/modules/cloud/vmware_httpapi/vmware_appliance_access_info.py b/lib/ansible/modules/cloud/vmware_httpapi/vmware_appliance_access_info.py
new file mode 100644
index 00000000000..abdd47abd5c
--- /dev/null
+++ b/lib/ansible/modules/cloud/vmware_httpapi/vmware_appliance_access_info.py
@@ -0,0 +1,117 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Knight <paul.knight@delaware.gov>
+# 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_appliance_access_info
+short_description: Gathers info about modes of access to the vCenter appliance using REST API.
+description:
+- This module can be used to gather information about the four modes of accessing the VCSA.
+- This module is based on REST API and uses httpapi connection plugin for persistent connection.
+- The Appliance API works against the VCSA and uses the "administrator@vsphere.local" user.
+version_added: '2.9'
+author:
+- Paul Knight (@n3pjk)
+notes:
+- Tested on vSphere 6.7
+requirements:
+- python >= 2.6
+options:
+  access_mode:
+    description:
+    - Method of access to get to appliance
+    - If not specified, all modes will be returned.
+    required: false
+    choices: ['consolecli', 'dcui', 'shell', 'ssh']
+    type: str
+extends_documentation_fragment: VmwareRestModule.documentation
+'''
+
+EXAMPLES = r'''
+- hosts: all
+  connection: httpapi
+  gather_facts: false
+  vars:
+    ansible_network_os: vmware
+    ansible_host: vcenter.my.domain
+    ansible_user: administrator@vsphere.local
+    ansible_httpapi_password: "SomePassword"
+    ansbile_httpapi_use_ssl: yes
+    ansible_httpapi_validate_certs: false
+  tasks:
+
+  - name: Get all access modes information
+    vmware_appliance_access_info:
+
+  - name: Get ssh access mode information
+    vmware_appliance_access_info:
+      access_mode: ssh
+'''
+
+RETURN = r'''
+access_mode:
+    description: facts about the specified access mode
+    returned: always
+    type: dict
+    sample: {
+        "value": true
+    }
+'''
+
+from ansible.module_utils.vmware_httpapi.VmwareRestModule import API, VmwareRestModule
+
+
+SLUG = dict(
+    consolecli='/access/consolecli',
+    dcui='/access/dcui',
+    shell='/access/shell',
+    ssh='/access/ssh',
+)
+
+
+def get_mode(module, mode):
+    try:
+        url = API['appliance']['base'] + SLUG[mode]
+    except KeyError:
+        module.fail(msg='[%s] is not a valid access mode. '
+                    'Please specify correct mode, valid choices are '
+                    '[%s].' % (mode, ", ".join(list(SLUG.keys()))))
+
+    module.get(url=url, key=mode)
+
+
+def main():
+    argument_spec = VmwareRestModule.create_argument_spec()
+    argument_spec.update(
+        access_mode=dict(type='str', choices=['consolecli', 'dcui', 'shell', 'ssh'], default=None),
+    )
+
+    module = VmwareRestModule(argument_spec=argument_spec,
+                              supports_check_mode=True,
+                              is_multipart=True,
+                              use_object_handler=True)
+    access_mode = module.params['access_mode']
+
+    if access_mode is None:
+        access_mode = SLUG.keys()
+        for mode in access_mode:
+            get_mode(module, mode)
+    else:
+        get_mode(module, access_mode)
+
+    module.exit()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/lib/ansible/modules/cloud/vmware_httpapi/vmware_appliance_health_info.py b/lib/ansible/modules/cloud/vmware_httpapi/vmware_appliance_health_info.py
new file mode 100644
index 00000000000..9acaaec721c
--- /dev/null
+++ b/lib/ansible/modules/cloud/vmware_httpapi/vmware_appliance_health_info.py
@@ -0,0 +1,148 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Knight <paul.knight@delaware.gov>
+# 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_appliance_health_info
+short_description: Gathers info about health of the VCSA.
+description:
+- This module can be used to gather information about VCSA health.
+- This module is based on REST API and uses httpapi connection plugin for persistent connection.
+- The Appliance API works against the VCSA and uses the "administrator@vsphere.local" user.
+version_added: '2.9'
+author:
+- Paul Knight (@n3pjk)
+notes:
+- Tested on vSphere 6.7
+requirements:
+- python >= 2.6
+options:
+  subsystem:
+    description:
+    - A subsystem of the VCSA.
+    required: false
+    choices: ['applmgmt', 'databasestorage', 'lastcheck', 'load', 'mem', 'softwarepackages', 'storage', 'swap', 'system']
+    type: str
+  asset:
+    description:
+    - A VCSA asset that has associated health metrics.
+    - Valid choices have yet to be determined at this time.
+    required: false
+    type: str
+extends_documentation_fragment: VmwareRestModule.documentation
+'''
+
+EXAMPLES = r'''
+- hosts: all
+  connection: httpapi
+  gather_facts: false
+  vars:
+    ansible_network_os: vmware
+    ansible_host: vcenter.my.domain
+    ansible_user: administrator@vsphere.local
+    ansible_httpapi_password: "SomePassword"
+    ansbile_httpapi_use_ssl: yes
+    ansible_httpapi_validate_certs: false
+  tasks:
+
+    - name: Get all health attribute information
+      vmware_appliance_health_info:
+
+    - name: Get system health information
+      vmware_appliance_health_info:
+        subsystem: system
+'''
+
+RETURN = r'''
+attribute:
+    description: facts about the specified health attribute
+    returned: always
+    type: dict
+    sample: {
+        "value": true
+    }
+'''
+
+from ansible.module_utils.vmware_httpapi.VmwareRestModule import API, VmwareRestModule
+
+
+SLUG = dict(
+    applmgmt='/health/applmgmt',
+    databasestorage='/health/database-storage',
+    load='/health/load',
+    mem='/health/mem',
+    softwarepackages='/health/software-packages',
+    storage='/health/storage',
+    swap='/health/swap',
+    system='/health/system',
+    lastcheck='/health/system/lastcheck',
+)
+
+
+def get_subsystem(module, subsystem):
+    try:
+        url = API['appliance']['base'] + SLUG[subsystem]
+    except KeyError:
+        module.fail(msg='[%s] is not a valid subsystem. '
+                    'Please specify correct subsystem, valid choices are '
+                    '[%s].' % (subsystem, ", ".join(list(SLUG.keys()))))
+
+    module.get(url=url, key=subsystem)
+
+
+def main():
+    argument_spec = VmwareRestModule.create_argument_spec()
+    argument_spec.update(
+        subsystem=dict(
+            type='str',
+            required=False,
+            choices=[
+                'applmgmt',
+                'databasestorage',
+                'lastcheck',
+                'load',
+                'mem',
+                'softwarepackages',
+                'storage',
+                'swap',
+                'system',
+            ],
+        ),
+        asset=dict(type='str', required=False),
+    )
+
+    module = VmwareRestModule(argument_spec=argument_spec,
+                              supports_check_mode=True,
+                              is_multipart=True,
+                              use_object_handler=True)
+    subsystem = module.params['subsystem']
+    asset = module.params['asset']
+
+    if asset is not None:
+        url = (API['appliance']['base']
+               + ('/health/%s/messages' % asset))
+
+        module.get(url=url, key=asset)
+    elif subsystem is None:
+        subsystem = SLUG.keys()
+        for sys in subsystem:
+            get_subsystem(module, sys)
+    else:
+        get_subsystem(module, subsystem)
+
+    module.exit()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/lib/ansible/modules/cloud/vmware_httpapi/vmware_cis_category_info.py b/lib/ansible/modules/cloud/vmware_httpapi/vmware_cis_category_info.py
new file mode 100644
index 00000000000..5c294d510fb
--- /dev/null
+++ b/lib/ansible/modules/cloud/vmware_httpapi/vmware_cis_category_info.py
@@ -0,0 +1,149 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Knight <paul.knight@delaware.gov>
+# 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_cis_category_info
+short_description: Gathers info about all, or a specified category.
+description:
+- This module can be used to gather information about a specific category.
+- This module can also gather facts about all categories.
+- This module is based on REST API and uses httpapi connection plugin for persistent connection.
+version_added: '2.9'
+author:
+- Paul Knight (@n3pjk)
+notes:
+- Tested on vSphere 6.7
+requirements:
+- python >= 2.6
+options:
+  category_id:
+    description:
+    - The object id of the category.
+    - Exclusive of category_name and used_by_*.
+    required: false
+    type: str
+  category_name:
+    description:
+    - The name of the category.
+    - Exclusive of category_id and used_by_*.
+    required: false
+    type: str
+  used_by_id:
+    description:
+    - The id of the entity to list applied categories.
+    - Exclusive of other used_by_* and category_*.
+    type: str
+  used_by_name:
+    description:
+    - The name of the entity to list applied categories, whose type is specified in used_by_type.
+    - Exclusive of other used_by_id and category_*.
+    type: str
+  used_by_type:
+    description:
+    - The type of the entity to list applied categories, whose name is specified in used_by_name.
+    - Exclusive of other used_by_id and category_*.
+    choices: ['cluster', 'content_library', 'content_type', 'datacenter',
+              'datastore', 'folder', 'host', 'local_library', 'network',
+              'resource_pool', 'subscribed_library', 'tag', 'vm']
+    type: str
+extends_documentation_fragment: VmwareRestModule.documentation
+'''
+
+EXAMPLES = r'''
+- name: Get all categories
+  vmware_cis_category_info:
+'''
+
+RETURN = r'''
+category:
+    description: facts about the specified category
+    returned: always
+    type: dict
+    sample: {
+        "value": true
+    }
+'''
+
+from ansible.module_utils.vmware_httpapi.VmwareRestModule import VmwareRestModule
+
+
+def main():
+    argument_spec = VmwareRestModule.create_argument_spec()
+    argument_spec.update(
+        category_name=dict(type='str', required=False),
+        category_id=dict(type='str', required=False),
+        used_by_name=dict(type='str', required=False),
+        used_by_type=dict(
+            type='str',
+            required=False,
+            choices=[
+                'cluster',
+                'content_library',
+                'content_type',
+                'datacenter',
+                'datastore',
+                'folder',
+                'host',
+                'local_library',
+                'network',
+                'resource_pool',
+                'subscribed_library',
+                'tag',
+                'vm',
+            ],
+        ),
+        used_by_id=dict(type='str', required=False),
+    )
+
+    required_together = [
+        ['used_by_name', 'used_by_type']
+    ]
+
+    mutually_exclusive = [
+        ['category_name', 'category_id', 'used_by_id', 'used_by_name'],
+        ['category_name', 'category_id', 'used_by_id', 'used_by_type'],
+    ]
+
+    module = VmwareRestModule(argument_spec=argument_spec,
+                              required_together=required_together,
+                              mutually_exclusive=mutually_exclusive,
+                              supports_check_mode=True)
+
+    category_name = module.params['category_name']
+    category_id = module.params['category_id']
+    used_by_name = module.params['used_by_name']
+    used_by_type = module.params['used_by_type']
+    used_by_id = module.params['used_by_id']
+
+    url = module.get_url('category')
+    data = {}
+    if category_name is not None:
+        category_id = module.get_id('category', category_name)
+    if category_id is not None:
+        url += '/id:' + category_id
+        module.get(url=url)
+    else:
+        if used_by_name is not None:
+            used_by_id = module.get_id(used_by_type, used_by_name)
+        url += '?~action=list-used-categories'
+        data = {
+            'used_by_entity': used_by_id
+        }
+        module.post(url=url, data=data)
+    module.exit()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/lib/ansible/modules/cloud/vmware_httpapi/vmware_core_info.py b/lib/ansible/modules/cloud/vmware_httpapi/vmware_core_info.py
new file mode 100644
index 00000000000..74a4425c9fd
--- /dev/null
+++ b/lib/ansible/modules/cloud/vmware_httpapi/vmware_core_info.py
@@ -0,0 +1,117 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Abhijeet Kasurde <akasurde@redhat.com>
+# Copyright: (c) 2019, Paul Knight <paul.knight@delaware.gov>
+# 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_core_info
+short_description: Gathers info about various VMware inventory objects using REST API
+description:
+- This module can be used to gather information about various VMware inventory objects.
+- This module is based on REST API and uses httpapi connection plugin for persistent connection.
+version_added: '2.9'
+author:
+- Abhijeet Kasurde (@Akasurde)
+- Paul Knight (@n3pjk)
+notes:
+- Tested on vSphere 6.7
+requirements:
+- python >= 2.6
+options:
+  object_type:
+    description:
+    - Type of VMware object.
+    - Valid choices are datacenter, cluster, datastore, folder, host,
+      network, resource_pool, virtual_machine, content_library,
+      local_library, subscribed_library, content_type, tag, category.
+    type: str
+    default: datacenter
+  filters:
+    description:
+    - A list of filters to find the given object.
+    - Valid filters for datacenter object type - folders, datacenters, names.
+    - Valid filters for cluster object type - folders, datacenters, names, clusters.
+    - Valid filters for datastore object type - folders, datacenters, names, datastores, types.
+    - Valid filters for folder object type - folders, parent_folders, names, datacenters, type.
+    - Valid filters for host object type - folders, hosts, names, datacenters, clusters, connection_states.
+    - Valid filters for network object type - folders, types, names, datacenters, networks.
+    - Valid filters for resource_pool object type - resource_pools, parent_resource_pools, names, datacenters, hosts, clusters.
+    - Valid filters for virtual_machine object type - folders, resource_pools, power_states, vms, names, datacenters, hosts, clusters.
+    - content_library, local_library, subscribed_library, content_type, tag, category does not take any filters.
+    default: []
+    type: list
+extends_documentation_fragment: VmwareRestModule_filters.documentation
+'''
+
+EXAMPLES = r'''
+- name: Get All VM without any filters
+  block:
+  - name: Get VMs
+    vmware_core_info:
+      object_type: "{{ object_type }}"
+    register: vm_result
+
+  - assert:
+      that:
+      - vm_result[object_type].value | length > 0
+  vars:
+    object_type: vm
+
+- name: Get all clusters from Asia-Datacenter1
+  vmware_core_info:
+    object_type: cluster
+    filters:
+      - datacenters: "{{ datacenter_obj }}"
+  register: clusters_result
+'''
+
+RETURN = r'''
+object_info:
+    description: information about the given VMware object
+    returned: always
+    type: dict
+    sample: {
+        "value": [
+            {
+                "cluster": "domain-c42",
+                "drs_enabled": false,
+                "ha_enabled": false,
+                "name": "Asia-Cluster1"
+            }
+        ]
+    }
+'''
+
+from ansible.module_utils.vmware_httpapi.VmwareRestModule import VmwareRestModule
+
+
+def main():
+    argument_spec = VmwareRestModule.create_argument_spec(use_filters=True)
+    argument_spec.update(
+        object_type=dict(type='str', default='datacenter'),
+    )
+
+    module = VmwareRestModule(argument_spec=argument_spec,
+                              supports_check_mode=True,
+                              use_object_handler=True)
+    object_type = module.params['object_type']
+
+    url = module.get_url_with_filter(object_type)
+
+    module.get(url=url, key=object_type)
+    module.exit()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/lib/ansible/plugins/doc_fragments/VmwareRestModule.py b/lib/ansible/plugins/doc_fragments/VmwareRestModule.py
new file mode 100644
index 00000000000..8dbd6038ca4
--- /dev/null
+++ b/lib/ansible/plugins/doc_fragments/VmwareRestModule.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Knight <paul.knight@delaware.gov>
+# 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
+
+
+class ModuleDocFragment(object):
+    # Parameters for VMware ReST HTTPAPI modules omits filters and state
+    DOCUMENTATION = r'''
+options:
+    allow_multiples:
+      description:
+      - Indicates whether get_id() can return multiple IDs for a given name.
+      - Typically, this should be false when updating or deleting; otherwise, all named objects could be affected.
+      required: true
+      version_added: "2.9"
+      type: bool
+    log_level:
+      description:
+      - If ANSIBLE_DEBUG is set, this will be forced to 'debug', but can be user-defined otherwise.
+      required: True
+      choices: ['debug', 'info', 'normal']
+      version_added: "2.9"
+      type: str
+      default: 'normal'
+    status_code:
+      description:
+      - A list of integer status codes considered to be successful for the this module.
+      required: true
+      version_added: "2.9"
+      type: list
+      default: [200]
+'''
diff --git a/lib/ansible/plugins/doc_fragments/VmwareRestModule_filters.py b/lib/ansible/plugins/doc_fragments/VmwareRestModule_filters.py
new file mode 100644
index 00000000000..73c52de4d98
--- /dev/null
+++ b/lib/ansible/plugins/doc_fragments/VmwareRestModule_filters.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Knight <paul.knight@delaware.gov>
+# 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
+
+
+class ModuleDocFragment(object):
+    # Parameters for VMware ReST HTTPAPI modules includes filters
+    DOCUMENTATION = r'''
+options:
+    allow_multiples:
+      description:
+      - Indicates whether get_id() can return multiple IDs for a given name.
+      - Typically, this should be false when updating or deleting; otherwise, all named objects could be affected.
+      required: true
+      version_added: "2.9"
+      type: bool
+    filters:
+      description:
+      - The key/value pairs describing filters to be applied to the request(s) made by this instance.
+      required: false
+      version_added: "2.9"
+      type: dict
+    log_level:
+      description:
+      - If ANSIBLE_DEBUG is set, this will be forced to 'debug', but can be user-defined otherwise.
+      required: True
+      choices: ['debug', 'info', 'normal']
+      version_added: "2.9"
+      type: str
+      default: 'normal'
+    status_code:
+      description:
+      - A list of integer status codes considered to be successful for the this module.
+      required: true
+      version_added: "2.9"
+      type: list
+      default: [200]
+'''
diff --git a/lib/ansible/plugins/doc_fragments/VmwareRestModule_full.py b/lib/ansible/plugins/doc_fragments/VmwareRestModule_full.py
new file mode 100644
index 00000000000..3adf3b2b383
--- /dev/null
+++ b/lib/ansible/plugins/doc_fragments/VmwareRestModule_full.py
@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Knight <paul.knight@delaware.gov>
+# 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
+
+
+class ModuleDocFragment(object):
+    # Parameters for VMware ReST HTTPAPI modules includes filters and state
+    DOCUMENTATION = r'''
+options:
+    allow_multiples:
+      description:
+      - Indicates whether get_id() can return multiple IDs for a given name.
+      - Typically, this should be false when updating or deleting; otherwise, all named objects could be affected.
+      required: true
+      version_added: "2.9"
+      type: bool
+    filters:
+      description:
+      - The key/value pairs describing filters to be applied to the request(s) made by this instance.
+      required: false
+      version_added: "2.9"
+      type: dict
+    log_level:
+      description:
+      - If ANSIBLE_DEBUG is set, this will be forced to 'debug', but can be user-defined otherwise.
+      required: True
+      choices: ['debug', 'info', 'normal']
+      version_added: "2.9"
+      type: str
+      default: 'normal'
+    state:
+      description:
+      - Either 'absent' or 'present', depending on whether object should be removed or created.
+      required: false
+      choices: ['absent', 'present', 'query']
+      version_added: "2.9"
+      type: str
+      default: 'present'
+    status_code:
+      description:
+      - A list of integer status codes considered to be successful for the this module.
+      required: true
+      version_added: "2.9"
+      type: list
+      default: [200]
+'''
diff --git a/lib/ansible/plugins/doc_fragments/VmwareRestModule_state.py b/lib/ansible/plugins/doc_fragments/VmwareRestModule_state.py
new file mode 100644
index 00000000000..01b692ca9fe
--- /dev/null
+++ b/lib/ansible/plugins/doc_fragments/VmwareRestModule_state.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Knight <paul.knight@delaware.gov>
+# 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
+
+
+class ModuleDocFragment(object):
+    # Parameters for VMware ReST HTTPAPI modules includes filters and state
+    DOCUMENTATION = r'''
+options:
+    allow_multiples:
+      description:
+      - Indicates whether get_id() can return multiple IDs for a given name.
+      - Typically, this should be false when updating or deleting; otherwise, all named objects could be affected.
+      required: true
+      version_added: "2.9"
+      type: bool
+    log_level:
+      description:
+      - If ANSIBLE_DEBUG is set, this will be forced to 'debug', but can be user-defined otherwise.
+      required: True
+      choices: ['debug', 'info', 'normal']
+      version_added: "2.9"
+      type: str
+      default: 'normal'
+    state:
+      description:
+      - Either 'absent' or 'present', depending on whether object should be removed or created.
+      required: false
+      choices: ['absent', 'present', 'query']
+      version_added: "2.9"
+      type: str
+      default: 'present'
+    status_code:
+      description:
+      - A list of integer status codes considered to be successful for the this module.
+      required: true
+      version_added: "2.9"
+      type: list
+      default: [200]
+'''
diff --git a/lib/ansible/plugins/httpapi/vmware.py b/lib/ansible/plugins/httpapi/vmware.py
new file mode 100644
index 00000000000..1e20d667a18
--- /dev/null
+++ b/lib/ansible/plugins/httpapi/vmware.py
@@ -0,0 +1,85 @@
+# Copyright: (c) 2018 Red Hat Inc.
+# Copyright: (c) 2019, Ansible Project
+# Copyright: (c) 2019, Abhijeet Kasurde <akasurde@redhat.com>
+# 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
+
+DOCUMENTATION = """
+---
+author: Abhijeet Kasurde (Akasurde)
+httpapi : vmware
+short_description: HttpApi Plugin for VMware REST API
+description:
+  - This HttpApi plugin provides methods to connect to VMware vCenter over a HTTP(S)-based APIs.
+version_added: "2.9"
+"""
+
+import json
+
+from ansible.module_utils.basic import to_text
+from ansible.errors import AnsibleConnectionFailure
+from ansible.module_utils.six.moves.urllib.error import HTTPError
+from ansible.plugins.httpapi import HttpApiBase
+from ansible.module_utils.connection import ConnectionError
+
+BASE_HEADERS = {
+    'Content-Type': 'application/json',
+    'Accept': 'application/json',
+}
+
+
+class HttpApi(HttpApiBase):
+    def login(self, username, password):
+        if username and password:
+            payload = {}
+            url = '/rest/com/vmware/cis/session'
+            response, response_data = self.send_request(url, payload)
+        else:
+            raise AnsibleConnectionFailure('Username and password are required for login')
+
+        if response == 404:
+            raise ConnectionError(response_data)
+
+        if not response_data.get('value'):
+            raise ConnectionError('Server returned response without token info during connection authentication: %s' % response)
+
+        self.connection._session_uid = "vmware-api-session-id:%s" % response_data['value']
+        self.connection._token = response_data['value']
+
+    def logout(self):
+        response, dummy = self.send_request('/rest/com/vmware/cis/session', None, method='DELETE')
+
+    def get_session_uid(self):
+        return self.connection._session_uid
+
+    def get_session_token(self):
+        return self.connection._token
+
+    def send_request(self, path, body_params, method='POST'):
+        data = json.dumps(body_params) if body_params else '{}'
+
+        try:
+            self._display_request(method=method)
+            response, response_data = self.connection.send(path, data, method=method, headers=BASE_HEADERS, force_basic_auth=True)
+            response_value = self._get_response_value(response_data)
+
+            return response.getcode(), self._response_to_json(response_value)
+        except AnsibleConnectionFailure as e:
+            return 404, 'Object not found'
+        except HTTPError as e:
+            return e.code, json.loads(e.read())
+
+    def _display_request(self, method='POST'):
+        self.connection.queue_message('vvvv', 'Web Services: %s %s' % (method, self.connection._url))
+
+    def _get_response_value(self, response_data):
+        return to_text(response_data.getvalue())
+
+    def _response_to_json(self, response_text):
+        try:
+            return json.loads(response_text) if response_text else {}
+        # JSONDecodeError only available on Python 3.5+
+        except ValueError:
+            raise ConnectionError('Invalid JSON response: %s' % response_text)