diff --git a/core/modules/system/src/EventSubscriber/GroupRouteSubscriber.php b/core/modules/system/src/EventSubscriber/GroupRouteSubscriber.php
new file mode 100644
index 0000000000..1828f5a0a5
--- /dev/null
+++ b/core/modules/system/src/EventSubscriber/GroupRouteSubscriber.php
@@ -0,0 +1,143 @@
+<?php
+
+namespace Drupal\system\EventSubscriber;
+
+use Drupal\Core\Access\AccessManagerInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Menu\MenuLinkTreeInterface;
+use Drupal\Core\Menu\MenuTreeParameters;
+use Drupal\Core\Routing\RouteSubscriberBase;
+use Drupal\user\Entity\User;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Alters routes, which are parents for other routes.
+ */
+class GroupRouteSubscriber extends RouteSubscriberBase {
+
+  /**
+   * The access manager.
+   *
+   * @var \Drupal\Core\Access\AccessManagerInterface
+   */
+  protected $accessManager;
+
+  /**
+   * The menu link tree service.
+   *
+   * @var \Drupal\Core\Menu\MenuLinkTreeInterface
+   */
+  protected $menuLinkTree;
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * Constructs a new GroupRouteSubscriber.
+   *
+   * @param \Drupal\Core\Access\AccessManagerInterface $access_manager
+   *   The access manager.
+   * @param \Drupal\Core\Menu\MenuLinkTreeInterface $menu_link_tree
+   *   The menu link tree service.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   */
+  public function __construct(AccessManagerInterface $access_manager, MenuLinkTreeInterface $menu_link_tree, EntityTypeManagerInterface $entity_type_manager) {
+    $this->accessManager = $access_manager;
+    $this->menuLinkTree = $menu_link_tree;
+    $this->entityTypeManager = $entity_type_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function alterRoutes(RouteCollection $collection) {
+    // Load all roles and leave only non-admin ones with necessary permission.
+    try {
+      $roles = $this->entityTypeManager->getStorage('user_role')->loadMultiple();
+      $user_storage = $this->entityTypeManager->getStorage('user');
+    }
+    catch (\Exception $e) {
+      return;
+    }
+    $access_roles = [];
+    $admin_role = '';
+    foreach ($roles as $role_id => $role) {
+      if ($role->isAdmin()) {
+        $admin_role = $role_id;
+        continue;
+      }
+      if ($role->hasPermission('access administration pages')) {
+        $access_roles[$role_id] = $user_storage->create([
+          'roles' => [
+            $role_id,
+          ],
+        ]);
+      }
+    }
+    if (empty($admin_role) || empty($access_roles)) {
+      return;
+    }
+    foreach ($collection->all() as $route_id => $route) {
+      if ($route->hasOption('_group_route')) {
+        // Add admin role in order to restrict access for other roles.
+        $route_accessible_roles = [$admin_role];
+        // Perform check.
+        $this->checkAccess($route_accessible_roles, $collection, $route_id, NULL, NULL, $access_roles);
+        $route->setRequirement('_role', (string) implode('+', $route_accessible_roles));
+      }
+    }
+  }
+
+  /**
+   * Checks route and its children.
+   *
+   * @param array $route_accessible_roles
+   *   Array with role names, which are allowed to access route.
+   * @param \Symfony\Component\Routing\RouteCollection $collection
+   *   Routes collection.
+   * @param string $route_id
+   *   Route ID.
+   * @param \Drupal\user\Entity\User $user
+   *   User object for test.
+   * @param string $role
+   *   User's role to be added in array.
+   * @param array $access_roles
+   *   Array with test users for access checking.
+   */
+  protected function checkAccess(array &$route_accessible_roles, RouteCollection $collection, $route_id, User $user = NULL, $role = NULL, array $access_roles = NULL) {
+    // Load menu links and check if route has any accessible children.
+    $parameters = new MenuTreeParameters();
+    $parameters->setRoot($route_id)->excludeRoot()->setTopLevelOnly()->onlyEnabledLinks();
+    $tree = $this->menuLinkTree->load(NULL, $parameters);
+    if (empty($tree)) {
+      if ($collection->get($route_id)->hasOption('_group_route')) {
+        return;
+      }
+      $route_accessible_roles[] = $role;
+    }
+    else {
+      foreach ($tree as $element_id => $element) {
+        $url = $element->link->getUrlObject();
+        if (!empty($access_roles)) {
+          foreach ($access_roles as $route_role => $role_user) {
+            if ($this->accessManager->checkNamedRoute($url->getRouteName(), $url->getRouteParameters(), $role_user) && !in_array($route_role, $route_accessible_roles)) {
+              // Check if it is a _group_route with inaccessible children.
+              $this->checkAccess($route_accessible_roles, $collection, $element_id, $role_user, $route_role);
+            }
+          }
+        }
+        else {
+          if ($this->accessManager->checkNamedRoute($url->getRouteName(), $url->getRouteParameters(), $user) && !in_array($role, $route_accessible_roles)) {
+            $route_accessible_roles[] = $role;
+          }
+        }
+      }
+    }
+  }
+
+}
diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml
index ffca932ba6..0d6198ec3c 100644
--- a/core/modules/system/system.routing.yml
+++ b/core/modules/system/system.routing.yml
@@ -45,6 +45,8 @@ system.admin_structure:
     _title: 'Structure'
   requirements:
     _permission: 'access administration pages'
+  options:
+    _group_route: TRUE
 
 system.admin_reports:
   path: '/admin/reports'
@@ -53,6 +55,8 @@ system.admin_reports:
     _title: 'Reports'
   requirements:
     _permission: 'access site reports'
+  options:
+    _group_route: TRUE
 
 system.admin_config_media:
   path: '/admin/config/media'
@@ -61,6 +65,8 @@ system.admin_config_media:
     _title: 'Media'
   requirements:
     _permission: 'access administration pages'
+  options:
+    _group_route: TRUE
 
 system.admin_config_services:
   path: '/admin/config/services'
@@ -69,6 +75,8 @@ system.admin_config_services:
     _title: 'Web services'
   requirements:
     _permission: 'access administration pages'
+  options:
+    _group_route: TRUE
 
 system.admin_config_development:
   path: '/admin/config/development'
@@ -77,6 +85,8 @@ system.admin_config_development:
     _title: 'Development'
   requirements:
     _permission: 'access administration pages'
+  options:
+    _group_route: TRUE
 
 system.admin_config_regional:
   path: '/admin/config/regional'
@@ -85,6 +95,8 @@ system.admin_config_regional:
     _title: 'Regional and language'
   requirements:
     _permission: 'access administration pages'
+  options:
+    _group_route: TRUE
 
 system.admin_config_search:
   path: '/admin/config/search'
@@ -93,6 +105,8 @@ system.admin_config_search:
     _title: 'Search and metadata'
   requirements:
     _permission: 'access administration pages'
+  options:
+    _group_route: TRUE
 
 system.admin_config_system:
   path: '/admin/config/system'
@@ -101,6 +115,8 @@ system.admin_config_system:
     _title: 'System'
   requirements:
     _permission: 'access administration pages'
+  options:
+    _group_route: TRUE
 
 system.admin_config_ui:
   path: '/admin/config/user-interface'
@@ -109,6 +125,8 @@ system.admin_config_ui:
     _title: 'User interface'
   requirements:
     _permission: 'access administration pages'
+  options:
+    _group_route: TRUE
 
 system.admin_config_workflow:
   path: '/admin/config/workflow'
@@ -117,6 +135,8 @@ system.admin_config_workflow:
     _title: 'Workflow'
   requirements:
     _permission: 'access administration pages'
+  options:
+    _group_route: TRUE
 
 system.admin_config_content:
   path: '/admin/config/content'
@@ -125,6 +145,8 @@ system.admin_config_content:
     _title: 'Content authoring'
   requirements:
     _permission: 'access administration pages'
+  options:
+    _group_route: TRUE
 
 system.cron:
   path: '/cron/{key}'
@@ -464,6 +486,8 @@ system.admin_config:
     _title: 'Configuration'
   requirements:
     _permission: 'access administration pages'
+  options:
+    _group_route: TRUE
 
 system.batch_page.html:
   path: '/batch'
@@ -506,6 +530,8 @@ system.admin_content:
     _title: 'Content'
   requirements:
     _permission: 'access administration pages'
+  options:
+    _group_route: TRUE
 
 system.entity_autocomplete:
   path: '/entity_reference_autocomplete/{target_type}/{selection_handler}/{selection_settings_key}'
diff --git a/core/modules/system/system.services.yml b/core/modules/system/system.services.yml
index 9db3db1a5a..3f34007e12 100644
--- a/core/modules/system/system.services.yml
+++ b/core/modules/system/system.services.yml
@@ -23,6 +23,11 @@ services:
     class: Drupal\system\EventSubscriber\AdminRouteSubscriber
     tags:
       - { name: event_subscriber }
+  system.group_route.route_subscriber:
+    class: Drupal\system\EventSubscriber\GroupRouteSubscriber
+    arguments: ['@access_manager', '@menu.link_tree', '@entity_type.manager']
+    tags:
+      - { name: event_subscriber }
   theme.negotiator.system.batch:
     class: Drupal\system\Theme\BatchNegotiator
     arguments: ['@batch.storage', '@request_stack']
diff --git a/core/modules/system/tests/src/Functional/Menu/MenuAccessTest.php b/core/modules/system/tests/src/Functional/Menu/MenuAccessTest.php
index 57415a37d3..f72f306cf9 100644
--- a/core/modules/system/tests/src/Functional/Menu/MenuAccessTest.php
+++ b/core/modules/system/tests/src/Functional/Menu/MenuAccessTest.php
@@ -17,7 +17,7 @@ class MenuAccessTest extends BrowserTestBase {
    *
    * @var array
    */
-  protected static $modules = ['block', 'menu_test'];
+  protected static $modules = ['block', 'menu_test', 'toolbar'];
 
   /**
    * {@inheritdoc}
@@ -70,4 +70,51 @@ public function testMenuBlockLinksAccessCheck() {
     $this->assertSession()->linkByHrefNotExists('foo/asdf/c');
   }
 
+  /**
+   * Tests menu access as per user role permissions.
+   */
+  public function testMenuAccessByUserRole() {
+    // Create an admin user and login.
+    $this->adminUser = $this->drupalCreateUser([
+      'administer site configuration',
+      'access administration pages',
+      'administer permissions',
+      'administer users',
+    ]);
+    $this->drupalLogin($this->adminUser);
+
+    // Create a test role.
+    $edit = [
+      'label' => 'Test role',
+      'id' => 'test_role',
+    ];
+    $this->drupalGet('admin/people/roles/add');
+    $this->submitForm($edit, 'Save');
+    $this->assertSession()->statusCodeEquals(200);
+
+    // Give access to toolbar, theme and admin pages.
+    $edit = [
+      'test_role[access administration pages]' => TRUE,
+      'test_role[view the administration theme]' => TRUE,
+      'test_role[access toolbar]' => TRUE,
+    ];
+    $this->drupalGet('admin/people/permissions/test_role');
+    $this->submitForm($edit, 'Save permissions');
+    $this->assertSession()->statusCodeEquals(200);
+
+    // Create a user with the role created above and login with this user.
+    $webUser = $this->drupalCreateUser();
+    $edit = [
+      'roles[test_role]' => 'test_role',
+    ];
+    $this->drupalGet('user/' . $webUser->id() . '/edit');
+    $this->submitForm($edit, 'Save');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->drupalLogout();
+    $this->drupalLogin($webUser);
+
+    // Verify that the structure menu item is not present in the toolbar.
+    $this->assertSession()->responseNotContains('class="toolbar-icon toolbar-icon-system-admin-structure"');
+  }
+
 }
