diff --git a/doc/source/command-objects/router.rst b/doc/source/command-objects/router.rst
index c3a952e9dd..952415a8ac 100644
--- a/doc/source/command-objects/router.rst
+++ b/doc/source/command-objects/router.rst
@@ -74,3 +74,42 @@ List routers
 .. option:: --long
 
     List additional fields in output
+
+router set
+----------
+
+Set router properties
+
+.. program:: router set
+.. code:: bash
+
+    os router set
+        [--name <name>]
+        [--enable | --disable]
+        [--distributed | --centralized]
+        <router>
+
+.. option:: --name <name>
+
+    Set router name
+
+.. option:: --enable
+
+    Enable router
+
+.. option:: --disable
+
+    Disable router
+
+.. option:: --distributed
+
+    Set router to distributed mode (disabled router only)
+
+.. option:: --centralized
+
+    Set router to centralized mode (disabled router only)
+
+.. _router_set-router:
+.. describe:: <router>
+
+    Router to modify (name or ID)
diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py
index 0042e93f73..d084db1f75 100644
--- a/openstackclient/network/v2/router.py
+++ b/openstackclient/network/v2/router.py
@@ -20,6 +20,7 @@ from cliff import command
 from cliff import lister
 from cliff import show
 
+from openstackclient.common import exceptions
 from openstackclient.common import utils
 from openstackclient.identity import common as identity_common
 
@@ -49,6 +50,7 @@ def _get_attrs(client_manager, parsed_args):
         attrs['admin_state_up'] = parsed_args.admin_state_up
     if parsed_args.distributed is not None:
         attrs['distributed'] = parsed_args.distributed
+    # "router set" command doesn't support setting project.
     if 'project' in parsed_args and parsed_args.project is not None:
         identity_client = client_manager.identity
         project_id = identity_common.find_project(
@@ -57,6 +59,11 @@ def _get_attrs(client_manager, parsed_args):
             parsed_args.project_domain,
         ).id
         attrs['tenant_id'] = project_id
+
+    # TODO(tangchen): Support getting 'ha' property.
+    # TODO(tangchen): Support getting 'external_gateway_info' property.
+    # TODO(tangchen): Support getting 'routes' property.
+
     return attrs
 
 
@@ -195,3 +202,74 @@ class ListRouter(lister.Lister):
                     s, columns,
                     formatters=_formatters,
                 ) for s in data))
+
+
+class SetRouter(command.Command):
+    """Set router properties"""
+
+    log = logging.getLogger(__name__ + '.SetRouter')
+
+    def get_parser(self, prog_name):
+        parser = super(SetRouter, self).get_parser(prog_name)
+        parser.add_argument(
+            'router',
+            metavar="<router>",
+            help=("Router to modify (name or ID)")
+        )
+        parser.add_argument(
+            '--name',
+            metavar='<name>',
+            help='Set router name',
+        )
+        admin_group = parser.add_mutually_exclusive_group()
+        admin_group.add_argument(
+            '--enable',
+            dest='admin_state_up',
+            action='store_true',
+            default=None,
+            help='Enable router',
+        )
+        admin_group.add_argument(
+            '--disable',
+            dest='admin_state_up',
+            action='store_false',
+            help='Disable router',
+        )
+        distribute_group = parser.add_mutually_exclusive_group()
+        distribute_group.add_argument(
+            '--distributed',
+            dest='distributed',
+            action='store_true',
+            default=None,
+            help="Set router to distributed mode (disabled router only)",
+        )
+        distribute_group.add_argument(
+            '--centralized',
+            dest='distributed',
+            action='store_false',
+            help="Set router to centralized mode (disabled router only)",
+        )
+
+        # TODO(tangchen): Support setting 'ha' property in 'router set'
+        # command. It appears that changing the ha state is supported by
+        # neutron under certain conditions.
+
+        # TODO(tangchen): Support setting 'external_gateway_info' property in
+        # 'router set' command.
+
+        # TODO(tangchen): Support setting 'routes' property in 'router set'
+        # command.
+
+        return parser
+
+    def take_action(self, parsed_args):
+        self.log.debug('take_action(%s)' % parsed_args)
+        client = self.app.client_manager.network
+        obj = client.find_router(parsed_args.router, ignore_missing=False)
+
+        attrs = _get_attrs(self.app.client_manager, parsed_args)
+        if attrs == {}:
+            msg = "Nothing specified to be set"
+            raise exceptions.CommandError(msg)
+
+        client.update_router(obj, **attrs)
diff --git a/openstackclient/tests/network/v2/test_router.py b/openstackclient/tests/network/v2/test_router.py
index ccda9d2dc2..e40d2d7105 100644
--- a/openstackclient/tests/network/v2/test_router.py
+++ b/openstackclient/tests/network/v2/test_router.py
@@ -13,6 +13,7 @@
 
 import mock
 
+from openstackclient.common import exceptions
 from openstackclient.network.v2 import router
 from openstackclient.tests.network.v2 import fakes as network_fakes
 from openstackclient.tests import utils as tests_utils
@@ -194,3 +195,92 @@ class TestListRouter(TestRouter):
         self.network.routers.assert_called_with()
         self.assertEqual(self.columns_long, columns)
         self.assertEqual(self.data_long, list(data))
+
+
+class TestSetRouter(TestRouter):
+
+    # The router to set.
+    _router = network_fakes.FakeRouter.create_one_router()
+
+    def setUp(self):
+        super(TestSetRouter, self).setUp()
+
+        self.network.update_router = mock.Mock(return_value=None)
+
+        self.network.find_router = mock.Mock(return_value=self._router)
+
+        # Get the command object to test
+        self.cmd = router.SetRouter(self.app, self.namespace)
+
+    def test_set_this(self):
+        arglist = [
+            self._router.name,
+            '--enable',
+            '--distributed',
+            '--name', 'noob',
+        ]
+        verifylist = [
+            ('router', self._router.name),
+            ('admin_state_up', True),
+            ('distributed', True),
+            ('name', 'noob'),
+        ]
+
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+        result = self.cmd.take_action(parsed_args)
+
+        attrs = {
+            'admin_state_up': True,
+            'distributed': True,
+            'name': 'noob',
+        }
+        self.network.update_router.assert_called_with(self._router, **attrs)
+        self.assertEqual(None, result)
+
+    def test_set_that(self):
+        arglist = [
+            self._router.name,
+            '--disable',
+            '--centralized',
+        ]
+        verifylist = [
+            ('router', self._router.name),
+            ('admin_state_up', False),
+            ('distributed', False),
+        ]
+
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+        result = self.cmd.take_action(parsed_args)
+
+        attrs = {
+            'admin_state_up': False,
+            'distributed': False,
+        }
+        self.network.update_router.assert_called_with(self._router, **attrs)
+        self.assertEqual(None, result)
+
+    def test_set_distributed_centralized(self):
+        arglist = [
+            self._router.name,
+            '--distributed',
+            '--centralized',
+        ]
+        verifylist = [
+            ('router', self._router.name),
+            ('distributed', True),
+            ('distributed', False),
+        ]
+
+        try:
+            # Argument parse failing should bail here
+            self.check_parser(self.cmd, arglist, verifylist)
+        except tests_utils.ParserException:
+            pass
+
+    def test_set_nothing(self):
+        arglist = [self._router.name, ]
+        verifylist = [('router', self._router.name), ]
+
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+        self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+                          parsed_args)
diff --git a/setup.cfg b/setup.cfg
index ee6192a72b..d55dd1c989 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -335,6 +335,7 @@ openstack.network.v2 =
     router_create = openstackclient.network.v2.router:CreateRouter
     router_delete = openstackclient.network.v2.router:DeleteRouter
     router_list = openstackclient.network.v2.router:ListRouter
+    router_set = openstackclient.network.v2.router:SetRouter
 
 openstack.object_store.v1 =
     object_store_account_set = openstackclient.object.v1.account:SetAccount