diff --git a/openstackclient/common/commandmanager.py b/openstackclient/common/commandmanager.py
index 9901ea2089..2d9575d9d2 100644
--- a/openstackclient/common/commandmanager.py
+++ b/openstackclient/common/commandmanager.py
@@ -16,6 +16,7 @@
 """Modify cliff.CommandManager"""
 
 import logging
+import pkg_resources
 
 import cliff.commandmanager
 
@@ -46,3 +47,17 @@ class CommandManager(cliff.commandmanager.CommandManager):
     def get_command_groups(self):
         """Returns a list of the loaded command groups"""
         return self.group_list
+
+    def get_command_names(self, group=None):
+        """Returns a list of commands loaded for the specified group"""
+        group_list = []
+        if group is not None:
+            for ep in pkg_resources.iter_entry_points(group):
+                cmd_name = (
+                    ep.name.replace('_', ' ')
+                    if self.convert_underscores
+                    else ep.name
+                )
+                group_list.append(cmd_name)
+            return group_list
+        return self.commands.keys()
diff --git a/openstackclient/common/module.py b/openstackclient/common/module.py
index 7f9c52db22..356cdca3b0 100644
--- a/openstackclient/common/module.py
+++ b/openstackclient/common/module.py
@@ -19,9 +19,25 @@ import logging
 import six
 import sys
 
+from cliff import lister
 from cliff import show
 
 
+class ListCommand(lister.Lister):
+    """List recognized commands by group"""
+
+    auth_required = False
+    log = logging.getLogger(__name__ + '.ListCommand')
+
+    def take_action(self, parsed_args):
+        self.log.debug('take_action(%s)', parsed_args)
+        cm = self.app.command_manager
+        groups = cm.get_command_groups()
+
+        columns = ('Command Group', 'Commands')
+        return (columns, ((c, cm.get_command_names(group=c)) for c in groups))
+
+
 class ListModule(show.ShowOne):
     """List module versions"""
 
diff --git a/openstackclient/tests/common/test_commandmanager.py b/openstackclient/tests/common/test_commandmanager.py
index ca9ee9a70e..e7803a48e4 100644
--- a/openstackclient/tests/common/test_commandmanager.py
+++ b/openstackclient/tests/common/test_commandmanager.py
@@ -86,3 +86,20 @@ class TestCommandManager(utils.TestCase):
 
         gl = mgr.get_command_groups()
         self.assertEqual(['test', 'greek'], gl)
+
+    def test_get_command_names(self):
+        mock_cmd_one = mock.Mock()
+        mock_cmd_one.name = 'one'
+        mock_cmd_two = mock.Mock()
+        mock_cmd_two.name = 'cmd two'
+        mock_pkg_resources = mock.Mock(
+            return_value=[mock_cmd_one, mock_cmd_two],
+        )
+        with mock.patch(
+            'pkg_resources.iter_entry_points',
+            mock_pkg_resources,
+        ) as iter_entry_points:
+            mgr = commandmanager.CommandManager('test')
+            assert iter_entry_points.called_once_with('test')
+            cmds = mgr.get_command_names('test')
+            self.assertEqual(['one', 'cmd two'], cmds)
diff --git a/openstackclient/tests/common/test_module.py b/openstackclient/tests/common/test_module.py
index ce1592e41c..6918c1b419 100644
--- a/openstackclient/tests/common/test_module.py
+++ b/openstackclient/tests/common/test_module.py
@@ -42,6 +42,38 @@ MODULES = {
 }
 
 
+class TestCommandList(utils.TestCommand):
+
+    def setUp(self):
+        super(TestCommandList, self).setUp()
+
+        self.app.command_manager = mock.Mock()
+        self.app.command_manager.get_command_groups.return_value = ['test']
+        self.app.command_manager.get_command_names.return_value = [
+            'one',
+            'cmd two',
+        ]
+
+        # Get the command object to test
+        self.cmd = osc_module.ListCommand(self.app, None)
+
+    def test_command_list_no_options(self):
+        arglist = []
+        verifylist = []
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        # DisplayCommandBase.take_action() returns two tuples
+        columns, data = self.cmd.take_action(parsed_args)
+
+        collist = ('Command Group', 'Commands')
+        self.assertEqual(collist, columns)
+        datalist = ((
+            'test',
+            ['one', 'cmd two'],
+        ), )
+        self.assertEqual(datalist, tuple(data))
+
+
 @mock.patch.dict(
     'openstackclient.common.module.sys.modules',
     values=MODULES,
diff --git a/setup.cfg b/setup.cfg
index 3178fe4467..267c9e330d 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -28,6 +28,7 @@ console_scripts =
     openstack = openstackclient.shell:main
 
 openstack.cli =
+    command_list = openstackclient.common.module:ListCommand
     module_list = openstackclient.common.module:ListModule
 
 openstack.cli.base =