diff --git a/package_manager/package_manager.services.yml b/package_manager/package_manager.services.yml
index b6fa99e3ac5a5e8d5ff7deb0248060b7d67956dc..bffff5e0a9e61379b7c8418003b76209f18d83b4 100644
--- a/package_manager/package_manager.services.yml
+++ b/package_manager/package_manager.services.yml
@@ -168,3 +168,6 @@ services:
     class: Drupal\package_manager\Validator\ComposerPatchesValidator
     tags:
       - { name: event_subscriber }
+  package_manager.update_processor:
+    class: Drupal\package_manager\PackageManagerUpdateProcessor
+    arguments: [ '@config.factory', '@queue', '@update.fetcher', '@state', '@private_key', '@keyvalue', '@keyvalue.expirable' ]
diff --git a/package_manager/src/PackageManagerUpdateProcessor.php b/package_manager/src/PackageManagerUpdateProcessor.php
new file mode 100644
index 0000000000000000000000000000000000000000..4d4f309fc526ead6f144133b6e037f13d96da706
--- /dev/null
+++ b/package_manager/src/PackageManagerUpdateProcessor.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace Drupal\package_manager;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface;
+use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
+use Drupal\Core\PrivateKey;
+use Drupal\Core\Queue\QueueFactory;
+use Drupal\Core\State\StateInterface;
+use Drupal\update\UpdateFetcherInterface;
+use Drupal\update\UpdateProcessor;
+
+/**
+ * Extends the Update module's update processor allow fetching any project.
+ *
+ * The Update module's update processor service is intended to only fetch
+ * information for projects in the active codebase. Although it would be
+ * possible to use the Update module's update processor service to fetch
+ * information for projects not in the active code base this would add the
+ * project information to Update module's cache which would result in these
+ * projects being returned from the Update module's global functions such as
+ * update_get_available().
+ */
+class PackageManagerUpdateProcessor extends UpdateProcessor {
+
+  /**
+   * Constructs an PackageManagerUpdateProcessor object.
+   *
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The config factory.
+   * @param \Drupal\Core\Queue\QueueFactory $queue_factory
+   *   The queue factory.
+   * @param \Drupal\update\UpdateFetcherInterface $update_fetcher
+   *   The update fetcher service.
+   * @param \Drupal\Core\State\StateInterface $state_store
+   *   The state service.
+   * @param \Drupal\Core\PrivateKey $private_key
+   *   The private key factory service.
+   * @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value_factory
+   *   The key/value factory.
+   * @param \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface $key_value_expirable_factory
+   *   The expirable key/value factory.
+   */
+  public function __construct(ConfigFactoryInterface $config_factory, QueueFactory $queue_factory, UpdateFetcherInterface $update_fetcher, StateInterface $state_store, PrivateKey $private_key, KeyValueFactoryInterface $key_value_factory, KeyValueExpirableFactoryInterface $key_value_expirable_factory) {
+    $this->updateFetcher = $update_fetcher;
+    $this->updateSettings = $config_factory->get('update.settings');
+    $this->fetchQueue = $queue_factory->get('package_manager.update_fetch_tasks');
+    $this->tempStore = $key_value_expirable_factory->get('package_manager.update');
+    $this->fetchTaskStore = $key_value_factory->get('package_manager.update_fetch_task');
+    $this->availableReleasesTempStore = $key_value_expirable_factory->get('package_manager.update_available_releases');
+    $this->stateStore = $state_store;
+    $this->privateKey = $private_key;
+    $this->fetchTasks = [];
+    $this->failed = [];
+  }
+
+  /**
+   * Gets the project data by name.
+   *
+   * @param string $name
+   *   The project name.
+   *
+   * @return mixed[]
+   *   The project data if any is available, otherwise NULL.
+   */
+  public function getProjectData(string $name): ?array {
+    if ($this->availableReleasesTempStore->has($name)) {
+      return $this->availableReleasesTempStore->get($name);
+    }
+    $project_fetch_data = [
+      'name' => $name,
+      'project_type' => 'unknown',
+      'includes' => [],
+    ];
+    $this->createFetchTask($project_fetch_data);
+    if ($this->processFetchTask($project_fetch_data)) {
+      // If the fetch task was successful return the project information.
+      return $this->availableReleasesTempStore->get($name);
+    }
+    return NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processFetchTask($project) {
+    // The parent method will set 'update.last_check' which will be used to
+    // inform the user when the last time update information was checked. In
+    // order to leave this value unaffected we will reset this to it's previous
+    // value.
+    $last_check = $this->stateStore->get('update.last_check');
+    $success = parent::processFetchTask($project);
+    $this->stateStore->set('update.last_check', $last_check);
+    return $success;
+  }
+
+}
diff --git a/package_manager/src/ProjectInfo.php b/package_manager/src/ProjectInfo.php
index c28b0927f2a8338e5814040d6362f8c8bd849a6d..fd1343a5596f66e2857927fc9da8546d6374f4e3 100644
--- a/package_manager/src/ProjectInfo.php
+++ b/package_manager/src/ProjectInfo.php
@@ -74,9 +74,12 @@ final class ProjectInfo {
    *   If data about available updates cannot be retrieved.
    */
   public function getProjectInfo(): ?array {
-    $available_updates = update_get_available(TRUE);
+    $available_updates = $this->getAvailableProjects();
     $project_data = update_calculate_project_data($available_updates);
-    return $project_data[$this->name] ?? NULL;
+    if (!isset($project_data[$this->name])) {
+      return $available_updates[$this->name] ?? NULL;
+    }
+    return $project_data[$this->name];
   }
 
   /**
@@ -99,31 +102,39 @@ final class ProjectInfo {
     if (!$project) {
       return NULL;
     }
-    $available_updates = update_get_available()[$this->name];
+    $available_updates = $this->getAvailableProjects()[$this->name];
     if ($available_updates['project_status'] !== 'published') {
       throw new \RuntimeException("The project '{$this->name}' can not be updated because its status is " . $available_updates['project_status']);
     }
+    $installed_version = $this->getInstalledVersion();
 
-    // If we're already up-to-date, there's nothing else we need to do.
-    if ($project['status'] === UpdateManagerInterface::CURRENT) {
-      return [];
-    }
+    if ($installed_version) {
+      // If the project is installed, and we're already up-to-date, there's
+      // nothing else we need to do.
+      if ($project['status'] === UpdateManagerInterface::CURRENT) {
+        return [];
+      }
 
-    if (empty($available_updates['releases'])) {
-      // If project is not current we should always have at least one release.
-      throw new \RuntimeException('There was a problem getting update information. Try again later.');
+      if (empty($available_updates['releases'])) {
+        // If project is installed but not current we should always have at
+        // least one release.
+        throw new \RuntimeException('There was a problem getting update information. Try again later.');
+      }
     }
-    $installed_version = $this->getInstalledVersion();
+
     $support_branches = explode(',', $available_updates['supported_branches']);
     $installable_releases = [];
     foreach ($available_updates['releases'] as $release_info) {
       $release = ProjectRelease::createFromArray($release_info);
       $version = $release->getVersion();
-      $semantic_version = LegacyVersionUtility::convertToSemanticVersion($version);
-      $semantic_installed_version = LegacyVersionUtility::convertToSemanticVersion($installed_version);
-      if (Comparator::lessThanOrEqualTo($semantic_version, $semantic_installed_version)) {
-        // Stop searching for releases as soon as we find the installed version.
-        break;
+      if ($installed_version) {
+        $semantic_version = LegacyVersionUtility::convertToSemanticVersion($version);
+        $semantic_installed_version = LegacyVersionUtility::convertToSemanticVersion($installed_version);
+        if (Comparator::lessThanOrEqualTo($semantic_version, $semantic_installed_version)) {
+          // If the project is installed stop searching for releases as soon as
+          // we find the installed version.
+          break;
+        }
       }
       if ($this->isInstallable($release, $support_branches)) {
         $installable_releases[$version] = $release;
@@ -141,12 +152,37 @@ final class ProjectInfo {
    */
   public function getInstalledVersion(): ?string {
     if ($project_data = $this->getProjectInfo()) {
-      if (empty($project_data['existing_version'])) {
-        throw new \UnexpectedValueException("Project '{$this->name}' does not have 'existing_version' set");
-      }
-      return $project_data['existing_version'];
+      return $project_data['existing_version'] ?? NULL;
     }
     return NULL;
   }
 
+  /**
+   * Gets the available projects.
+   *
+   * @return array
+   *   The available projects keyed by project machine name in the format
+   *   returned by update_get_available(). If the project specified in ::name is
+   *   not returned from update_get_available() this project will be explicitly
+   *   fetched and added the return value of this function.
+   *
+   * @see \update_get_available()
+   */
+  private function getAvailableProjects(): array {
+    $available_projects = update_get_available(TRUE);
+    // update_get_available() will only returns projects that are in the active
+    // codebase. If the project specified by ::name is not returned in
+    // $available_projects, it means it is not in the active codebase, therefore
+    // we will retrieve the project information from Package Manager's own
+    // update processor service.
+    if (!isset($available_projects[$this->name])) {
+      /** @var \Drupal\package_manager\PackageManagerUpdateProcessor $update_processor */
+      $update_processor = \Drupal::service('package_manager.update_processor');
+      if ($project_data = $update_processor->getProjectData($this->name)) {
+        $available_projects[$this->name] = $project_data;
+      }
+    }
+    return $available_projects;
+  }
+
 }
diff --git a/package_manager/tests/src/Kernel/ComposerUtilityTest.php b/package_manager/tests/src/Kernel/ComposerUtilityTest.php
index bd1d7c16f960393f6e5141d3f49aa2a2d8434192..f80331b01dc407f482d05f0c95dd3562f50671ce 100644
--- a/package_manager/tests/src/Kernel/ComposerUtilityTest.php
+++ b/package_manager/tests/src/Kernel/ComposerUtilityTest.php
@@ -19,7 +19,7 @@ class ComposerUtilityTest extends KernelTestBase {
   /**
    * {@inheritdoc}
    */
-  protected static $modules = ['package_manager'];
+  protected static $modules = ['package_manager', 'update'];
 
   /**
    * {@inheritdoc}
diff --git a/package_manager/tests/src/Kernel/FileSyncerFactoryTest.php b/package_manager/tests/src/Kernel/FileSyncerFactoryTest.php
index b22fda8ed1c83a8bad260b31b272544b02e2845a..56aefbc94bbd3825e4e434237e00b27e12124934 100644
--- a/package_manager/tests/src/Kernel/FileSyncerFactoryTest.php
+++ b/package_manager/tests/src/Kernel/FileSyncerFactoryTest.php
@@ -17,7 +17,7 @@ class FileSyncerFactoryTest extends KernelTestBase {
   /**
    * {@inheritdoc}
    */
-  protected static $modules = ['package_manager'];
+  protected static $modules = ['package_manager', 'update'];
 
   /**
    * Data provider for testFactory().
diff --git a/package_manager/tests/src/Kernel/ProjectInfoTest.php b/package_manager/tests/src/Kernel/ProjectInfoTest.php
index d0e404ff77f82130df6a28fda57c6e1aa1f49297..70952df90a40c088b66c271d26505c1f3d6ca342 100644
--- a/package_manager/tests/src/Kernel/ProjectInfoTest.php
+++ b/package_manager/tests/src/Kernel/ProjectInfoTest.php
@@ -1,9 +1,8 @@
 <?php
 
-namespace Drupal\Tests\package_manger\Kernel;
+namespace Drupal\Tests\package_manager\Kernel;
 
 use Drupal\package_manager\ProjectInfo;
-use Drupal\Tests\package_manager\Kernel\PackageManagerKernelTestBase;
 
 /**
  * @coversDefaultClass \Drupal\package_manager\ProjectInfo
@@ -118,6 +117,52 @@ class ProjectInfoTest extends PackageManagerKernelTestBase {
     ];
   }
 
+  /**
+   * Tests a project that is not in the codebase.
+   */
+  public function testNewProject(): void {
+    $fixtures_directory = __DIR__ . '/../../fixtures/release-history/';
+    $metadata_fixtures['drupal'] = $fixtures_directory . 'drupal.9.8.2.xml';
+    $metadata_fixtures['aaa_automatic_updates_test'] = $fixtures_directory . 'aaa_automatic_updates_test.9.8.2.xml';
+    $this->setReleaseMetadata($metadata_fixtures);
+    $available = update_get_available(TRUE);
+    $this->assertSame(['drupal'], array_keys($available));
+    $this->setReleaseMetadata($metadata_fixtures);
+    $state = $this->container->get('state');
+    // Set the state that the update module uses to store last checked time
+    // ensure our calls do not affect it.
+    $state->set('update.last_check', 123);
+    $project_info = new ProjectInfo('aaa_automatic_updates_test');
+    $project_data = $project_info->getProjectInfo();
+    // Ensure the project information is correct.
+    $this->assertSame('AAA', $project_data['title']);
+    $all_releases = [
+      '7.0.1',
+      '7.0.0',
+      '7.0.0-alpha1',
+      '8.x-6.2',
+      '8.x-6.1',
+      '8.x-6.0',
+      '8.x-6.0-alpha1',
+      '7.0.x-dev',
+      '8.x-6.x-dev',
+    ];
+    $uninstallable_releases = ['7.0.x-dev', '8.x-6.x-dev'];
+    $installable_releases = array_values(array_diff($all_releases, $uninstallable_releases));
+    $this->assertSame(
+      $all_releases,
+      array_keys($project_data['releases'])
+    );
+    $this->assertSame(
+      $installable_releases,
+      array_keys($project_info->getInstallableReleases())
+    );
+    $this->assertNull($project_info->getInstalledVersion());
+    // Ensure we have not changed the state the update module uses to store
+    // the last checked time.
+    $this->assertSame(123, $state->get('update.last_check'));
+  }
+
   /**
    * Tests a project with a status other than "published".
    *
diff --git a/package_manager/tests/src/Kernel/ServicesTest.php b/package_manager/tests/src/Kernel/ServicesTest.php
index ef91a3c2158d3220586ff1d17970501a819a8255..db6f8628f1bc7da4a0cc40f13d1123fdf7bc930a 100644
--- a/package_manager/tests/src/Kernel/ServicesTest.php
+++ b/package_manager/tests/src/Kernel/ServicesTest.php
@@ -18,7 +18,7 @@ class ServicesTest extends KernelTestBase {
   /**
    * {@inheritdoc}
    */
-  protected static $modules = ['package_manager'];
+  protected static $modules = ['package_manager', 'update'];
 
   /**
    * Tests that Package Manager's public services can be instantiated.