diff --git a/core/config/install/core.extension.yml b/core/config/install/core.extension.yml
index ce74bae..ff9a4ba 100644
--- a/core/config/install/core.extension.yml
+++ b/core/config/install/core.extension.yml
@@ -1,2 +1,3 @@
 module: {}
 theme: {}
+versions: {}
diff --git a/core/config/schema/core.extension.schema.yml b/core/config/schema/core.extension.schema.yml
index bdc84b9..7bf3749 100644
--- a/core/config/schema/core.extension.schema.yml
+++ b/core/config/schema/core.extension.schema.yml
@@ -14,3 +14,16 @@ core.extension:
       sequence:
         type: integer
         label: 'Weight'
+    versions:
+      type: sequence
+      label: 'Version information'
+      sequence:
+        type: mapping
+        label: 'Extensions'
+        mapping:
+          current:
+            type: string
+            label: 'Currently installed version'
+          schema:
+            type: string
+            label: 'Currently installed schema version'
diff --git a/core/includes/schema.inc b/core/includes/schema.inc
index 4712d3b..bf2b300 100644
--- a/core/includes/schema.inc
+++ b/core/includes/schema.inc
@@ -104,6 +104,12 @@ function drupal_get_installed_schema_version($module, $reset = FALSE, $array = F
  */
 function drupal_set_installed_schema_version($module, $version) {
   \Drupal::keyValue('system.schema')->set($module, $version);
+  // Store the information in config so that we can ensure that database updates
+  // are the same between the source and the target site instances.
+  \Drupal::configFactory()
+    ->getEditable('core.extension')
+    ->set("versions.$module.schema", (string) $version)
+    ->save();
   // Reset the static cache of module schema versions.
   drupal_get_installed_schema_version(NULL, TRUE);
 }
diff --git a/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php
index 6a69082..18ead25 100644
--- a/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php
@@ -72,14 +72,33 @@ public function onConfigImporterValidate(ConfigImporterEvent $event) {
       }
     }
     $config_importer = $event->getConfigImporter();
-    if ($config_importer->getStorageComparer()->getSourceStorage()->exists('core.extension')) {
+    if ($this->validateCoreExtension($config_importer)) {
       $this->validateModules($config_importer);
       $this->validateThemes($config_importer);
       $this->validateDependencies($config_importer);
     }
-    else {
+  }
+
+  /**
+   * Validates the core.extension configuration file.
+   *
+   * @param \Drupal\Core\Config\ConfigImporter $config_importer
+   *   The configuration importer.
+   *
+   * @return bool
+   *   TRUE if the core.extension is valid, FALSE if not.
+   */
+  protected function validateCoreExtension(ConfigImporter $config_importer) {
+    $data = $config_importer->getStorageComparer()->getSourceStorage()->read('core.extension');
+    if ($data === FALSE) {
       $config_importer->logError($this->t('The core.extension configuration does not exist.'));
+      return FALSE;
     }
+    elseif (empty($data['versions'])) {
+      $config_importer->logError($this->t('The core.extension file is out-of-date. Update the codebase of the source site, run update.php and export the configuration again.'));
+      return FALSE;
+    }
+    return TRUE;
   }
 
   /**
@@ -97,6 +116,25 @@ protected function validateModules(ConfigImporter $config_importer) {
       $config_importer->logError($this->t('Unable to install the %module module since it does not exist.', array('%module' => $module)));
     }
 
+    // Ensure that schema and version of the existing modules that produced the
+    // source configuration match the site.
+    $existing_modules = array_keys(array_intersect_key($core_extension['module'], $config_importer->getStorageComparer()->getTargetStorage()->read('core.extension')['module']));
+    foreach ($existing_modules as $module) {
+      if ($core_extension['versions'][$module]['current'] !== $module_data[$module]->info['version']) {
+        $config_importer->logError($this->t(
+          'The version of %module module on the source site (@source_version) does not match this site (@target_version).',
+          ['%module' => $module, '@source_version' => $core_extension['versions'][$module]['current'], '@target_version' => $module_data[$module]->info['version']]
+        ));
+      }
+      elseif ($core_extension['versions'][$module]['schema'] !== (string) drupal_get_installed_schema_version($module)) {
+        $config_importer->logError($this->t(
+          'The schema version of %module module on the source site (@source_schema) does not match this site (@target_schema).',
+          ['%module' => $module, '@source_schema' => $core_extension['versions'][$module]['schema'], '@target_schema' => drupal_get_installed_schema_version($module)]
+        ));
+
+      }
+    }
+
     // Ensure that all modules being installed have their dependencies met.
     $installs = $config_importer->getExtensionChangelist('module', 'install');
     foreach ($installs as $module) {
@@ -115,6 +153,12 @@ protected function validateModules(ConfigImporter $config_importer) {
         );
         $config_importer->logError($message);
       }
+      if ($core_extension['versions'][$module]['current'] !== $module_data[$module]->info['version']) {
+        $config_importer->logError($this->t(
+          'Unable to install the %module module since the installed version (@config_version) does not match the code base (@code_version).',
+          ['%module' => $module, '@config_version' => $core_extension['versions'][$module]['current'], '@code_version' => $module_data[$module]->info['version']]
+        ));
+      }
     }
 
     // Settings is safe to use because settings.php is written before any module
@@ -160,6 +204,18 @@ protected function validateThemes(ConfigImporter $config_importer) {
       }
     }
 
+    // Ensure that version of the existing themes that produced the source
+    // configuration match the site.
+    $existing_themes = array_keys(array_intersect_key($core_extension['theme'], $config_importer->getStorageComparer()->getTargetStorage()->read('core.extension')['theme']));
+    foreach ($existing_themes as $theme) {
+      if ($core_extension['versions'][$theme]['current'] !== $theme_data[$theme]->info['version']) {
+        $config_importer->logError($this->t(
+          'The version of %theme theme on the source site (@source_version) does not match this site (@target_version).',
+          ['%theme' => $theme, '@source_version' => $core_extension['versions'][$theme]['current'], '@target_version' => $theme_data[$theme]->info['version']]
+        ));
+      }
+    }
+
     // Ensure that all themes being installed have their dependencies met.
     foreach ($installs as $theme) {
       foreach (array_keys($theme_data[$theme]->requires) as $required_theme) {
@@ -169,6 +225,12 @@ protected function validateThemes(ConfigImporter $config_importer) {
           $config_importer->logError($this->t('Unable to install the %theme theme since it requires the %required_theme theme.', array('%theme' => $theme_name, '%required_theme' => $required_theme_name)));
         }
       }
+      if ($core_extension['versions'][$theme]['current'] !== $theme_data[$theme]->info['version']) {
+        $config_importer->logError($this->t(
+          'Unable to install the %theme theme since the installed version (@config_version) does not match the code base (@code_version).',
+          ['%theme' => $theme, '@config_version' => $core_extension['versions'][$theme]['current'], '@code_version' => $theme_data[$theme]->info['version']]
+        ));
+      }
     }
 
     // Ensure that all themes being uninstalled are not required by themes that
diff --git a/core/lib/Drupal/Core/Extension/InfoParserDynamic.php b/core/lib/Drupal/Core/Extension/InfoParserDynamic.php
index 64e4941..0ea95f0 100644
--- a/core/lib/Drupal/Core/Extension/InfoParserDynamic.php
+++ b/core/lib/Drupal/Core/Extension/InfoParserDynamic.php
@@ -36,6 +36,9 @@ public function parse($filename) {
       if (isset($parsed_info['version']) && $parsed_info['version'] === 'VERSION') {
         $parsed_info['version'] = \Drupal::VERSION;
       }
+      if (!isset($parsed_info['version'])) {
+        $parsed_info['version'] = 'None';
+      }
     }
     return $parsed_info;
   }
diff --git a/core/lib/Drupal/Core/Extension/ModuleInstaller.php b/core/lib/Drupal/Core/Extension/ModuleInstaller.php
index f8300cb..5ab9c85 100644
--- a/core/lib/Drupal/Core/Extension/ModuleInstaller.php
+++ b/core/lib/Drupal/Core/Extension/ModuleInstaller.php
@@ -294,6 +294,20 @@ public function install(array $module_list, $enable_dependencies = TRUE) {
 
     // If any modules were newly installed, invoke hook_modules_installed().
     if (!empty($modules_installed)) {
+      // There will be a new config factory.
+      $extension_config = \Drupal::configFactory()->getEditable('core.extension');
+      // Update version information in configuration.
+      $list = $this->moduleHandler->getModuleList();
+      $versions = $extension_config->get('versions');
+      foreach ($modules_installed as $module_name) {
+        $info = \Drupal::service('info_parser')->parse($list[$module_name]->getPathname());
+        $versions[$module_name] = [
+          'current' => $info['version'],
+          'schema' => (string) drupal_get_installed_schema_version($module_name),
+        ];
+      }
+      ksort($versions);
+      $extension_config->set('versions', $versions)->save();
       \Drupal::service('router.builder')->setRebuildNeeded();
       $this->moduleHandler->invokeAll('modules_installed', array($modules_installed));
     }
@@ -414,7 +428,11 @@ public function uninstall(array $module_list, $uninstall_dependents = TRUE) {
 
       // Remove the module's entry from the config. Don't check schema when
       // uninstalling a module since we are only clearing a key.
-      \Drupal::configFactory()->getEditable('core.extension')->clear("module.$module")->save(TRUE);
+      \Drupal::configFactory()
+        ->getEditable('core.extension')
+        ->clear("module.$module")
+        ->clear("versions.$module")
+        ->save(TRUE);
 
       // Update the module handler to remove the module.
       // The current ModuleHandler instance is obsolete with the kernel rebuild
diff --git a/core/lib/Drupal/Core/Extension/ThemeInstaller.php b/core/lib/Drupal/Core/Extension/ThemeInstaller.php
index b3b391e..5bae419 100644
--- a/core/lib/Drupal/Core/Extension/ThemeInstaller.php
+++ b/core/lib/Drupal/Core/Extension/ThemeInstaller.php
@@ -170,8 +170,14 @@ public function install(array $theme_list, $install_dependencies = TRUE) {
 
       // The value is not used; the weight is ignored for themes currently. Do
       // not check schema when saving the configuration.
+      $versions = $extension_config->get('versions');
+      $versions[$key] = [
+        'current' => $theme_data[$key]->info['version'],
+      ];
+      ksort($versions);
       $extension_config
         ->set("theme.$key", 0)
+        ->set('versions', $versions)
         ->save(TRUE);
 
       // Add the theme to the current list.
@@ -248,7 +254,9 @@ public function uninstall(array $theme_list) {
     $current_theme_data = $this->state->get('system.theme.data', array());
     foreach ($theme_list as $key) {
       // The value is not used; the weight is ignored for themes currently.
-      $extension_config->clear("theme.$key");
+      $extension_config
+        ->clear("theme.$key")
+        ->clear("versions.$key");
 
       // Update the current theme data accordingly.
       unset($current_theme_data[$key]);
diff --git a/core/modules/config/src/Tests/ConfigImportInstallProfileTest.php b/core/modules/config/src/Tests/ConfigImportInstallProfileTest.php
index e15d3ac..4f8553a 100644
--- a/core/modules/config/src/Tests/ConfigImportInstallProfileTest.php
+++ b/core/modules/config/src/Tests/ConfigImportInstallProfileTest.php
@@ -67,8 +67,13 @@ public function testInstallProfileValidation() {
     $core['module']['testing_config_import'] = 0;
     unset($core['module']['syslog']);
     unset($core['theme']['stark']);
+    unset($core['versions']['syslog']);
+    unset($core['versions']['stark']);
     $core['theme']['stable'] = 0;
     $core['theme']['classy'] = 0;
+    $core['versions']['stable'] = ['current' => \Drupal::VERSION];
+    $core['versions']['classy'] = ['current' => \Drupal::VERSION];
+    ksort($core['versions']);
     $sync->write('core.extension', $core);
     $sync->deleteAll('syslog.');
     $theme = $sync->read('system.theme');
diff --git a/core/modules/config/src/Tests/ConfigImportUITest.php b/core/modules/config/src/Tests/ConfigImportUITest.php
index 98f11c2..7c64dbf 100644
--- a/core/modules/config/src/Tests/ConfigImportUITest.php
+++ b/core/modules/config/src/Tests/ConfigImportUITest.php
@@ -84,9 +84,14 @@ function testImport() {
     $core_extension['module']['action'] = 0;
     $core_extension['module']['ban'] = 0;
     $core_extension['module'] = module_config_sort($core_extension['module']);
+    $core_extension['versions']['action'] = ['current' => \Drupal::VERSION, 'schema' => '8000'];
+    $core_extension['versions']['ban'] = ['current' => \Drupal::VERSION, 'schema' => '8000'];
     // Bartik is a subtheme of classy so classy must be enabled.
     $core_extension['theme']['classy'] = 0;
     $core_extension['theme']['bartik'] = 0;
+    $core_extension['versions']['classy'] = ['current' => \Drupal::VERSION];
+    $core_extension['versions']['bartik'] = ['current' => \Drupal::VERSION];
+    ksort($core_extension['versions']);
     $sync->write('core.extension', $core_extension);
 
     // Use the install storage so that we can read configuration from modules
@@ -175,6 +180,11 @@ function testImport() {
     unset($core_extension['module']['options']);
     unset($core_extension['module']['text']);
     unset($core_extension['theme']['bartik']);
+    unset($core_extension['versions']['action']);
+    unset($core_extension['versions']['ban']);
+    unset($core_extension['versions']['options']);
+    unset($core_extension['versions']['text']);
+    unset($core_extension['versions']['bartik']);
     $sync->write('core.extension', $core_extension);
     $sync->delete('action.settings');
     $sync->delete('text.settings');
@@ -511,6 +521,16 @@ public function testExtensionValidation() {
     $core['module']['does_not_exist'] = 0;
     // This theme does not exist.
     $core['theme']['does_not_exist'] = 0;
+    // This module does exist but the version is wrong.
+    $core['module']['ban'] = 0;
+    $core['versions']['ban'] = ['current' => '8.0.0-rc3', 'schema' => '8000'];
+    // This theme does exist but the version is wrong.
+    $core['theme']['seven'] = 0;
+    $core['versions']['seven'] = ['current' => '8.0.0-rc3'];
+    $core['versions']['bartik']['current'] = '8.0.0-rc3';
+    $core['versions']['text']['current'] = '8.0.0-rc3';
+    $core['versions']['config_test']['schema'] = '8999';
+
     $sync->write('core.extension', $core);
 
     $this->drupalPostForm('admin/config/development/configuration', array(), t('Import all'));
@@ -519,6 +539,10 @@ public function testExtensionValidation() {
     $this->assertText('Unable to uninstall the Classy theme since the Bartik theme is installed.');
     $this->assertText('Unable to install the does_not_exist module since it does not exist.');
     $this->assertText('Unable to install the does_not_exist theme since it does not exist.');
+    $this->assertText('Unable to install the ban module since the installed version (8.0.0-rc3) does not match the code base (' . \Drupal::VERSION . ').');
+    $this->assertText('Unable to install the seven theme since the installed version (8.0.0-rc3) does not match the code base (' . \Drupal::VERSION . ').');
+    $this->assertText('The schema version of config_test module on the source site (8999) does not match this site (' . drupal_get_installed_schema_version('config_test') . ').');
+    $this->assertText('The version of bartik theme on the source site (8.0.0-rc3) does not match this site (' . \Drupal::VERSION . ').');
   }
 
 }
diff --git a/core/modules/config/src/Tests/ConfigImporterTest.php b/core/modules/config/src/Tests/ConfigImporterTest.php
index 3fca8c0..57af7d7 100644
--- a/core/modules/config/src/Tests/ConfigImporterTest.php
+++ b/core/modules/config/src/Tests/ConfigImporterTest.php
@@ -578,6 +578,8 @@ public function testUnmetDependency() {
     // Add a module and a theme that depend on uninstalled extensions.
     $extensions['module']['book'] = 0;
     $extensions['theme']['bartik'] = 0;
+    $extensions['versions']['book'] = ['current' => \Drupal::VERSION];
+    $extensions['versions']['bartik'] = ['current' => \Drupal::VERSION];
 
     $sync->write('core.extension', $extensions);
     try {
@@ -649,6 +651,58 @@ public function testMissingCoreExtension() {
   }
 
   /**
+   * Tests missing core.extension:versions during configuration import.
+   *
+   * Configuration exported before the versions information was added should
+   * fail.
+   *
+   * @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber
+   */
+  public function testMissingVersionInformation() {
+    $sync = $this->container->get('config.storage.sync');
+    $data = $sync->read('core.extension');
+    unset($data['versions']);
+    $sync->write('core.extension', $data);
+    try {
+      $this->configImporter->reset()->import();
+      $this->fail('ConfigImporterException not thrown, invalid import was not stopped due to missing version information.');
+    }
+    catch (ConfigImporterException $e) {
+      $this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.');
+      $error_log = $this->configImporter->getErrors();
+      $this->assertEqual(['The core.extension file is out-of-date. Update the codebase of the source site, run update.php and export the configuration again.'], $error_log);
+    }
+  }
+
+  /**
+   * Tests missing core.extension:versions during configuration import.
+   *
+   * Configuration exported before the versions information was added should
+   * fail.
+   *
+   * @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber
+   */
+  public function testVersionMismatch() {
+    $sync = $this->container->get('config.storage.sync');
+    $data = $sync->read('core.extension');
+    $data['versions']['system']['current'] = '8.0.0-beta1';
+    $data['versions']['config_test']['schema'] = '8999';
+    $sync->write('core.extension', $data);
+    try {
+      $this->configImporter->reset()->import();
+      $this->fail('ConfigImporterException not thrown, invalid import was not stopped due to missing version information.');
+    }
+    catch (ConfigImporterException $e) {
+      $this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.');
+      $error_log = $this->configImporter->getErrors();
+      $this->assertEqual([
+        'The schema version of <em class="placeholder">config_test</em> module on the source site (8999) does not match this site (-1).',
+        'The version of <em class="placeholder">system</em> module on the source site (8.0.0-beta1) does not match this site (' . \Drupal::VERSION . ').'
+      ], $error_log);
+    }
+  }
+
+  /**
    * Tests install profile validation during configuration import.
    *
    * @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber
diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php
index 10463d9..848d5df 100644
--- a/core/modules/simpletest/src/KernelTestBase.php
+++ b/core/modules/simpletest/src/KernelTestBase.php
@@ -236,7 +236,13 @@ protected function setUp() {
     // \Drupal\Core\Config\ConfigInstaller::installDefaultConfig() to work.
     // Write directly to active storage to avoid early instantiation of
     // the event dispatcher which can prevent modules from registering events.
-    \Drupal::service('config.storage')->write('core.extension', array('module' => array(), 'theme' => array()));
+    \Drupal::service('config.storage')->write('core.extension',
+      [
+        'module' => [],
+        'theme' => [],
+        'versions' => [],
+      ]
+    );
 
     // Collect and set a fixed module list.
     $class = get_class($this);
@@ -517,6 +523,11 @@ protected function enableModules(array $modules) {
       $module_handler->addModule($module, $module_list[$module]->getPath());
       // Maintain the list of enabled modules in configuration.
       $extensions['module'][$module] = 0;
+      $version = \Drupal::service('info_parser')->parse($module_list[$module]->getPathname())['version'];
+      $extensions['versions'][$module] = [
+        'current' => $version,
+        'schema' => (string) SCHEMA_UNINSTALLED,
+      ];
     }
     $active_storage->write('core.extension', $extensions);
 
diff --git a/core/modules/simpletest/src/Tests/KernelTestBaseTest.php b/core/modules/simpletest/src/Tests/KernelTestBaseTest.php
index 2187ccc..240683d 100644
--- a/core/modules/simpletest/src/Tests/KernelTestBaseTest.php
+++ b/core/modules/simpletest/src/Tests/KernelTestBaseTest.php
@@ -335,6 +335,18 @@ public function testDrupalGetProfile() {
   }
 
   /**
+   * @covers ::enableModules
+   */
+  public function testEnableModules() {
+    $this->assertFalse(\Drupal::moduleHandler()->moduleExists('system'));
+    $this->enableModules(['system', 'user']);
+    $core = \Drupal::config('core.extension');
+    $this->assertEqual(['entity_test' => 0, 'user' => 0, 'system' => 0], $core->get('module'));
+    $this->assertEqual(['current' => \Drupal::VERSION, 'schema' => (string) SCHEMA_UNINSTALLED], $core->get('versions.user'));
+    $this->assertTrue(\Drupal::moduleHandler()->moduleExists('system'));
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function run(array $methods = array()) {
diff --git a/core/modules/system/src/Controller/DbUpdateController.php b/core/modules/system/src/Controller/DbUpdateController.php
index 0239a41..c865675 100644
--- a/core/modules/system/src/Controller/DbUpdateController.php
+++ b/core/modules/system/src/Controller/DbUpdateController.php
@@ -653,6 +653,17 @@ public static function batchFinished($success, $results, $operations) {
     // No updates to run, so caches won't get flushed later.  Clear them now.
     drupal_flush_all_caches();
 
+    // Update core.extensions to have the correct version information.
+    $core = \Drupal::configFactory()->getEditable('core.extension');
+    foreach (\Drupal::moduleHandler()->getModuleList() as $module => $extension) {
+      $version = \Drupal::service('info_parser')->parse($extension->getPathname())['version'];
+      $core->set("versions.$module.current", $version);
+    }
+    foreach (\Drupal::service('theme_handler')->listInfo() as $theme => $extension) {
+      $version = \Drupal::service('info_parser')->parse($extension->getPathname())['version'];
+      $core->set("versions.$theme.current", $version);
+    }
+
     $_SESSION['update_results'] = $results;
     $_SESSION['update_success'] = $success;
     $_SESSION['updates_remaining'] = $operations;
diff --git a/core/modules/system/src/Tests/Extension/ThemeInstallerTest.php b/core/modules/system/src/Tests/Extension/ThemeInstallerTest.php
index 6fe81f7..b6b994c 100644
--- a/core/modules/system/src/Tests/Extension/ThemeInstallerTest.php
+++ b/core/modules/system/src/Tests/Extension/ThemeInstallerTest.php
@@ -269,6 +269,7 @@ function testUninstall() {
 
     $this->themeInstaller()->install(array($name));
     $this->assertTrue($this->config("$name.settings")->get());
+    $this->assertNotNull($this->config('core.extension')->get('versions.test_basetheme'), 'The "test_basetheme" version information is present after install.');
 
     $this->themeInstaller()->uninstall(array($name));
 
@@ -276,6 +277,7 @@ function testUninstall() {
     $this->assertFalse(array_keys(system_list('theme')));
 
     $this->assertFalse($this->config("$name.settings")->get());
+    $this->assertNull($this->config('core.extension')->get('versions.test_basetheme'), 'The "test_basetheme" version information is not present after uninstall.');
 
     // Ensure that the uninstalled theme can be installed again.
     $this->themeInstaller()->install(array($name));
diff --git a/core/modules/system/src/Tests/Module/UninstallTest.php b/core/modules/system/src/Tests/Module/UninstallTest.php
index 253ea18..de8cd2d 100644
--- a/core/modules/system/src/Tests/Module/UninstallTest.php
+++ b/core/modules/system/src/Tests/Module/UninstallTest.php
@@ -66,6 +66,10 @@ function testUninstallPage() {
     // Delete the node to allow node to be uninstalled.
     $node->delete();
 
+    // Ensure the module information is present in core.extension before
+    // uninstall.
+    $this->assertNotNull($this->config('core.extension')->get('versions.module_test'), 'The "module_test" version information is present.');
+
     // Uninstall module_test.
     $edit = array();
     $edit['uninstall[module_test]'] = TRUE;
@@ -76,6 +80,10 @@ function testUninstallPage() {
     $this->drupalPostForm(NULL, NULL, t('Uninstall'));
     $this->assertText(t('The selected modules have been uninstalled.'), 'Modules status has been updated.');
 
+    // Ensure the module information has been removed from core.extension after
+    // uninstall.
+    $this->assertNull($this->config('core.extension')->get('versions.module_test'), 'The "module_test" version information has been removed.');
+
     // Uninstall node testing that the configuration that will be deleted is
     // listed.
     $node_dependencies = \Drupal::service('config.manager')->findConfigEntityDependentsAsEntities('module', array('node'));
diff --git a/core/modules/system/src/Tests/Update/UpdateScriptTest.php b/core/modules/system/src/Tests/Update/UpdateScriptTest.php
index cba80ca..8bf914b 100644
--- a/core/modules/system/src/Tests/Update/UpdateScriptTest.php
+++ b/core/modules/system/src/Tests/Update/UpdateScriptTest.php
@@ -182,11 +182,13 @@ function testSuccessfulUpdateFunctionality() {
     // Reset the static cache to ensure we have the most current setting.
     $schema_version = drupal_get_installed_schema_version('update_script_test', TRUE);
     $this->assertEqual($schema_version, 8001, 'update_script_test schema version is 8001 after updating.');
+    $this->assertIdentical('8001', $this->config('core.extension')->get('versions.update_script_test.schema'), 'update_script_test schema version is 8001 in core.extension config after updating.');
 
     // Set the installed schema version to one less than the current update.
     drupal_set_installed_schema_version('update_script_test', $schema_version - 1);
     $schema_version = drupal_get_installed_schema_version('update_script_test', TRUE);
     $this->assertEqual($schema_version, 8000, 'update_script_test schema version overridden to 8000.');
+    $this->assertIdentical('8000', $this->config('core.extension')->get('versions.update_script_test.schema'), 'update_script_test schema version is overridden to 8000 in core.extension config.');
 
     // Click through update.php with 'access administration pages' and
     // 'access site reports' permissions.
diff --git a/core/modules/system/src/Tests/Update/VersionInfoUpdateTest.php b/core/modules/system/src/Tests/Update/VersionInfoUpdateTest.php
new file mode 100644
index 0000000..17132e4
--- /dev/null
+++ b/core/modules/system/src/Tests/Update/VersionInfoUpdateTest.php
@@ -0,0 +1,60 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Update\VersionInfoUpdateTest.
+ */
+
+namespace Drupal\system\Tests\Update;
+
+/**
+ * Tests the upgrade path for adding version information to core.extension.
+ *
+ * @see system_update_8015()
+ *
+ * @group Update
+ */
+class VersionInfoUpdateTest extends UpdatePathTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setDatabaseDumpFiles() {
+    $this->databaseDumpFiles = [
+      __DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
+    ];
+  }
+
+  /**
+   * Tests that core.extension:versions is updated properly.
+   */
+  public function testSystemUpdate8015() {
+    // Set a partial value to ensure that regardless everything is updated
+    // correctly.
+    $this->config('core.extension')->set('versions.user.current', '8.0.0')->save();
+    $this->runUpdates();
+
+    // Test that a module is added.
+    $this->assertEqual([
+      'current' => \Drupal::VERSION,
+      'schema' => drupal_get_installed_schema_version('system'),
+    ], $this->config('core.extension')->get('versions.system'));
+    // Test that a profile is added.
+    $this->assertEqual([
+      'current' => \Drupal::VERSION,
+      'schema' => drupal_get_installed_schema_version('standard'),
+    ], $this->config('core.extension')->get('versions.standard'));
+    // Test that a theme is added.
+    $this->assertEqual([
+      'current' => \Drupal::VERSION,
+    ], $this->config('core.extension')->get('versions.bartik'));
+
+    // Test that any existing version information is merged correctly.
+    $this->assertEqual([
+      'current' => \Drupal::VERSION,
+      'schema' => drupal_get_installed_schema_version('user'),
+    ], $this->config('core.extension')->get('versions.user'));
+
+  }
+
+}
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 169921e..3bcfa85 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -10,6 +10,7 @@
 use Drupal\Core\Url;
 use Drupal\Core\Database\Database;
 use Drupal\Core\DrupalKernel;
+use Drupal\Core\Extension\Extension;
 use Drupal\Core\Site\Settings;
 use Drupal\Core\StreamWrapper\PrivateStream;
 use Drupal\Core\StreamWrapper\PublicStream;
@@ -1884,3 +1885,48 @@ function system_update_8014() {
 /**
  * @} End of "addtogroup updates-8.0.0-rc".
  */
+
+/**
+ * @addtogroup updates-8.1.0
+ * @{
+ */
+
+/**
+ * Add the version information to core.extension
+ */
+function system_update_8015() {
+  // Update core.extensions to have the correct version information.
+  $core = \Drupal::configFactory()->getEditable('core.extension');
+  $versions = $core->get('versions') ?: [];
+  $info_parser = \Drupal::service('info_parser');
+
+  // Create a method to populate the version information for an array of
+  // \Drupal\Core\Extension\Extension objects.
+  $map_version_info = function (Extension $extension) use ($info_parser) {
+    // Read from disk to avoid using APIs.
+    $version = $info_parser->parse($extension->getPathname())['version'];
+    $return = [
+      'current' => $version,
+    ];
+    // Modules and profiles also stored the schema version.
+    if ($extension->getType() === 'module' || $extension->getType() === 'profile') {
+      $return['schema'] = (string) drupal_get_installed_schema_version($extension->getName());
+    }
+    return $return;
+  };
+
+  // It is possible that version information already exists. For example,
+  // another hook_update_N hook could have installed a module. Do not overwrite
+  // existing data.
+  $versions = \Drupal\Component\Utility\NestedArray::mergeDeep(
+    array_map($map_version_info, \Drupal::moduleHandler()->getModuleList()),
+    array_map($map_version_info, \Drupal::service('theme_handler')->listInfo()),
+    $versions
+  );
+  ksort($versions);
+  $core->set('versions', $versions)->save();
+}
+
+/**
+ * @} End of "addtogroup updates-8.1.0".
+ */
diff --git a/core/tests/Drupal/KernelTests/KernelTestBase.php b/core/tests/Drupal/KernelTests/KernelTestBase.php
index f1a1414..b9c633f 100644
--- a/core/tests/Drupal/KernelTests/KernelTestBase.php
+++ b/core/tests/Drupal/KernelTests/KernelTestBase.php
@@ -835,6 +835,11 @@ protected function enableModules(array $modules) {
       $module_handler->addModule($module, $module_list[$module]->getPath());
       // Maintain the list of enabled modules in configuration.
       $extension_config['module'][$module] = 0;
+      $version = \Drupal::service('info_parser')->parse($module_list[$module]->getPathname())['version'];
+      $extension_config['versions'][$module] = [
+        'current' => $version,
+        'schema' => (string) SCHEMA_UNINSTALLED,
+      ];
     }
     $active_storage->write('core.extension', $extension_config);
 
diff --git a/core/tests/Drupal/KernelTests/KernelTestBaseTest.php b/core/tests/Drupal/KernelTests/KernelTestBaseTest.php
index 188d111..8585726 100644
--- a/core/tests/Drupal/KernelTests/KernelTestBaseTest.php
+++ b/core/tests/Drupal/KernelTests/KernelTestBaseTest.php
@@ -211,6 +211,18 @@ public function testRenderWithTheme() {
   }
 
   /**
+   * @covers ::enableModules
+   */
+  public function testEnableModules() {
+    $this->assertFalse(\Drupal::moduleHandler()->moduleExists('system'));
+    $this->enableModules(['system', 'user']);
+    $core = \Drupal::config('core.extension');
+    $this->assertEquals(['user' => 0, 'system' => 0], $core->get('module'));
+    $this->assertEquals(['current' => \Drupal::VERSION, 'schema' => (string) SCHEMA_UNINSTALLED], $core->get('versions.user'));
+    $this->assertTrue(\Drupal::moduleHandler()->moduleExists('system'));
+  }
+
+  /**
    * {@inheritdoc}
    */
   protected function tearDown() {
diff --git a/core/tests/Drupal/Tests/Core/Extension/DefaultConfigTest.php b/core/tests/Drupal/Tests/Core/Extension/DefaultConfigTest.php
index e65c651..62377c2 100644
--- a/core/tests/Drupal/Tests/Core/Extension/DefaultConfigTest.php
+++ b/core/tests/Drupal/Tests/Core/Extension/DefaultConfigTest.php
@@ -32,6 +32,7 @@ public function testConfigIsEmpty() {
     $expected = array(
       'module' => array(),
       'theme' => array(),
+      'versions' => array(),
     );
     $this->assertEquals($expected, $config);
   }
