diff --git a/core/modules/migrate/config/schema/migrate.schema.yml b/core/modules/migrate/config/schema/migrate.schema.yml
index c48bdd1..d6cecd20 100644
--- a/core/modules/migrate/config/schema/migrate.schema.yml
+++ b/core/modules/migrate/config/schema/migrate.schema.yml
@@ -13,6 +13,9 @@ migrate.migration.*:
       sequence:
         type: string
         label: 'Group'
+    active_group:
+      type: string
+      label: 'Active group'
     label:
       type: label
       label: 'Label'
@@ -44,3 +47,20 @@ migrate.migration.*:
           sequence:
             type: string
             label: 'Dependency'
+
+migrate.migration_group.*:
+  type: config_entity
+  label: 'Migration Group'
+  mapping:
+    id:
+      type: string
+      label: 'ID'
+    label:
+      type: label
+      label: 'Label'
+    module:
+      type: string
+      label: 'Dependent module'
+    shared_configuration:
+      type: sequence
+      label: 'Shared migration configuration'
diff --git a/core/modules/migrate/src/Entity/Migration.php b/core/modules/migrate/src/Entity/Migration.php
index 42d9a7a..cbe81c2 100644
--- a/core/modules/migrate/src/Entity/Migration.php
+++ b/core/modules/migrate/src/Entity/Migration.php
@@ -212,6 +212,32 @@ class Migration extends ConfigEntityBase implements MigrationInterface, Requirem
   protected $entityManager;
 
   /**
+   * Overrides ConfigEntityBase::__construct().
+   */
+  public function __construct(array $values, $entity_type) {
+    parent::__construct($values, $entity_type);
+
+    // If we are pointing to a valid group, merge its properties into ours.
+    if (isset($this->active_group)) {
+      /** @var MigrationGroupInterface $group */
+      $group = \Drupal::entityManager()->getStorage('migration_group')->load($this->active_group);
+      if ($group) {
+        $shared_configuration = $group->get('shared_configuration');
+        foreach ($shared_configuration as $key => $group_value) {
+          $migration_value = $this->get($key);
+          if (is_array($migration_value) && is_array($group_value)) {
+            $merged_values = array_merge_recursive($group_value, $migration_value);
+            $this->set($key, $merged_values);
+          }
+          elseif (is_null($migration_value)) {
+            $this->set($key, $group_value);
+          }
+        }
+      }
+    }
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function getSourcePlugin() {
diff --git a/core/modules/migrate/src/Entity/MigrationGroup.php b/core/modules/migrate/src/Entity/MigrationGroup.php
new file mode 100644
index 0000000..d51f0b8
--- /dev/null
+++ b/core/modules/migrate/src/Entity/MigrationGroup.php
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\migrate\Entity\MigrationGroup.
+ */
+
+namespace Drupal\migrate\Entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+
+/**
+ * Defines the Migration Group entity.
+ *
+ * The migration group entity is used to group active migrations, as well as to
+ * store shared migration configuration.
+ *
+ * @ConfigEntityType(
+ *   id = "migration_group",
+ *   label = @Translation("Migration Group"),
+ *   module = "migrate",
+ *   handlers = {
+ *   },
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "label" = "label",
+ *     "weight" = "weight"
+ *   }
+ * )
+ */
+class MigrationGroup extends ConfigEntityBase implements MigrationGroupInterface {
+
+  /**
+   * The migration group ID (machine name).
+   *
+   * @var string
+   */
+  protected $id;
+
+  /**
+   * The human-readable label for the migration group.
+   *
+   * @var string
+   */
+  protected $label;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function delete() {
+    // Delete all migrations contained in this group.
+    $query = \Drupal::entityQuery('migration')
+      ->condition('active_group', $this->id());
+    $names = $query->execute();
+
+    // Order the migrations according to their dependencies.
+    /** @var MigrationInterface[] $migrations */
+    $migrations = \Drupal::entityManager()->getStorage('migration')->loadMultiple($names);
+
+    // Delete in reverse order, so dependencies are never violated.
+    $migrations = array_reverse($migrations);
+
+    foreach ($migrations as $migration) {
+      $migration->delete();
+    }
+
+    // Finally, delete the group itself.
+    parent::delete();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function calculateDependencies() {
+    parent::calculateDependencies();
+    if ($provider = $this->get('module')) {
+      $this->addDependency('module', $provider);
+    }
+    return $this->dependencies;
+  }
+
+}
diff --git a/core/modules/migrate/src/Entity/MigrationGroupInterface.php b/core/modules/migrate/src/Entity/MigrationGroupInterface.php
new file mode 100644
index 0000000..7461477
--- /dev/null
+++ b/core/modules/migrate/src/Entity/MigrationGroupInterface.php
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\migrate\Entity\MigrationGroupInterface.
+ */
+
+namespace Drupal\migrate\Entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityInterface;
+
+/**
+ * Interface for migration groups.
+ */
+interface MigrationGroupInterface extends ConfigEntityInterface {
+
+}
