diff --git a/core/core.services.yml b/core/core.services.yml
index 6049d3b..e9998d0 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -290,7 +290,7 @@ services:
       - { name: event_subscriber }
   config.installer:
     class: Drupal\Core\Config\ConfigInstaller
-    arguments: ['@config.factory', '@config.storage', '@config.typed', '@config.manager', '@event_dispatcher']
+    arguments: ['@config.factory', '@config.storage', '@config.typed', '@config.manager', '@event_dispatcher', '%install_profile%']
     lazy: true
   config.storage:
     class: Drupal\Core\Config\CachedStorage
@@ -315,7 +315,7 @@ services:
       - { name: backend_overridable }
   config.storage.schema:
     class: Drupal\Core\Config\ExtensionInstallStorage
-    arguments: ['@config.storage', 'config/schema']
+    arguments: ['@config.storage', '%install_profile%', 'config/schema']
   config.typed:
     class: Drupal\Core\Config\TypedConfigManager
     arguments: ['@config.storage', '@config.storage.schema', '@cache.discovery', '@module_handler']
diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 2294d47..179ca7f 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -715,31 +715,19 @@ function drupal_installation_attempted() {
  * When this function is called during Drupal's initial installation process,
  * the name of the profile that's about to be installed is stored in the global
  * installation state. At all other times, the "install_profile" setting will be
- * available in settings.php.
+ * available in settings.php, or declared as a Distribution.
  *
  * @return string|null $profile
  *   The name of the installation profile or NULL if no installation profile is
  *   currently active. This is the case for example during the first steps of
  *   the installer or during unit tests.
+ *
+ * @deprecated in Drupal 8.2.0, will be removed before Drupal 9.0.0.
+ *   Use the install_profile container parameter or \Drupal::installProfile()
+ *   instead.
  */
 function drupal_get_profile() {
-  global $install_state;
-
-  if (drupal_installation_attempted()) {
-    // If the profile has been selected return it.
-    if (isset($install_state['parameters']['profile'])) {
-      $profile = $install_state['parameters']['profile'];
-    }
-    else {
-      $profile = NULL;
-    }
-  }
-  else {
-    // Fall back to NULL, if there is no 'install_profile' setting.
-    $profile = Settings::get('install_profile');
-  }
-
-  return $profile;
+  return \Drupal::installProfile();
 }
 
 /**
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 5052611..4062365 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -13,6 +13,7 @@
 use Drupal\Core\Installer\Exception\AlreadyInstalledException;
 use Drupal\Core\Installer\Exception\InstallerException;
 use Drupal\Core\Installer\Exception\NoProfilesException;
+use Drupal\Core\Installer\Exception\TooManyDistributionsException;
 use Drupal\Core\Installer\InstallerKernel;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Language\LanguageManager;
@@ -1206,13 +1207,14 @@ function _install_select_profile(&$install_state) {
       return $profile;
     }
   }
-  // Check for a distribution profile.
-  foreach ($install_state['profiles'] as $profile) {
-    $profile_info = install_profile_info($profile->getName());
-    if (!empty($profile_info['distribution'])) {
-      return $profile->getName();
+  // Check for a distribution. If the site has more than one distribution force
+  // the user to choose which will ensure that settings.php has to be written.
+  try {
+    if ($distribution = \Drupal::service('kernel')->getDistribution()) {
+      return $distribution;
     }
   }
+  catch (TooManyDistributionsException $e) {}
 
   // Get all visible (not hidden) profiles.
   $visible_profiles = array_filter($install_state['profiles'], function ($profile) {
@@ -2164,13 +2166,30 @@ function install_display_requirements($install_state, $requirements) {
 }
 
 /**
- * Installation task; ensures install profile is written to settings.php.
+ * Installation task; writes profile to settings.php (absent a distribution).
  *
  * @param array $install_state
  *   An array of information about the current installation state.
  */
 function install_write_profile($install_state) {
-  if (Settings::get('install_profile') !== $install_state['parameters']['profile']) {
+  $settings_value = Settings::get('install_profile');
+  // We need to write to settings.php if the value in settings.php does not
+  // equal the selected profile.
+  $need_to_write = $settings_value !== $install_state['parameters']['profile'];
+  // However, if we're dealing with a distribution and the profile is not
+  // writable do not write the value to settings.php if the current value is not
+  // set.
+  $distribution = FALSE;
+  try {
+    $distribution = \Drupal::service('kernel')->getDistribution();
+  }
+  catch (TooManyDistributionsException $e) {}
+
+  if ($settings_value == '' && $distribution && !is_writable(\Drupal::service('site.path') . '/settings.php')) {
+    $need_to_write = FALSE;
+  }
+
+  if ($need_to_write) {
     // Remember the profile which was used.
     $settings['settings']['install_profile'] = (object) array(
       'value' => $install_state['parameters']['profile'],
diff --git a/core/includes/install.inc b/core/includes/install.inc
index 88ca1e2a..2300678 100644
--- a/core/includes/install.inc
+++ b/core/includes/install.inc
@@ -334,7 +334,7 @@ function drupal_rewrite_settings($settings = array(), $settings_file = NULL) {
     }
 
     // Write the new settings file.
-    if (file_put_contents($settings_file, $buffer) === FALSE) {
+    if (@file_put_contents($settings_file, $buffer) === FALSE) {
       throw new Exception(t('Failed to modify %settings. Verify the file permissions.', array('%settings' => $settings_file)));
     }
     else {
diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php
index 5d1588d..38c7615 100644
--- a/core/lib/Drupal.php
+++ b/core/lib/Drupal.php
@@ -182,6 +182,16 @@ public static function root() {
   }
 
   /**
+   * Gets the active install profile.
+   *
+   * @return string|null
+   *   The name of the any active install profile or distribution.
+   */
+  public static function installProfile() {
+    return static::getContainer()->getParameter('install_profile');
+  }
+
+  /**
    * Indicates if there is a currently active request object.
    *
    * @return bool
diff --git a/core/lib/Drupal/Core/Config/ConfigInstaller.php b/core/lib/Drupal/Core/Config/ConfigInstaller.php
index cc0d88f..a49792f 100644
--- a/core/lib/Drupal/Core/Config/ConfigInstaller.php
+++ b/core/lib/Drupal/Core/Config/ConfigInstaller.php
@@ -6,7 +6,6 @@
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Config\Entity\ConfigDependencyManager;
 use Drupal\Core\Config\Entity\ConfigEntityDependency;
-use Drupal\Core\Site\Settings;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 class ConfigInstaller implements ConfigInstallerInterface {
@@ -61,6 +60,13 @@ class ConfigInstaller implements ConfigInstallerInterface {
   protected $isSyncing = FALSE;
 
   /**
+   * The name of the currently active installation profile.
+   *
+   * @var string
+   */
+  protected $installProfile;
+
+  /**
    * Constructs the configuration installer.
    *
    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
@@ -73,13 +79,16 @@ class ConfigInstaller implements ConfigInstallerInterface {
    *   The configuration manager.
    * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
    *   The event dispatcher.
+   * @param string $install_profile
+   *   The name of the currently active installation profile.
    */
-  public function __construct(ConfigFactoryInterface $config_factory, StorageInterface $active_storage, TypedConfigManagerInterface $typed_config, ConfigManagerInterface $config_manager, EventDispatcherInterface $event_dispatcher) {
+  public function __construct(ConfigFactoryInterface $config_factory, StorageInterface $active_storage, TypedConfigManagerInterface $typed_config, ConfigManagerInterface $config_manager, EventDispatcherInterface $event_dispatcher, $install_profile) {
     $this->configFactory = $config_factory;
     $this->activeStorages[$active_storage->getCollectionName()] = $active_storage;
     $this->typedConfig = $typed_config;
     $this->configManager = $config_manager;
     $this->eventDispatcher = $event_dispatcher;
+    $this->installProfile = $install_profile;
   }
 
   /**
@@ -117,7 +126,7 @@ public function installDefaultConfig($type, $name) {
         $config_to_create = $this->getConfigToCreate($storage, $collection, $prefix, $profile_storages);
         // If we're installing a profile ensure configuration that is overriding
         // is excluded.
-        if ($name == $this->drupalGetProfile()) {
+        if ($name == $this->installProfile) {
           $existing_configuration = $this->getActiveStorages($collection)->listAll();
           $config_to_create = array_diff_key($config_to_create, array_flip($existing_configuration));
         }
@@ -140,7 +149,7 @@ public function installDefaultConfig($type, $name) {
       // Install any optional configuration entities whose dependencies can now
       // be met. This searches all the installed modules config/optional
       // directories.
-      $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, FALSE);
+      $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), $this->installProfile, InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, FALSE);
       $this->installOptionalConfig($storage, [$type => $name]);
     }
 
@@ -152,17 +161,16 @@ public function installDefaultConfig($type, $name) {
    * {@inheritdoc}
    */
   public function installOptionalConfig(StorageInterface $storage = NULL, $dependency = []) {
-    $profile = $this->drupalGetProfile();
     $optional_profile_config = [];
     if (!$storage) {
       // Search the install profile's optional configuration too.
-      $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, TRUE);
+      $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), $this->installProfile, InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, TRUE);
       // The extension install storage ensures that overrides are used.
       $profile_storage = NULL;
     }
-    elseif (isset($profile)) {
+    elseif ($this->installProfile) {
       // Creates a profile storage to search for overrides.
-      $profile_install_path = $this->drupalGetPath('module', $profile) . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY;
+      $profile_install_path = $this->drupalGetPath('module', $this->installProfile) . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY;
       $profile_storage = new FileStorage($profile_install_path, StorageInterface::DEFAULT_COLLECTION);
       $optional_profile_config = $profile_storage->listAll();
     }
@@ -331,7 +339,7 @@ protected function createConfiguration($collection, array $config_to_create) {
    * {@inheritdoc}
    */
   public function installCollectionDefaultConfig($collection) {
-    $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_INSTALL_DIRECTORY, $collection, $this->drupalInstallationAttempted());
+    $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), $this->installProfile, InstallStorage::CONFIG_INSTALL_DIRECTORY, $collection, $this->drupalInstallationAttempted());
     // Only install configuration for enabled extensions.
     $enabled_extensions = $this->getEnabledExtensions();
     $config_to_install = array_filter($storage->listAll(), function ($config_name) use ($enabled_extensions) {
@@ -462,7 +470,7 @@ public function checkConfigurationToInstall($type, $name) {
 
     // Install profiles can not have config clashes. Configuration that
     // has the same name as a module's configuration will be used instead.
-    if ($name != $this->drupalGetProfile()) {
+    if ($name != $this->installProfile) {
       // Throw an exception if the module being installed contains configuration
       // that already exists. Additionally, can not continue installing more
       // modules because those may depend on the current module being installed.
@@ -585,7 +593,7 @@ protected function getEnabledExtensions() {
    *   profile storage should not be used.
    */
   protected function getProfileStorages($installing_name = '') {
-    $profile = $this->drupalGetProfile();
+    $profile = $this->installProfile;
     $profile_storages = [];
     if ($profile && $profile != $installing_name) {
       $profile_path = $this->drupalGetPath('module', $profile);
@@ -632,20 +640,6 @@ protected function drupalGetPath($type, $name) {
   }
 
   /**
-   * Gets the install profile from settings.
-   *
-   * @return string|null $profile
-   *   The name of the installation profile or NULL if no installation profile
-   *   is currently active. This is the case for example during the first steps
-   *   of the installer or during unit tests.
-   */
-  protected function drupalGetProfile() {
-    // Settings is safe to use because settings.php is written before any module
-    // is installed.
-    return Settings::get('install_profile');
-  }
-
-  /**
    * Wrapper for drupal_installation_attempted().
    *
    * @return bool
diff --git a/core/lib/Drupal/Core/Config/ExtensionInstallStorage.php b/core/lib/Drupal/Core/Config/ExtensionInstallStorage.php
index bdeb023..4db57df 100644
--- a/core/lib/Drupal/Core/Config/ExtensionInstallStorage.php
+++ b/core/lib/Drupal/Core/Config/ExtensionInstallStorage.php
@@ -2,7 +2,6 @@
 
 namespace Drupal\Core\Config;
 
-use Drupal\Core\Site\Settings;
 use Drupal\Core\Extension\ExtensionDiscovery;
 
 /**
@@ -28,6 +27,13 @@ class ExtensionInstallStorage extends InstallStorage {
   protected $includeProfile = TRUE;
 
   /**
+   * The name of the currently active installation profile.
+   *
+   * @var string
+   */
+  protected $installProfile;
+
+  /**
    * Overrides \Drupal\Core\Config\InstallStorage::__construct().
    *
    * @param \Drupal\Core\Config\StorageInterface $config_storage
@@ -43,11 +49,12 @@ class ExtensionInstallStorage extends InstallStorage {
    *   (optional) Whether to include the install profile in extensions to
    *   search and to get overrides from.
    */
-  public function __construct(StorageInterface $config_storage, $directory = self::CONFIG_INSTALL_DIRECTORY, $collection = StorageInterface::DEFAULT_COLLECTION, $include_profile = TRUE) {
+  public function __construct(StorageInterface $config_storage, $profile, $directory = self::CONFIG_INSTALL_DIRECTORY, $collection = StorageInterface::DEFAULT_COLLECTION, $include_profile = TRUE) {
     $this->configStorage = $config_storage;
     $this->directory = $directory;
     $this->collection = $collection;
     $this->includeProfile = $include_profile;
+    $this->installProfile = $profile;
   }
 
   /**
@@ -78,22 +85,20 @@ protected function getAllFolders() {
       $this->folders = array();
       $this->folders += $this->getCoreNames();
 
-      $install_profile = Settings::get('install_profile');
-      $profile = drupal_get_profile();
       $extensions = $this->configStorage->read('core.extension');
       // @todo Remove this scan as part of https://www.drupal.org/node/2186491
       $listing = new ExtensionDiscovery(\Drupal::root());
       if (!empty($extensions['module'])) {
         $modules = $extensions['module'];
         // Remove the install profile as this is handled later.
-        unset($modules[$install_profile]);
+        unset($modules[$this->installProfile]);
         $profile_list = $listing->scan('profile');
-        if ($profile && isset($profile_list[$profile])) {
+        if ($this->installProfile && isset($profile_list[$this->installProfile])) {
           // Prime the drupal_get_filename() static cache with the profile info
           // file location so we can use drupal_get_path() on the active profile
           // during the module scan.
           // @todo Remove as part of https://www.drupal.org/node/2186491
-          drupal_get_filename('profile', $profile, $profile_list[$profile]->getPathname());
+          drupal_get_filename('profile', $this->installProfile, $profile_list[$this->installProfile]->getPathname());
         }
         $module_list_scan = $listing->scan('module');
         $module_list = array();
@@ -118,12 +123,12 @@ protected function getAllFolders() {
         // The install profile can override module default configuration. We do
         // this by replacing the config file path from the module/theme with the
         // install profile version if there are any duplicates.
-        if (isset($profile)) {
+        if ($this->installProfile) {
           if (!isset($profile_list)) {
             $profile_list = $listing->scan('profile');
           }
-          if (isset($profile_list[$profile])) {
-            $profile_folders = $this->getComponentNames(array($profile_list[$profile]));
+          if (isset($profile_list[$this->installProfile])) {
+            $profile_folders = $this->getComponentNames(array($profile_list[$this->installProfile]));
             $this->folders = $profile_folders + $this->folders;
           }
         }
diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
index 90541f4..8ff1ee5 100644
--- a/core/lib/Drupal/Core/DrupalKernel.php
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -14,8 +14,10 @@
 use Drupal\Core\DependencyInjection\ServiceProviderInterface;
 use Drupal\Core\DependencyInjection\YamlFileLoader;
 use Drupal\Core\Extension\ExtensionDiscovery;
+use Drupal\Core\Extension\InfoParser;
 use Drupal\Core\File\MimeType\MimeTypeGuesser;
 use Drupal\Core\Http\TrustedHostsRequestFactory;
+use Drupal\Core\Installer\Exception\TooManyDistributionsException;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Site\Settings;
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
@@ -1158,6 +1160,7 @@ protected function compileContainer() {
     $container = $this->getContainerBuilder();
     $container->set('kernel', $this);
     $container->setParameter('container.modules', $this->getModulesParameter());
+    $container->setParameter('install_profile', $this->getInstallProfile());
 
     // Get a list of namespaces and put it onto the container.
     $namespaces = $this->getModuleNamespacesPsr4($this->getModuleFileNames());
@@ -1507,4 +1510,61 @@ protected function addServiceFiles(array $service_yamls) {
     $this->serviceYamls['site'] = array_filter($service_yamls, 'file_exists');
   }
 
+  /**
+   * Gets the active install profile.
+   *
+   * @return string|null
+   *   The name of the any active install profile or distribution.
+   */
+  protected function getInstallProfile() {
+    $install_profile = Settings::get('install_profile');
+    if (empty($install_profile)) {
+      $install_profile = $this->getDistribution();
+    }
+    return $install_profile;
+
+  }
+
+  /**
+   * Get the name of any discovered profile that is a distribution.
+   *
+   * Scans the filesystem looking for all installation profiles.
+   * Returns the one that has a 'distribution' entry in its info.yml
+   * file.  If multiple  profiles are distributions, an exception will
+   * be thrown.
+   *
+   * This is used in two places:
+   *  1) During installation, if there is a single distribution, then
+   *     the installer will not write the installation profile name
+   *     to settings.php.
+   *  2) Whenever DrupalKernel::getInstallProfile() is called, if there
+   *     is no installation profile name noted in settings.php, then
+   *     it will call this function to determine the distribution
+   *     to use.
+   *
+   * @return string|FALSE
+   *   The machine name of any discovered distribution. FALSE if there are no
+   *   distributions.
+   *
+   * @throws \Drupal\Core\Installer\Exception\TooManyDistributionsException
+   *   Thrown when a site has more than one distribution installation profile.
+   */
+  protected function getDistribution() {
+    $listing = new ExtensionDiscovery($this->root);
+    $listing->setProfileDirectories(array());
+    $info_parser = new InfoParser();
+    $distributions = [];
+    foreach ($listing->scan('profile') as $profile) {
+      $info = $info_parser->parse($profile->getPathname());
+      if (!empty($info['distribution'])) {
+        $distributions[] = $profile->getName();
+      }
+    }
+    // There can be only one.
+    if (count($distributions) > 1) {
+      throw new TooManyDistributionsException('A site can only have one distribution, multiple installation profiles discovered: ' . implode(', ', $distributions));
+    }
+    return reset($distributions);
+  }
+
 }
diff --git a/core/lib/Drupal/Core/DrupalKernelInterface.php b/core/lib/Drupal/Core/DrupalKernelInterface.php
index 4f7e9c4..99a7420 100644
--- a/core/lib/Drupal/Core/DrupalKernelInterface.php
+++ b/core/lib/Drupal/Core/DrupalKernelInterface.php
@@ -139,5 +139,4 @@ public function preHandle(Request $request);
    * Helper method that loads legacy Drupal include files.
    */
   public function loadLegacyIncludes();
-
 }
diff --git a/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php b/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php
index c7848e2..f1043c3 100644
--- a/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php
+++ b/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php
@@ -510,4 +510,11 @@ protected function getInfoParser() {
     return $this->infoParser;
   }
 
+  /**
+   * Reset the discovered files.
+   */
+  public static function reset() {
+    static::$files = [];
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Installer/Exception/TooManyDistributionsException.php b/core/lib/Drupal/Core/Installer/Exception/TooManyDistributionsException.php
new file mode 100644
index 0000000..d9028db
--- /dev/null
+++ b/core/lib/Drupal/Core/Installer/Exception/TooManyDistributionsException.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace Drupal\Core\Installer\Exception;
+
+/**
+ * Thrown when a site has more than one distribution installation profile.
+ *
+ * @see \Drupal\Core\DrupalKernel::getDistribution()
+ */
+class TooManyDistributionsException extends \RuntimeException {
+}
diff --git a/core/lib/Drupal/Core/Installer/InstallerKernel.php b/core/lib/Drupal/Core/Installer/InstallerKernel.php
index e00c215..c149985 100644
--- a/core/lib/Drupal/Core/Installer/InstallerKernel.php
+++ b/core/lib/Drupal/Core/Installer/InstallerKernel.php
@@ -46,4 +46,30 @@ public function getConfigStorage() {
     return parent::getConfigStorage();
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getInstallProfile() {
+    global $install_state;
+    if ($install_state && empty($install_state['installation_finished'])) {
+      // If the profile has been selected return it.
+      if (isset($install_state['parameters']['profile'])) {
+        $profile = $install_state['parameters']['profile'];
+      }
+      else {
+        $profile = NULL;
+      }
+    }
+    else {
+      $profile = parent::getInstallProfile();
+    }
+    return $profile;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDistribution() {
+    return parent::getDistribution();
+  }
 }
diff --git a/core/modules/locale/locale.services.yml b/core/modules/locale/locale.services.yml
index e37cb48..b9f8fb8 100644
--- a/core/modules/locale/locale.services.yml
+++ b/core/modules/locale/locale.services.yml
@@ -1,7 +1,7 @@
 services:
   locale.default.config.storage:
     class: Drupal\locale\LocaleDefaultConfigStorage
-    arguments: ['@config.storage', '@language_manager']
+    arguments: ['@config.storage', '@language_manager', '%install_profile%']
     public: false
   locale.config_manager:
     class: Drupal\locale\LocaleConfigManager
diff --git a/core/modules/locale/src/LocaleDefaultConfigStorage.php b/core/modules/locale/src/LocaleDefaultConfigStorage.php
index 29697a1..bcb242b 100644
--- a/core/modules/locale/src/LocaleDefaultConfigStorage.php
+++ b/core/modules/locale/src/LocaleDefaultConfigStorage.php
@@ -57,12 +57,12 @@ class LocaleDefaultConfigStorage {
    * @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager
    *   The language manager.
    */
-  public function __construct(StorageInterface $config_storage, ConfigurableLanguageManagerInterface $language_manager) {
+  public function __construct(StorageInterface $config_storage, ConfigurableLanguageManagerInterface $language_manager, $install_profile) {
     $this->configStorage = $config_storage;
     $this->languageManager = $language_manager;
 
-    $this->requiredInstallStorage = new ExtensionInstallStorage($this->configStorage);
-    $this->optionalInstallStorage = new ExtensionInstallStorage($this->configStorage, ExtensionInstallStorage::CONFIG_OPTIONAL_DIRECTORY);
+    $this->requiredInstallStorage = new ExtensionInstallStorage($this->configStorage, $install_profile);
+    $this->optionalInstallStorage = new ExtensionInstallStorage($this->configStorage, $install_profile, ExtensionInstallStorage::CONFIG_OPTIONAL_DIRECTORY);
   }
 
   /**
diff --git a/core/modules/simpletest/src/Tests/KernelTestBaseTest.php b/core/modules/simpletest/src/Tests/KernelTestBaseTest.php
index 15ecf1f..5876eb7 100644
--- a/core/modules/simpletest/src/Tests/KernelTestBaseTest.php
+++ b/core/modules/simpletest/src/Tests/KernelTestBaseTest.php
@@ -323,13 +323,13 @@ function testNoThemeByDefault() {
   }
 
   /**
-   * Tests that drupal_get_profile() returns NULL.
+   * Tests that \Drupal::installProfile() returns FALSE.
    *
    * As the currently active installation profile is used when installing
    * configuration, for example, this is essential to ensure test isolation.
    */
   public function testDrupalGetProfile() {
-    $this->assertNull(drupal_get_profile());
+    $this->assertFalse(\Drupal::installProfile());
   }
 
   /**
diff --git a/core/modules/system/src/Tests/Bootstrap/GetFilenameUnitTest.php b/core/modules/system/src/Tests/Bootstrap/GetFilenameUnitTest.php
index 50bbc6b..2b75a13 100644
--- a/core/modules/system/src/Tests/Bootstrap/GetFilenameUnitTest.php
+++ b/core/modules/system/src/Tests/Bootstrap/GetFilenameUnitTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\system\Tests\Bootstrap;
 
 use Drupal\simpletest\KernelTestBase;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
 
 /**
  * Tests that drupal_get_filename() works correctly.
@@ -12,14 +13,18 @@
 class GetFilenameUnitTest extends KernelTestBase {
 
   /**
+   * {@inheritdoc}
+   */
+  public function containerBuild(ContainerBuilder $container) {
+    parent::containerBuild($container);
+    // Use the testing install profile.
+    $container->setParameter('install_profile', 'testing');
+  }
+
+  /**
    * Tests that drupal_get_filename() works when the file is not in database.
    */
   function testDrupalGetFilename() {
-    // drupal_get_profile() is using obtaining the profile from state if the
-    // install_state global is not set.
-    global $install_state;
-    $install_state['parameters']['profile'] = 'testing';
-
     // Rebuild system.module.files state data.
     // @todo Remove as part of https://www.drupal.org/node/2186491
     drupal_static_reset('system_rebuild_module_data');
diff --git a/core/modules/system/src/Tests/Installer/DistributionProfileExistingSettingsTest.php b/core/modules/system/src/Tests/Installer/DistributionProfileExistingSettingsTest.php
new file mode 100644
index 0000000..2c6bb01
--- /dev/null
+++ b/core/modules/system/src/Tests/Installer/DistributionProfileExistingSettingsTest.php
@@ -0,0 +1,160 @@
+<?php
+
+namespace Drupal\system\Tests\Installer;
+
+use Drupal\Component\Serialization\Yaml;
+use Drupal\Core\Database\Database;
+use Drupal\Core\DrupalKernel;
+use Drupal\Core\Extension\ExtensionDiscovery;
+use Drupal\Core\Installer\Exception\TooManyDistributionsException;
+use Drupal\Core\Site\Settings;
+use Drupal\simpletest\InstallerTestBase;
+use Symfony\Component\HttpFoundation\Request;
+
+
+/**
+ * Tests distribution profile support with existing settings.
+ *
+ * @group Installer
+ */
+class DistributionProfileExistingSettingsTest extends InstallerTestBase {
+
+  /**
+   * The distribution profile info.
+   *
+   * @var array
+   */
+  protected $info;
+
+  protected function setUp() {
+    $this->info = array(
+      'type' => 'profile',
+      'core' => \Drupal::CORE_COMPATIBILITY,
+      'name' => 'Distribution profile',
+      'distribution' => [
+        'name' => 'My Distribution',
+        'install' => [
+          'theme' => 'bartik',
+        ],
+      ],
+    );
+    // File API functions are not available yet.
+    $path = $this->siteDirectory . '/profiles/mydistro';
+    mkdir($path, 0777, TRUE);
+    file_put_contents("$path/mydistro.info.yml", Yaml::encode($this->info));
+
+    // Pre-configure hash salt.
+    // Any string is valid, so simply use the class name of this test.
+    $this->settings['settings']['hash_salt'] = (object) [
+      'value' => __CLASS__,
+      'required' => TRUE,
+    ];
+
+    // Pre-configure database credentials.
+    $connection_info = Database::getConnectionInfo();
+    unset($connection_info['default']['pdo']);
+    unset($connection_info['default']['init_commands']);
+
+    $this->settings['databases']['default'] = (object) [
+      'value' => $connection_info,
+      'required' => TRUE,
+    ];
+
+    // Use the kernel to find the site path because the site.path service should
+    // not be available at this point in the install process.
+    $site_path = DrupalKernel::findSitePath(Request::createFromGlobals());
+    // Pre-configure config directories.
+    $this->settings['config_directories'] = [
+      CONFIG_SYNC_DIRECTORY => (object) [
+        'value' => $site_path . '/files/config_staging',
+        'required' => TRUE,
+      ],
+    ];
+    mkdir($this->settings['config_directories'][CONFIG_SYNC_DIRECTORY]->value, 0777, TRUE);
+    parent::setUp();
+  }
+
+  /**
+   * Overrides InstallerTest::setUpLanguage().
+   */
+  protected function setUpLanguage() {
+    // Make settings file not writable.
+    $filename = $this->siteDirectory . '/settings.php';
+    // Make the settings file read-only.
+    // Not using File API; a potential error must trigger a PHP warning.
+    chmod($filename, 0444);
+
+    // Verify that the distribution name appears.
+    $this->assertRaw($this->info['distribution']['name']);
+    // Verify that the requested theme is used.
+    $this->assertRaw($this->info['distribution']['install']['theme']);
+    // Verify that the "Choose profile" step does not appear.
+    $this->assertNoText('profile');
+
+    parent::setUpLanguage();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUpProfile() {
+    // This step is skipped, because there is a distribution profile.
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUpSettings() {
+    // This step should not appear, since settings.php is fully configured
+    // already.
+  }
+
+  /**
+   * Confirms that the installation succeeded.
+   */
+  public function testInstalled() {
+    $this->assertUrl('user/1');
+    $this->assertResponse(200);
+    // Confirm that we are logged-in after installation.
+    $this->assertText($this->rootUser->getUsername());
+
+    // Confirm that Drupal recognizes this distribution as the current profile.
+    $this->assertEqual(\Drupal::installProfile(), 'mydistro');
+    $this->assertNull(Settings::get('install_profile'), 'The install profile has not been written to settings.php.');
+
+    $this->rebuildContainer();
+    $this->pass('Container can be rebuilt even though distribution is not written to settings.php.');
+
+    // Create another installation profile which is a distrubtion.
+    $info = [
+      'type' => 'profile',
+      'core' => \Drupal::CORE_COMPATIBILITY,
+      'name' => 'Distribution profile 2',
+      'distribution' => [
+        'name' => 'My Distribution 2',
+        'install' => [
+          'theme' => 'bartik',
+        ],
+      ],
+    ];
+    // File API functions are not available yet.
+    $path = $this->siteDirectory . '/profiles/mydistro2';
+    mkdir($path, 0777, TRUE);
+    file_put_contents("$path/mydistro2.info.yml", Yaml::encode($info));
+
+    // Test that a site will multiple distributions will get an exception when
+    // rebuilding the container. In order to do this we need to reset the
+    // discovered files in ExtensionDiscovery.
+    try {
+      ExtensionDiscovery::reset();
+      $this->rebuildContainer();
+      $this->fail('TooManyDistributionsException exception thrown.');
+    }
+    catch (TooManyDistributionsException $e) {
+      $this->pass('TooManyDistributionsException exception thrown.');
+      $this->assertEqual('A site can only have one distribution, multiple installation profiles discovered: mydistro, mydistro2', $e->getMessage());
+    }
+
+  }
+
+}
diff --git a/core/modules/system/src/Tests/Installer/DistributionProfileTest.php b/core/modules/system/src/Tests/Installer/DistributionProfileTest.php
index d4d3553..bdffb30 100644
--- a/core/modules/system/src/Tests/Installer/DistributionProfileTest.php
+++ b/core/modules/system/src/Tests/Installer/DistributionProfileTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\system\Tests\Installer;
 
 use Drupal\Component\Serialization\Yaml;
+use Drupal\Core\Site\Settings;
 use Drupal\simpletest\InstallerTestBase;
 
 /**
@@ -68,6 +69,10 @@ public function testInstalled() {
     $this->assertResponse(200);
     // Confirm that we are logged-in after installation.
     $this->assertText($this->rootUser->getUsername());
+
+    // Confirm that Drupal recognizes this distribution as the current profile.
+    $this->assertEqual(\Drupal::installProfile(), 'mydistro');
+    $this->assertEqual(Settings::get('install_profile'), 'mydistro', 'The install profile has been written to settings.php.');
   }
 
 }
diff --git a/core/modules/system/src/Tests/Installer/InstallerExistingSettingsMismatchProfileBrokenTest.php b/core/modules/system/src/Tests/Installer/InstallerExistingSettingsMismatchProfileBrokenTest.php
new file mode 100644
index 0000000..69a81c6
--- /dev/null
+++ b/core/modules/system/src/Tests/Installer/InstallerExistingSettingsMismatchProfileBrokenTest.php
@@ -0,0 +1,131 @@
+<?php
+
+namespace Drupal\system\Tests\Installer;
+
+use Drupal\Component\Render\FormattableMarkup;
+use Drupal\Component\Utility\Html;
+use Drupal\Core\DrupalKernel;
+use Drupal\simpletest\InstallerTestBase;
+use Drupal\Core\Database\Database;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Tests installer breaks with a profile mismatch and a read-only settings.php.
+ *
+ * @group Installer
+ */
+class InstallerExistingSettingsMismatchProfileBrokenTest extends InstallerTestBase {
+
+  /**
+   * The excepted exception message thrown during the installer.
+   * @var string;
+   */
+  protected $exceptionMessage;
+
+  /**
+   * {@inheritdoc}
+   *
+   * Configures a preexisting settings.php file without an install_profile
+   * setting before invoking the interactive installer.
+   */
+  protected function setUp() {
+    // Pre-configure hash salt.
+    // Any string is valid, so simply use the class name of this test.
+    $this->settings['settings']['hash_salt'] = (object) [
+      'value' => __CLASS__,
+      'required' => TRUE,
+    ];
+
+    // Pre-configure database credentials.
+    $connection_info = Database::getConnectionInfo();
+    unset($connection_info['default']['pdo']);
+    unset($connection_info['default']['init_commands']);
+
+    $this->settings['databases']['default'] = (object) [
+      'value' => $connection_info,
+      'required' => TRUE,
+    ];
+
+    // During interactive install we'll change this to a different profile and
+    // this test will ensure that the new value is written to settings.php.
+    $this->settings['settings']['install_profile'] = (object) [
+      'value' => 'minimal',
+      'required' => TRUE,
+    ];
+
+    // Pre-configure config directories.
+    $site_path = DrupalKernel::findSitePath(Request::createFromGlobals());
+    $this->settings['config_directories'] = [
+      CONFIG_SYNC_DIRECTORY => (object) [
+        'value' => $site_path . '/files/config_staging',
+        'required' => TRUE,
+      ],
+    ];
+    mkdir($this->settings['config_directories'][CONFIG_SYNC_DIRECTORY]->value, 0777, TRUE);
+
+    // @todo Remove HTML once https://www.drupal.org/node/2514044 is fixed.
+    $this->exceptionMessage = (string) new FormattableMarkup('Failed to modify %path. Verify the file permissions.', ['%path' => $this->siteDirectory . '/settings.php']);
+    parent::setUp();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function visitInstaller() {
+    // Make settings file not writable. This will break the installer.
+    $filename = $this->siteDirectory . '/settings.php';
+    // Make the settings file read-only.
+    // Not using File API; a potential error must trigger a PHP warning.
+    chmod($filename, 0444);
+
+    $this->drupalGet($GLOBALS['base_url'] . '/core/install.php?langcode=en&profile=testing');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUpLanguage() {
+    // This step is skipped, because there is a lagcode as a query param.
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUpProfile() {
+    // This step is skipped, because there is a profile as a query param.
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUpSettings() {
+    // This step should not appear, since settings.php is fully configured
+    // already.
+  }
+
+  protected function setUpSite() {
+    // This step should not appear, since settings.php could not be written.
+  }
+
+  /**
+   * Verifies that installation did not succeed.
+   */
+  public function testBrokenInstaller() {
+    $this->assertRaw(Html::escape($this->exceptionMessage));
+    // The exceptions are expected. Do not interpret them as a test failure.
+    // Not using File API; a potential error must trigger a PHP warning.
+    unlink(\Drupal::root() . '/' . $this->siteDirectory . '/error.log');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function error($message = '', $group = 'Other', array $caller = NULL) {
+    if ($group == 'Exception' && $message == $this->exceptionMessage) {
+      // Ignore the expected exception.
+      return FALSE;
+    }
+    return parent::error($message, $group, $caller);
+  }
+
+}
diff --git a/core/modules/system/src/Tests/Installer/InstallerExistingSettingsMismatchProfileTest.php b/core/modules/system/src/Tests/Installer/InstallerExistingSettingsMismatchProfileTest.php
new file mode 100644
index 0000000..17d4660
--- /dev/null
+++ b/core/modules/system/src/Tests/Installer/InstallerExistingSettingsMismatchProfileTest.php
@@ -0,0 +1,101 @@
+<?php
+
+namespace Drupal\system\Tests\Installer;
+
+use Drupal\Core\DrupalKernel;
+use Drupal\Core\Site\Settings;
+use Drupal\simpletest\InstallerTestBase;
+use Drupal\Core\Database\Database;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Tests the installer with an existing settings file but no install profile.
+ *
+ * @group Installer
+ */
+class InstallerExistingSettingsMismatchProfileTest extends InstallerTestBase {
+
+  /**
+   * {@inheritdoc}
+   *
+   * Configures a preexisting settings.php file without an install_profile
+   * setting before invoking the interactive installer.
+   */
+  protected function setUp() {
+    // Pre-configure hash salt.
+    // Any string is valid, so simply use the class name of this test.
+    $this->settings['settings']['hash_salt'] = (object) array(
+      'value' => __CLASS__,
+      'required' => TRUE,
+    );
+
+    // Pre-configure database credentials.
+    $connection_info = Database::getConnectionInfo();
+    unset($connection_info['default']['pdo']);
+    unset($connection_info['default']['init_commands']);
+
+    $this->settings['databases']['default'] = (object) array(
+      'value' => $connection_info,
+      'required' => TRUE,
+    );
+
+    // During interactive install we'll change this to a different profile and
+    // this test will ensure that the new value is written to settings.php.
+    $this->settings['settings']['install_profile'] = (object) array(
+      'value' => 'minimal',
+      'required' => TRUE,
+    );
+
+    // Pre-configure config directories.
+    $this->settings['config_directories'] = array(
+      CONFIG_SYNC_DIRECTORY => (object) array(
+        'value' => DrupalKernel::findSitePath(Request::createFromGlobals()) . '/files/config_sync',
+        'required' => TRUE,
+      ),
+    );
+    mkdir($this->settings['config_directories'][CONFIG_SYNC_DIRECTORY]->value, 0777, TRUE);
+
+    parent::setUp();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function visitInstaller() {
+    // Provide profile and language in query string to skip these pages.
+    $this->drupalGet($GLOBALS['base_url'] . '/core/install.php?langcode=en&profile=testing');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUpLanguage() {
+    // This step is skipped, because there is a lagcode as a query param.
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUpProfile() {
+    // This step is skipped, because there is a profile as a query param.
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUpSettings() {
+    // This step should not appear, since settings.php is fully configured
+    // already.
+  }
+
+  /**
+   * Verifies that installation succeeded.
+   */
+  public function testInstaller() {
+    $this->assertUrl('user/1');
+    $this->assertResponse(200);
+    $this->assertEqual('testing', \Drupal::installProfile());
+    $this->assertEqual('testing', Settings::get('install_profile'), 'Profile was correctly changed to testing in Settings.php');
+  }
+
+}
diff --git a/core/modules/system/src/Tests/Installer/InstallerExistingSettingsNoProfileTest.php b/core/modules/system/src/Tests/Installer/InstallerExistingSettingsNoProfileTest.php
index 84f861a..9517308 100644
--- a/core/modules/system/src/Tests/Installer/InstallerExistingSettingsNoProfileTest.php
+++ b/core/modules/system/src/Tests/Installer/InstallerExistingSettingsNoProfileTest.php
@@ -3,7 +3,6 @@
 namespace Drupal\system\Tests\Installer;
 
 use Drupal\Core\DrupalKernel;
-use Drupal\Core\Site\Settings;
 use Drupal\simpletest\InstallerTestBase;
 use Drupal\Core\Database\Database;
 use Symfony\Component\HttpFoundation\Request;
@@ -65,7 +64,7 @@ protected function setUpSettings() {
   public function testInstaller() {
     $this->assertUrl('user/1');
     $this->assertResponse(200);
-    $this->assertEqual('testing', Settings::get('install_profile'));
+    $this->assertEqual('testing', \Drupal::installProfile());
   }
 
 }
diff --git a/core/modules/system/src/Tests/Installer/InstallerExistingSettingsTest.php b/core/modules/system/src/Tests/Installer/InstallerExistingSettingsTest.php
index 6ecd9fd..4dc0333 100644
--- a/core/modules/system/src/Tests/Installer/InstallerExistingSettingsTest.php
+++ b/core/modules/system/src/Tests/Installer/InstallerExistingSettingsTest.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\system\Tests\Installer;
 
+use Drupal\Core\Site\Settings;
 use Drupal\simpletest\InstallerTestBase;
 use Drupal\Core\Database\Database;
 use Drupal\Core\DrupalKernel;
@@ -75,6 +76,7 @@ public function testInstaller() {
     $this->assertUrl('user/1');
     $this->assertResponse(200);
     $this->assertEqual('testing', drupal_get_profile(), 'Profile was changed from minimal to testing during interactive install.');
+    $this->assertEqual('testing', Settings::get('install_profile'), 'Profile was correctly changed to testing in Settings.php');
   }
 
 }
diff --git a/core/modules/system/src/Tests/Installer/MultipleDistributionsProfileTest.php b/core/modules/system/src/Tests/Installer/MultipleDistributionsProfileTest.php
new file mode 100644
index 0000000..124e55f
--- /dev/null
+++ b/core/modules/system/src/Tests/Installer/MultipleDistributionsProfileTest.php
@@ -0,0 +1,106 @@
+<?php
+
+namespace Drupal\system\Tests\Installer;
+
+use Drupal\Component\Serialization\Yaml;
+use Drupal\Core\Extension\ExtensionDiscovery;
+use Drupal\Core\Installer\Exception\TooManyDistributionsException;
+use Drupal\Core\Site\Settings;
+use Drupal\simpletest\InstallerTestBase;
+
+/**
+ * Tests multiple distribution profile support.
+ *
+ * @group Installer
+ */
+class MultipleDistributionsProfileTest extends InstallerTestBase {
+
+  /**
+   * The distribution profile info.
+   *
+   * @var array
+   */
+  protected $info;
+
+  protected function setUp() {
+    // Create two distributions.
+    foreach (['distribution_one', 'distribution_two'] as $name) {
+      $info = array(
+        'type' => 'profile',
+        'core' => \Drupal::CORE_COMPATIBILITY,
+        'name' => $name .' profile',
+        'distribution' => array(
+          'name' => $name,
+          'install' => array(
+            'theme' => 'bartik',
+          ),
+        ),
+      );
+      // File API functions are not available yet.
+      $path = $this->siteDirectory . '/profiles/' . $name;
+      mkdir($path, 0777, TRUE);
+      file_put_contents("$path/$name.info.yml", Yaml::encode($info));
+    }
+    // Install the first distribution.
+    $this->profile = 'distribution_one';
+
+    parent::setUp();
+  }
+
+  /**
+   * Overrides InstallerTest::setUpLanguage().
+   */
+  protected function setUpLanguage() {
+    $this->assertNoRaw('distribution_one');
+    $this->assertNoRaw('distribution_two');
+    // Verify that the "Choose profile" step appears.
+    $this->assertText('Choose profile');
+
+    parent::setUpLanguage();
+  }
+
+  /**
+   * Overrides InstallerTest::setUpProfile().
+   */
+  protected function setUpProfile() {
+    $this->assertText('distribution_one');
+    $this->assertText('distribution_two');
+    parent::setUpProfile();
+  }
+
+  /**
+   * Confirms that the installation succeeded.
+   */
+  public function testInstalled() {
+    $this->assertUrl('user/1');
+    $this->assertResponse(200);
+    // Confirm that we are logged-in after installation.
+    $this->assertText($this->rootUser->getUsername());
+
+    // Confirm that Drupal recognizes this distribution as the current profile.
+    $this->assertEqual(\Drupal::installProfile(), 'distribution_one');
+    $this->assertEqual(Settings::get('install_profile'), 'distribution_one', 'The install profile has been written to settings.php.');
+
+    // Test that a site will multiple distributions will get an exception when
+    // calling \Drupal\Core\DrupalKernel::getDistribution().
+    try {
+      $kernel = $this->container->get('kernel');
+      // getDistribution() is protected in the DrupalKernel class.
+      // Call setAccessible(TRUE) so that we can call it to test its behavior.
+      $getDistributionMethod = new \ReflectionMethod($kernel, 'getDistribution');
+      $getDistributionMethod->setAccessible(TRUE);
+      $getDistributionMethod->invokeArgs($kernel, array());
+      $this->fail('TooManyDistributionsException exception thrown.');
+    }
+    catch (TooManyDistributionsException $e) {
+      $this->pass('TooManyDistributionsException exception thrown.');
+    }
+    // To mirror the test in DistributionProfileExistingSettingsTest we reset
+    // the discovered files in ExtensionDiscovery.
+    // @see \Drupal\system\Tests\Installer\DistributionProfileExistingSettingsTest::testInstalled()
+    ExtensionDiscovery::reset();
+    $this->rebuildContainer();
+    $this->pass('Container can be rebuilt as distribution is written to settings.php.');
+  }
+
+}
