diff --git a/core/lib/Drupal/Core/Field/Plugin/migrate/field/Email.php b/core/lib/Drupal/Core/Field/Plugin/migrate/field/Email.php
index 3b490f0f19..1649700f4c 100644
--- a/core/lib/Drupal/Core/Field/Plugin/migrate/field/Email.php
+++ b/core/lib/Drupal/Core/Field/Plugin/migrate/field/Email.php
@@ -11,7 +11,9 @@
  *   core = {6,7},
  *   type_map = {
  *     "email" = "email"
- *   }
+ *   },
+ *   source_module = "email",
+ *   destination_module = "core"
  * )
  */
 class Email extends FieldPluginBase {
diff --git a/core/lib/Drupal/Core/Field/Plugin/migrate/field/d7/EntityReference.php b/core/lib/Drupal/Core/Field/Plugin/migrate/field/d7/EntityReference.php
index 2e61589fb5..f45e95d2cc 100644
--- a/core/lib/Drupal/Core/Field/Plugin/migrate/field/d7/EntityReference.php
+++ b/core/lib/Drupal/Core/Field/Plugin/migrate/field/d7/EntityReference.php
@@ -10,7 +10,9 @@
  *   type_map = {
  *     "entityreference" = "entity_reference",
  *   },
- *   core = {7}
+ *   core = {7},
+ *   source_module = "entityreference",
+ *   destination_module = "core"
  * )
  */
 class EntityReference extends FieldPluginBase {}
diff --git a/core/lib/Drupal/Core/Field/Plugin/migrate/field/d7/NumberField.php b/core/lib/Drupal/Core/Field/Plugin/migrate/field/d7/NumberField.php
index 90803288e4..cf2f4567a8 100644
--- a/core/lib/Drupal/Core/Field/Plugin/migrate/field/d7/NumberField.php
+++ b/core/lib/Drupal/Core/Field/Plugin/migrate/field/d7/NumberField.php
@@ -12,7 +12,9 @@
  *     "number_decimal" = "decimal",
  *     "number_float" = "float",
  *   },
- *   core = {7}
+ *   core = {7},
+ *   source_module = "number",
+ *   destination_module = "core"
  * )
  */
 class NumberField extends FieldPluginBase {}
diff --git a/core/modules/datetime/src/Plugin/migrate/field/DateField.php b/core/modules/datetime/src/Plugin/migrate/field/DateField.php
index 0dd56c9acf..c3e611a4d4 100644
--- a/core/modules/datetime/src/Plugin/migrate/field/DateField.php
+++ b/core/modules/datetime/src/Plugin/migrate/field/DateField.php
@@ -14,7 +14,9 @@
  *     "datestamp" =  "timestamp",
  *     "datetime" =  "datetime",
  *   },
- *   core = {6,7}
+ *   core = {6,7},
+ *   source_module = "date",
+ *   destination_module = "datetime"
  * )
  */
 class DateField extends FieldPluginBase {
diff --git a/core/modules/datetime/src/Plugin/migrate/field/d6/DateField.php b/core/modules/datetime/src/Plugin/migrate/field/d6/DateField.php
index c4e7b4981d..732fd3feea 100644
--- a/core/modules/datetime/src/Plugin/migrate/field/d6/DateField.php
+++ b/core/modules/datetime/src/Plugin/migrate/field/d6/DateField.php
@@ -16,7 +16,9 @@
  *     "datestamp" =  "timestamp",
  *     "datetime" =  "datetime",
  *   },
- *   core = {6}
+ *   core = {6},
+ *   source_module = "date",
+ *   destination_module = "datetime"
  * )
  *
  * @deprecated in Drupal 8.4.x, to be removed before Drupal 9.0.x. Use
diff --git a/core/modules/field/src/Plugin/migrate/source/d7/Field.php b/core/modules/field/src/Plugin/migrate/source/d7/Field.php
index 9e92619f8e..da9e037328 100644
--- a/core/modules/field/src/Plugin/migrate/source/d7/Field.php
+++ b/core/modules/field/src/Plugin/migrate/source/d7/Field.php
@@ -15,7 +15,7 @@
  *
  * @MigrateSource(
  *   id = "d7_field",
- *   source_module = "field"
+ *   source_module = "field_sql_storage"
  * )
  */
 class Field extends DrupalSqlBase {
diff --git a/core/modules/file/src/Plugin/migrate/cckfield/d6/FileField.php b/core/modules/file/src/Plugin/migrate/cckfield/d6/FileField.php
index 373d1e90b7..8dba3b022a 100644
--- a/core/modules/file/src/Plugin/migrate/cckfield/d6/FileField.php
+++ b/core/modules/file/src/Plugin/migrate/cckfield/d6/FileField.php
@@ -11,7 +11,9 @@
 /**
  * @MigrateCckField(
  *   id = "filefield",
- *   core = {6}
+ *   core = {6},
+ *   source_module = "filefield",
+ *   destination_module = "file"
  * )
  *
  *  @deprecated in Drupal 8.3.x, to be removed before Drupal 9.0.x. Use
diff --git a/core/modules/file/src/Plugin/migrate/cckfield/d7/FileField.php b/core/modules/file/src/Plugin/migrate/cckfield/d7/FileField.php
index 9d184ff86a..3a4cf89f3b 100644
--- a/core/modules/file/src/Plugin/migrate/cckfield/d7/FileField.php
+++ b/core/modules/file/src/Plugin/migrate/cckfield/d7/FileField.php
@@ -11,7 +11,9 @@
 /**
  * @MigrateCckField(
  *   id = "file",
- *   core = {7}
+ *   core = {7},
+ *   source_module = "file",
+ *   destination_module = "file"
  * )
  *
  * @deprecated in Drupal 8.3.x, to be removed before Drupal 9.0.x. Use
diff --git a/core/modules/file/src/Plugin/migrate/cckfield/d7/ImageField.php b/core/modules/file/src/Plugin/migrate/cckfield/d7/ImageField.php
index 45f5b9d239..7a51444109 100644
--- a/core/modules/file/src/Plugin/migrate/cckfield/d7/ImageField.php
+++ b/core/modules/file/src/Plugin/migrate/cckfield/d7/ImageField.php
@@ -10,7 +10,9 @@
 /**
  * @MigrateCckField(
  *   id = "image",
- *   core = {7}
+ *   core = {7},
+ *   source_module = "image",
+ *   destination_module = "file"
  * )
  *
  * @deprecated in Drupal 8.3.x, to be removed before Drupal 9.0.x. Use
diff --git a/core/modules/file/src/Plugin/migrate/field/d6/FileField.php b/core/modules/file/src/Plugin/migrate/field/d6/FileField.php
index 930c13b030..87c3bfe7ac 100644
--- a/core/modules/file/src/Plugin/migrate/field/d6/FileField.php
+++ b/core/modules/file/src/Plugin/migrate/field/d6/FileField.php
@@ -9,7 +9,9 @@
 /**
  * @MigrateField(
  *   id = "filefield",
- *   core = {6}
+ *   core = {6},
+ *   source_module = "filefield",
+ *   destination_module = "file"
  * )
  */
 class FileField extends FieldPluginBase {
diff --git a/core/modules/file/src/Plugin/migrate/field/d6/ImageField.php b/core/modules/file/src/Plugin/migrate/field/d6/ImageField.php
new file mode 100644
index 0000000000..3ec65ddece
--- /dev/null
+++ b/core/modules/file/src/Plugin/migrate/field/d6/ImageField.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Drupal\file\Plugin\migrate\field\d6;
+
+/**
+ * @MigrateField(
+ *   id = "imagefield",
+ *   core = {6},
+ *   source_module = "imagefield",
+ *   destination_module = "file"
+ * )
+ */
+class ImageField extends FileField {}
diff --git a/core/modules/file/src/Plugin/migrate/field/d7/FileField.php b/core/modules/file/src/Plugin/migrate/field/d7/FileField.php
index 4243d43c42..2d7e7ba387 100644
--- a/core/modules/file/src/Plugin/migrate/field/d7/FileField.php
+++ b/core/modules/file/src/Plugin/migrate/field/d7/FileField.php
@@ -8,7 +8,9 @@
 /**
  * @MigrateField(
  *   id = "file",
- *   core = {7}
+ *   core = {7},
+ *   source_module = "file",
+ *   destination_module = "file"
  * )
  */
 class FileField extends D6FileField {
diff --git a/core/modules/file/src/Plugin/migrate/field/d7/ImageField.php b/core/modules/file/src/Plugin/migrate/field/d7/ImageField.php
index 7783ae2886..fea19c667b 100644
--- a/core/modules/file/src/Plugin/migrate/field/d7/ImageField.php
+++ b/core/modules/file/src/Plugin/migrate/field/d7/ImageField.php
@@ -8,7 +8,9 @@
 /**
  * @MigrateField(
  *   id = "image",
- *   core = {7}
+ *   core = {7},
+ *   source_module = "image",
+ *   destination_module = "file"
  * )
  */
 class ImageField extends FieldPluginBase {
diff --git a/core/modules/link/src/Plugin/migrate/cckfield/LinkField.php b/core/modules/link/src/Plugin/migrate/cckfield/LinkField.php
index 8acf70a998..f902987c67 100644
--- a/core/modules/link/src/Plugin/migrate/cckfield/LinkField.php
+++ b/core/modules/link/src/Plugin/migrate/cckfield/LinkField.php
@@ -13,7 +13,9 @@
  *   core = {6},
  *   type_map = {
  *     "link_field" = "link"
- *   }
+ *   },
+ *   source_module = "link",
+ *   destination_module = "link"
  * )
  *
  * @deprecated in Drupal 8.3.x and will be removed in Drupal 9.0.x. Use
diff --git a/core/modules/link/src/Plugin/migrate/cckfield/d7/LinkField.php b/core/modules/link/src/Plugin/migrate/cckfield/d7/LinkField.php
index 6a7ff8898b..c4f09f25e5 100644
--- a/core/modules/link/src/Plugin/migrate/cckfield/d7/LinkField.php
+++ b/core/modules/link/src/Plugin/migrate/cckfield/d7/LinkField.php
@@ -13,7 +13,9 @@
  *   core = {7},
  *   type_map = {
  *     "link_field" = "link"
- *   }
+ *   },
+ *   source_module = "link",
+ *   destination_module = "link"
  * )
  *
  * This plugin provides the exact same functionality as the Drupal 6 "link"
diff --git a/core/modules/link/src/Plugin/migrate/field/d6/LinkField.php b/core/modules/link/src/Plugin/migrate/field/d6/LinkField.php
index 0d03656ad8..de0814ad66 100644
--- a/core/modules/link/src/Plugin/migrate/field/d6/LinkField.php
+++ b/core/modules/link/src/Plugin/migrate/field/d6/LinkField.php
@@ -11,7 +11,9 @@
  *   core = {6},
  *   type_map = {
  *     "link_field" = "link"
- *   }
+ *   },
+ *   source_module = "link",
+ *   destination_module = "link"
  * )
  */
 class LinkField extends FieldPluginBase {
diff --git a/core/modules/link/src/Plugin/migrate/field/d7/LinkField.php b/core/modules/link/src/Plugin/migrate/field/d7/LinkField.php
index bb71418aba..0dcf4c64c5 100644
--- a/core/modules/link/src/Plugin/migrate/field/d7/LinkField.php
+++ b/core/modules/link/src/Plugin/migrate/field/d7/LinkField.php
@@ -11,7 +11,9 @@
  *   core = {7},
  *   type_map = {
  *     "link_field" = "link"
- *   }
+ *   },
+ *   source_module = "link",
+ *   destination_module = "link"
  * )
  *
  * This plugin provides the exact same functionality as the Drupal 6 "link"
diff --git a/core/modules/migrate/tests/src/Kernel/Plugin/MigrationProvidersExistTest.php b/core/modules/migrate/tests/src/Kernel/Plugin/MigrationProvidersExistTest.php
index 8e166998c4..4759005fc8 100644
--- a/core/modules/migrate/tests/src/Kernel/Plugin/MigrationProvidersExistTest.php
+++ b/core/modules/migrate/tests/src/Kernel/Plugin/MigrationProvidersExistTest.php
@@ -2,8 +2,10 @@
 
 namespace Drupal\Tests\migrate\Kernel\Plugin;
 
+use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
 use Drupal\Component\Render\FormattableMarkup;
 use Drupal\KernelTests\FileSystemModuleDiscoveryDataProviderTrait;
+use Drupal\migrate_drupal\Plugin\MigrateFieldPluginManager;
 use Drupal\Tests\migrate_drupal\Kernel\MigrateDrupalTestBase;
 
 /**
@@ -61,4 +63,188 @@ public function testProvidersExist() {
     }
   }
 
+  /**
+   * Tests that modules exist for all field plugins.
+   */
+  public function testFieldProvidersExist() {
+    $expected_mappings = [
+      'userreference' => [
+        'source_module' => 'user_reference',
+        'destination_module' => 'entity_reference',
+      ],
+      'nodereference' => [
+        'source_module' => 'node_reference',
+        'destination_module' => 'entity_reference',
+      ],
+      'optionwidgets' => [
+        'source_module' => 'optionwidgets',
+        'destination_module' => 'options',
+      ],
+      'list' => [
+        'source_module' => 'list',
+        'destination_module' => 'options',
+      ],
+      'options' => [
+        'source_module' => 'options',
+        'destination_module' => 'options',
+      ],
+      'filefield' => [
+        'source_module' => 'filefield',
+        'destination_module' => 'file',
+      ],
+      'imagefield' => [
+        'source_module' => 'imagefield',
+        'destination_module' => 'file',
+      ],
+      'file' => [
+        'source_module' => 'file',
+        'destination_module' => 'file',
+      ],
+      'image' => [
+        'source_module' => 'image',
+        'destination_module' => 'file',
+      ],
+      'phone' => [
+        'source_module' => 'phone',
+        'destination_module' => 'telephone',
+      ],
+      'link' => [
+        'source_module' => 'link',
+        'destination_module' => 'link',
+      ],
+      'link_field' => [
+        'source_module' => 'link',
+        'destination_module' => 'link',
+      ],
+      'd6_text' => [
+        'source_module' => 'text',
+        'destination_module' => 'text',
+      ],
+      'd7_text' => [
+        'source_module' => 'text',
+        'destination_module' => 'text',
+      ],
+      'taxonomy_term_reference' => [
+        'source_module' => 'taxonomy',
+        'destination_module' => 'entity_reference',
+      ],
+      'date' => [
+        'source_module' => 'date',
+        'destination_module' => 'datetime',
+      ],
+      'datetime' => [
+        'source_module' => 'date',
+        'destination_module' => 'datetime',
+      ],
+      'email' => [
+        'source_module' => 'email',
+        'destination_module' => 'core',
+      ],
+      'number_default' => [
+        'source_module' => 'number',
+        'destination_module' => 'core',
+      ],
+      'entityreference' => [
+        'source_module' => 'entityreference',
+        'destination_module' => 'core',
+      ],
+    ];
+    // Install all available modules.
+    $module_handler = $this->container->get('module_handler');
+    $modules = $this->coreModuleListDataProvider();
+    $modules_enabled = $module_handler->getModuleList();
+    $modules_to_enable = array_keys(array_diff_key($modules, $modules_enabled));
+    $this->enableModules($modules_to_enable);
+
+    /** @var \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManager $plugin_manager */
+    $plugin_manager = $this->container->get('plugin.manager.migrate.field');
+    $definitions = $plugin_manager->getDefinitions();
+    foreach ($definitions as $key => $definition) {
+      $this->assertEquals($expected_mappings[$key]['source_module'], $plugin_manager->getSourceModule($definition));
+      $this->assertEquals($expected_mappings[$key]['destination_module'], $plugin_manager->getDestinationModule($definition));
+      unset($definitions[$key]);
+    }
+    // Make sure we test all the fields.
+    $this->assertEmpty($definitions);
+  }
+
+  /**
+   * Test a missing required definition.
+   *
+   * @param array $definitions
+   *   A field plugin definition.
+   * @param string $missing_property
+   *   The name of the property missing from the definition.
+   *
+   * @dataProvider fieldPluginDefinitionsProvider
+   */
+  public function testFieldProviderMissingRequiredProperty($definitions, $missing_property) {
+    $mock_discovery = $this->getMockBuilder(MigrateFieldPluginManager::class)
+      ->disableOriginalConstructor()
+      ->setMethods(['getDefinitions'])
+      ->getMock();
+    $mock_discovery->method('getDefinitions')
+      ->willReturn($definitions);
+    $mock_plugin_manager = $this->getMockBuilder(MigrateFieldPluginManager::class)
+      ->disableOriginalConstructor()
+      ->setMethods(['getDiscovery'])
+      ->getMock();
+    $mock_plugin_manager->method('getDiscovery')
+      ->willReturn($mock_discovery);
+    $this->setExpectedException(InvalidPluginDefinitionException::class, "The missing_{$missing_property} plugin should define the $missing_property property.");
+    $mock_plugin_manager->getDefinitions();
+  }
+
+  /**
+   * Data provider for field plugin definitions.
+   *
+   * @return array
+   */
+  public function fieldPluginDefinitionsProvider() {
+    return [
+      'missing_core_scenario' => [
+        'definitions' => [
+          'missing_core' => [
+            'source_module' => 'migrate',
+            'destination_module' => 'migrate',
+            'id' => 'missing_core',
+            'class' => 'foo',
+            'provider' => 'foo',
+          ],
+        ],
+        'missing_property' => 'core',
+      ],
+      'missing_source_scenario' => [
+        'definitions' => [
+          'missing_source_module' => [
+            'core' => [
+              0 => 6,
+              1 => 7,
+            ],
+            'destination_module' => 'migrate',
+            'id' => 'missing_source_module',
+            'class' => 'foo',
+            'provider' => 'foo',
+          ],
+        ],
+        'missing_property' => 'source_module',
+      ],
+      'missing_destination_scenario' => [
+        'definitions' => [
+          'missing_destination_module' => [
+            'core' => [
+              0 => 6,
+              1 => 7,
+            ],
+            'source_module' => 'migrate',
+            'id' => 'missing_destination_module',
+            'class' => 'foo',
+            'provider' => 'foo',
+          ],
+        ],
+        'missing_property' => 'destination_module',
+      ],
+    ];
+  }
+
 }
diff --git a/core/modules/migrate_drupal/src/Annotation/MigrateField.php b/core/modules/migrate_drupal/src/Annotation/MigrateField.php
index ad78bc924d..061b0765e3 100644
--- a/core/modules/migrate_drupal/src/Annotation/MigrateField.php
+++ b/core/modules/migrate_drupal/src/Annotation/MigrateField.php
@@ -49,6 +49,28 @@ public function __construct($values) {
    *
    * @var int[]
    */
-  public $core = [];
+  public $core;
+
+  /**
+   * Identifies the system providing the data the field plugin will read.
+   *
+   * The field plugin itself determines how the value is used. For example,
+   * Migrate Drupal's field plugins expect source_module to be the name of a
+   * module that must be installed and enabled in the source database.
+   *
+   * @var string
+   */
+  public $source_module;
+
+  /**
+   * Identifies the system handling the data the destination plugin will write.
+   *
+   * The destination plugin itself determines how the value is used. For
+   * example, Migrate Drupal's destination plugins expect destination_module to
+   * be the name of a module that must be installed on the destination.
+   *
+   * @var string
+   */
+  public $destination_module;
 
 }
diff --git a/core/modules/migrate_drupal/src/Plugin/MigrateFieldPluginManager.php b/core/modules/migrate_drupal/src/Plugin/MigrateFieldPluginManager.php
index 2b33c858b9..804d6f41d8 100644
--- a/core/modules/migrate_drupal/src/Plugin/MigrateFieldPluginManager.php
+++ b/core/modules/migrate_drupal/src/Plugin/MigrateFieldPluginManager.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\migrate_drupal\Plugin;
 
+use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
 use Drupal\Component\Plugin\Exception\PluginNotFoundException;
 use Drupal\migrate\Plugin\MigratePluginManager;
 use Drupal\migrate\Plugin\MigrationInterface;
@@ -53,4 +54,31 @@ public function getPluginIdFromFieldType($field_type, array $configuration = [],
     throw new PluginNotFoundException($field_type);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function processDefinition(&$definition, $plugin_id) {
+    parent::processDefinition($definition, $plugin_id);
+
+    foreach (['core', 'source_module', 'destination_module'] as $required_property) {
+      if (empty($definition[$required_property])) {
+        throw new InvalidPluginDefinitionException($plugin_id, sprintf('The %s plugin should define the %s property.', $definition['id'], $required_property));
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSourceModule($definition) {
+    return isset($definition['source_module']) ? $definition['source_module'] : '';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDestinationModule($definition) {
+    return isset($definition['destination_module']) ? $definition['destination_module'] : '';
+  }
+
 }
diff --git a/core/modules/migrate_drupal/src/Plugin/MigrateFieldPluginManagerInterface.php b/core/modules/migrate_drupal/src/Plugin/MigrateFieldPluginManagerInterface.php
index 7219c2b301..82a8d6bf3c 100644
--- a/core/modules/migrate_drupal/src/Plugin/MigrateFieldPluginManagerInterface.php
+++ b/core/modules/migrate_drupal/src/Plugin/MigrateFieldPluginManagerInterface.php
@@ -25,4 +25,26 @@
    */
   public function getPluginIdFromFieldType($field_type, array $configuration = [], MigrationInterface $migration = NULL);
 
+  /**
+   * Gets the source module providing the source data.
+   *
+   * @param array $definition
+   *   The field plugin definition.
+   *
+   * @return string|null
+   *   The source module or NULL if not found.
+   */
+  public function getSourceModule($definition);
+
+  /**
+   * Gets the destination module handling the destination data.
+   *
+   * @param array $definition
+   *   The field plugin definition.
+   *
+   * @return string|null
+   *   The destination module or NULL if not found.
+   */
+  public function getDestinationModule($definition);
+
 }
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/field/NodeReference.php b/core/modules/migrate_drupal/src/Plugin/migrate/field/NodeReference.php
index 1579afadc5..d3de02ccd4 100644
--- a/core/modules/migrate_drupal/src/Plugin/migrate/field/NodeReference.php
+++ b/core/modules/migrate_drupal/src/Plugin/migrate/field/NodeReference.php
@@ -11,6 +11,8 @@
  *   type_map = {
  *     "nodereference" = "entity_reference",
  *   },
+ *   source_module = "node_reference",
+ *   destination_module = "entity_reference",
  * )
  */
 class NodeReference extends FieldPluginBase {
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/field/UserReference.php b/core/modules/migrate_drupal/src/Plugin/migrate/field/UserReference.php
index 11df8ec243..9702cd69df 100644
--- a/core/modules/migrate_drupal/src/Plugin/migrate/field/UserReference.php
+++ b/core/modules/migrate_drupal/src/Plugin/migrate/field/UserReference.php
@@ -11,6 +11,8 @@
  *   type_map = {
  *     "userreference" = "entity_reference",
  *   },
+ *   source_module = "user_reference",
+ *   destination_module = "entity_reference",
  * )
  */
 class UserReference extends FieldPluginBase {
diff --git a/core/modules/migrate_drupal/tests/modules/migrate_cckfield_plugin_manager_test/src/Plugin/migrate/cckfield/D6FileField.php b/core/modules/migrate_drupal/tests/modules/migrate_cckfield_plugin_manager_test/src/Plugin/migrate/cckfield/D6FileField.php
index 1b43724991..f8a89da1ee 100644
--- a/core/modules/migrate_drupal/tests/modules/migrate_cckfield_plugin_manager_test/src/Plugin/migrate/cckfield/D6FileField.php
+++ b/core/modules/migrate_drupal/tests/modules/migrate_cckfield_plugin_manager_test/src/Plugin/migrate/cckfield/D6FileField.php
@@ -11,7 +11,9 @@
  *   core = {6},
  *   type_map = {
  *     "file" = "file"
- *   }
+ *   },
+ *   source_module = "foo",
+ *   destination_module = "bar"
  * )
  */
 class D6FileField extends CckFieldPluginBase {
diff --git a/core/modules/migrate_drupal/tests/modules/migrate_cckfield_plugin_manager_test/src/Plugin/migrate/cckfield/D6NoCoreVersionSpecified.php b/core/modules/migrate_drupal/tests/modules/migrate_cckfield_plugin_manager_test/src/Plugin/migrate/cckfield/D6NoCoreVersionSpecified.php
index 74dfd3bcd7..34bed95e08 100644
--- a/core/modules/migrate_drupal/tests/modules/migrate_cckfield_plugin_manager_test/src/Plugin/migrate/cckfield/D6NoCoreVersionSpecified.php
+++ b/core/modules/migrate_drupal/tests/modules/migrate_cckfield_plugin_manager_test/src/Plugin/migrate/cckfield/D6NoCoreVersionSpecified.php
@@ -7,7 +7,9 @@
 
 /**
  * @MigrateCckField(
- *   id = "d6_no_core_version_specified"
+ *   id = "d6_no_core_version_specified",
+ *   source_module = "foo",
+ *   destination_module = "bar",
  * )
  */
 class D6NoCoreVersionSpecified extends CckFieldPluginBase {
diff --git a/core/modules/migrate_drupal/tests/modules/migrate_field_plugin_manager_test/src/Plugin/migrate/field/D6FileField.php b/core/modules/migrate_drupal/tests/modules/migrate_field_plugin_manager_test/src/Plugin/migrate/field/D6FileField.php
index acb9d9cd7e..de55e1337a 100644
--- a/core/modules/migrate_drupal/tests/modules/migrate_field_plugin_manager_test/src/Plugin/migrate/field/D6FileField.php
+++ b/core/modules/migrate_drupal/tests/modules/migrate_field_plugin_manager_test/src/Plugin/migrate/field/D6FileField.php
@@ -10,7 +10,9 @@
  *   core = {6},
  *   type_map = {
  *     "file" = "file"
- *   }
+ *   },
+ *   source_module = "foo",
+ *   destination_module = "bar"
  * )
  */
 class D6FileField extends FieldPluginBase {}
diff --git a/core/modules/migrate_drupal/tests/modules/migrate_field_plugin_manager_test/src/Plugin/migrate/field/D6NoCoreVersionSpecified.php b/core/modules/migrate_drupal/tests/modules/migrate_field_plugin_manager_test/src/Plugin/migrate/field/D6NoCoreVersionSpecified.php
index 39f109b697..e7647a1562 100644
--- a/core/modules/migrate_drupal/tests/modules/migrate_field_plugin_manager_test/src/Plugin/migrate/field/D6NoCoreVersionSpecified.php
+++ b/core/modules/migrate_drupal/tests/modules/migrate_field_plugin_manager_test/src/Plugin/migrate/field/D6NoCoreVersionSpecified.php
@@ -6,10 +6,9 @@
 
 /**
  * @MigrateField(
- *   id = "d6_no_core_version_specified"
+ *   id = "d6_no_core_version_specified",
+ *   source_module = "foo",
+ *   destination_module = "bar",
  * )
  */
-class D6NoCoreVersionSpecified extends FieldPluginBase {
-
-
-}
+class D6NoCoreVersionSpecified extends FieldPluginBase {}
diff --git a/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php b/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php
index 0d9b53e531..09b415d75a 100644
--- a/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php
+++ b/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php
@@ -3,12 +3,14 @@
 namespace Drupal\migrate_drupal_ui\Form;
 
 use Drupal\Core\Datetime\DateFormatterInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Form\ConfirmFormBase;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\State\StateInterface;
 use Drupal\Core\Url;
 use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
+use Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface;
 use Drupal\migrate_drupal_ui\Batch\MigrateUpgradeImportBatch;
 use Drupal\migrate_drupal\MigrationConfigurationTrait;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -49,6 +51,20 @@ class MigrateUpgradeForm extends ConfirmFormBase {
   protected $pluginManager;
 
   /**
+   * The field plugin manager.
+   *
+   * @var \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface
+   */
+  protected $fieldPluginManager;
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
    * Constructs the MigrateUpgradeForm.
    *
    * @param \Drupal\Core\State\StateInterface $state
@@ -59,12 +75,18 @@ class MigrateUpgradeForm extends ConfirmFormBase {
    *   The renderer service.
    * @param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $plugin_manager
    *   The migration plugin manager.
+   * @param \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface $field_plugin_manager
+   *   The field plugin manager.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
    */
-  public function __construct(StateInterface $state, DateFormatterInterface $date_formatter, RendererInterface $renderer, MigrationPluginManagerInterface $plugin_manager) {
+  public function __construct(StateInterface $state, DateFormatterInterface $date_formatter, RendererInterface $renderer, MigrationPluginManagerInterface $plugin_manager, MigrateFieldPluginManagerInterface $field_plugin_manager, ModuleHandlerInterface $module_handler) {
     $this->state = $state;
     $this->dateFormatter = $date_formatter;
     $this->renderer = $renderer;
     $this->pluginManager = $plugin_manager;
+    $this->fieldPluginManager = $field_plugin_manager;
+    $this->moduleHandler = $module_handler;
   }
 
   /**
@@ -75,7 +97,9 @@ public static function create(ContainerInterface $container) {
       $container->get('state'),
       $container->get('date.formatter'),
       $container->get('renderer'),
-      $container->get('plugin.manager.migration')
+      $container->get('plugin.manager.migration'),
+      $container->get('plugin.manager.migrate.field'),
+      $container->get('module_handler')
     );
   }
 
@@ -439,7 +463,12 @@ public function submitCredentialForm(array &$form, FormStateInterface $form_stat
   }
 
   /**
-   * Confirmation form for missing migrations, etc.
+   * Confirmation form showing available and missing migration paths.
+   *
+   * The confirmation form uses the source_module and destination_module
+   * properties on the source, destination and field plugins as well as the
+   * system data from the source to determine if there is a migration path for
+   * each module in the source.
    *
    * @param array $form
    *   An associative array containing the structure of the form.
@@ -456,8 +485,9 @@ public function buildConfirmForm(array $form, FormStateInterface $form_state) {
     $form['actions']['submit']['#value'] = $this->t('Perform upgrade');
 
     $version = $form_state->get('version');
-    $migrations = $this->getMigrations('migrate_drupal_' . $version, $version);
 
+    // Get the source_module and destination_module for each migration.
+    $migrations = $this->getMigrations('migrate_drupal_' . $version, $version);
     $table_data = [];
     foreach ($migrations as $migration) {
       $migration_id = $migration->getPluginId();
@@ -474,6 +504,23 @@ public function buildConfirmForm(array $form, FormStateInterface $form_state) {
         $table_data[$source_module][$destination_module][$migration_id] = $migration->label();
       }
     }
+
+    // Get the source_module and destination_module from the field plugins.
+    $definitions = $this->fieldPluginManager->getDefinitions();
+    foreach ($definitions as $definition) {
+      $source_module = $this->fieldPluginManager->getSourceModule($definition);
+      if (!$source_module) {
+        drupal_set_message($this->t('Source module not found for field plugin @id.', ['@id' => $definition['id']]), 'error');
+      }
+      $destination_module = $this->fieldPluginManager->getDestinationModule($definition);
+      if (!$destination_module) {
+        drupal_set_message($this->t('Destination module not found for field plugin @mid.', ['@id' => $definition['id']]), 'error');
+      }
+      if ($source_module && $destination_module) {
+        $table_data[$source_module][$destination_module][$definition['id']] = $definition['id'];
+      }
+    }
+
     // Sort the table by source module names and within that destination
     // module names.
     ksort($table_data);
@@ -485,6 +532,11 @@ public function buildConfirmForm(array $form, FormStateInterface $form_state) {
     $system_data = $form_state->get('system_data');
     $unmigrated_source_modules = array_diff_key($system_data['module'], $table_data);
 
+    // Remove core profiles from the list.
+    foreach (['standard', 'minimal'] as $profile) {
+      unset($unmigrated_source_modules[$profile]);
+    }
+
     // Missing migrations.
     $form['missing_module_list_title'] = [
       '#type' => 'item',
diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php
index 3ddd36149e..180414f422 100644
--- a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php
+++ b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php
@@ -178,6 +178,21 @@ public function testMigrateUpgrade() {
     $this->assertSession()->pageTextNotContains(t('Destination module not found for migration_provider_test'));
     // Ensure there are no errors about any other missing migration providers.
     $this->assertSession()->pageTextNotContains(t('module not found'));
+
+    // Test the available migration paths.
+    $all_available = $this->getAvailablePaths();
+    foreach ($all_available as $available) {
+      $this->assertSession()->elementExists('xpath', '//table[@id="edit-available-module-list-module-list"]//td[text()="' . $available . '"]');
+      $this->assertSession()->elementNotExists('xpath', '//table[@id="edit-missing-module-list"]//td[text()="' . $available . '"]');
+    }
+
+    // Test the missing migration paths.
+    $all_missing = $this->getMissingPaths();
+    foreach ($all_missing as $missing) {
+      $this->assertSession()->elementExists('xpath', '//table[@id="edit-missing-module-list"]//td[text()="' . $missing . '"]');
+      $this->assertSession()->elementNotExists('xpath', '//table[@id="edit-available-module-list-module-list"]//td[text()="' . $missing . '"]');
+    }
+
     $this->drupalPostForm(NULL, [], t('Perform upgrade'));
     $this->assertText(t('Congratulations, you upgraded Drupal!'));
 
@@ -259,4 +274,20 @@ protected function translatePostValues(array $values) {
    */
   abstract protected function getEntityCounts();
 
+  /**
+   * Gets the available upgrade paths.
+   *
+   * @return int[]
+   *   An array of expected counts keyed by entity type ID.
+   */
+  abstract protected function getAvailablePaths();
+
+  /**
+   * Gets the missing upgrade paths.
+   *
+   * @return int[]
+   *   An array of expected counts keyed by entity type ID.
+   */
+  abstract protected function getMissingPaths();
+
 }
diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/d6/MigrateUpgrade6Test.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d6/MigrateUpgrade6Test.php
index 993fdf493d..b9beeb9aec 100644
--- a/core/modules/migrate_drupal_ui/tests/src/Functional/d6/MigrateUpgrade6Test.php
+++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d6/MigrateUpgrade6Test.php
@@ -76,6 +76,72 @@ protected function getEntityCounts() {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function getAvailablePaths() {
+    return [
+      'aggregator',
+      'block',
+      'book',
+      'comment',
+      'contact',
+      'content',
+      'date',
+      'dblog',
+      'email',
+      'entityreference',
+      'file',
+      'filefield',
+      'filter',
+      'i18ntaxonomy',
+      'image',
+      'imagecache',
+      'imagefield',
+      'link',
+      'list',
+      'menu',
+      'locale',
+      'node',
+      'node_reference',
+      'number',
+      'options',
+      'optionwidgets',
+      'path',
+      'phone',
+      'profile',
+      'search',
+      'system',
+      'taxonomy',
+      'text',
+      'translation',
+      'upload',
+      'user',
+      'user_reference',
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMissingPaths() {
+    return [
+      'date_api',
+      'date_timezone',
+      'event',
+      'i18n',
+      'i18nblocks',
+      'i18ncck',
+      'i18ncontent',
+      'i18nmenu',
+      'i18nprofile',
+      'i18nstrings',
+      'imageapi',
+      'php',
+      'variable_admin',
+    ];
+  }
+
+  /**
    * Executes all steps of migrations upgrade.
    */
   public function testMigrateUpgrade() {
diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php
index a3ebb50e53..4a7afbe9e9 100644
--- a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php
+++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php
@@ -82,6 +82,74 @@ protected function getEntityCounts() {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function getAvailablePaths() {
+    return [
+      'aggregator',
+      'block',
+      'comment',
+      'contact',
+      'date',
+      'dblog',
+      'email',
+      'entityreference',
+      'field',
+      'field_sql_storage',
+      'file',
+      'filefield',
+      'filter',
+      'forum',
+      'image',
+      'imagefield',
+      'link',
+      'list',
+      'locale',
+      'menu',
+      'node',
+      'node_reference',
+      'number',
+      'options',
+      'optionwidgets',
+      'path',
+      'phone',
+      'search',
+      'shortcut',
+      'statistics',
+      'system',
+      'taxonomy',
+      'text',
+      'translation',
+      'user',
+      'user_reference',
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMissingPaths() {
+    return [
+      'blog',
+      'book',
+      'color',
+      'contextual',
+      'date_api',
+      'entity',
+      'field_ui',
+      'help',
+      'php',
+      'rdf',
+      'simpletest',
+      'syslog',
+      'toolbar',
+      'tracker',
+      'trigger',
+      'update',
+    ];
+  }
+
+  /**
    * Executes all steps of migrations upgrade.
    */
   public function testMigrateUpgrade() {
diff --git a/core/modules/options/src/Plugin/migrate/field/d6/OptionWidgetsField.php b/core/modules/options/src/Plugin/migrate/field/d6/OptionWidgetsField.php
new file mode 100644
index 0000000000..b6b5516aa0
--- /dev/null
+++ b/core/modules/options/src/Plugin/migrate/field/d6/OptionWidgetsField.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Drupal\options\Plugin\migrate\field\d7;
+
+use Drupal\migrate_drupal\Plugin\migrate\field\FieldPluginBase;
+
+/**
+ * @MigrateField(
+ *   id = "optionwidgets",
+ *   core = {6},
+ *   source_module = "optionwidgets",
+ *   destination_module = "options"
+ * )
+ */
+class OptionWidgetsField extends FieldPluginBase {}
diff --git a/core/modules/options/src/Plugin/migrate/field/d7/ListField.php b/core/modules/options/src/Plugin/migrate/field/d7/ListField.php
index fff29ee177..714c364b8f 100644
--- a/core/modules/options/src/Plugin/migrate/field/d7/ListField.php
+++ b/core/modules/options/src/Plugin/migrate/field/d7/ListField.php
@@ -12,7 +12,9 @@
  *     "list_integer" = "list_integer",
  *     "list_text" = "list_string",
  *   },
- *   core = {7}
+ *   core = {7},
+ *   source_module = "list",
+ *   destination_module = "options"
  * )
  */
 class ListField extends FieldPluginBase {}
diff --git a/core/modules/options/src/Plugin/migrate/field/d7/OptionsField.php b/core/modules/options/src/Plugin/migrate/field/d7/OptionsField.php
new file mode 100644
index 0000000000..e54ccb3e37
--- /dev/null
+++ b/core/modules/options/src/Plugin/migrate/field/d7/OptionsField.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Drupal\options\Plugin\migrate\field\d7;
+
+use Drupal\migrate_drupal\Plugin\migrate\field\FieldPluginBase;
+
+/**
+ * @MigrateField(
+ *   id = "options",
+ *   core = {7},
+ *   source_module = "options",
+ *   destination_module = "options"
+ * )
+ */
+class OptionsField extends FieldPluginBase {}
diff --git a/core/modules/taxonomy/src/Plugin/migrate/cckfield/TaxonomyTermReference.php b/core/modules/taxonomy/src/Plugin/migrate/cckfield/TaxonomyTermReference.php
index 27cb8d355f..0c72879598 100644
--- a/core/modules/taxonomy/src/Plugin/migrate/cckfield/TaxonomyTermReference.php
+++ b/core/modules/taxonomy/src/Plugin/migrate/cckfield/TaxonomyTermReference.php
@@ -13,7 +13,9 @@
  *   type_map = {
  *     "taxonomy_term_reference" = "entity_reference"
  *   },
- *   core = {6,7}
+ *   core = {6,7},
+ *   source_module = "taxonomy",
+ *   destination_module = "entity_reference",
  * )
  *
  * @deprecated in Drupal 8.4.x, to be removed before Drupal 9.0.x. Use
diff --git a/core/modules/taxonomy/src/Plugin/migrate/field/TaxonomyTermReference.php b/core/modules/taxonomy/src/Plugin/migrate/field/TaxonomyTermReference.php
index 56ec20ac27..afb5458de0 100644
--- a/core/modules/taxonomy/src/Plugin/migrate/field/TaxonomyTermReference.php
+++ b/core/modules/taxonomy/src/Plugin/migrate/field/TaxonomyTermReference.php
@@ -11,7 +11,9 @@
  *   type_map = {
  *     "taxonomy_term_reference" = "entity_reference"
  *   },
- *   core = {6,7}
+ *   core = {6,7},
+ *   source_module = "taxonomy",
+ *   destination_module = "entity_reference",
  * )
  */
 class TaxonomyTermReference extends FieldPluginBase {
diff --git a/core/modules/telephone/src/Plugin/migrate/field/d7/PhoneField.php b/core/modules/telephone/src/Plugin/migrate/field/d7/PhoneField.php
index 3164cecdca..d98f344344 100644
--- a/core/modules/telephone/src/Plugin/migrate/field/d7/PhoneField.php
+++ b/core/modules/telephone/src/Plugin/migrate/field/d7/PhoneField.php
@@ -10,7 +10,9 @@
  *   type_map = {
  *     "phone" = "telephone",
  *   },
- *   core = {7}
+ *   core = {7},
+ *   source_module = "phone",
+ *   destination_module = "telephone"
  * )
  */
 class PhoneField extends FieldPluginBase {}
diff --git a/core/modules/text/src/Plugin/migrate/cckfield/TextField.php b/core/modules/text/src/Plugin/migrate/cckfield/TextField.php
index e2511de836..394f104173 100644
--- a/core/modules/text/src/Plugin/migrate/cckfield/TextField.php
+++ b/core/modules/text/src/Plugin/migrate/cckfield/TextField.php
@@ -16,7 +16,9 @@
  *     "text_long" = "text_long",
  *     "text_with_summary" = "text_with_summary"
  *   },
- *   core = {6,7}
+ *   core = {6,7},
+ *   source_module = "text",
+ *   destination_module = "text",
  * )
  *
  * @deprecated in Drupal 8.3.x, to be removed before Drupal 9.0.x. Use
diff --git a/core/modules/text/src/Plugin/migrate/field/d6/TextField.php b/core/modules/text/src/Plugin/migrate/field/d6/TextField.php
index 62deeee10c..cd1e2f7b29 100644
--- a/core/modules/text/src/Plugin/migrate/field/d6/TextField.php
+++ b/core/modules/text/src/Plugin/migrate/field/d6/TextField.php
@@ -14,7 +14,9 @@
  *     "text_long" = "text_long",
  *     "text_with_summary" = "text_with_summary"
  *   },
- *   core = {6}
+ *   core = {6},
+ *   source_module = "text",
+ *   destination_module = "text",
  * )
  */
 class TextField extends FieldPluginBase {
diff --git a/core/modules/text/src/Plugin/migrate/field/d7/TextField.php b/core/modules/text/src/Plugin/migrate/field/d7/TextField.php
index ccdedd427a..6433ed33d7 100644
--- a/core/modules/text/src/Plugin/migrate/field/d7/TextField.php
+++ b/core/modules/text/src/Plugin/migrate/field/d7/TextField.php
@@ -14,7 +14,9 @@
  *     "text_long" = "text_long",
  *     "text_with_summary" = "text_with_summary"
  *   },
- *   core = {7}
+ *   core = {7},
+ *   source_module = "text",
+ *   destination_module = "text",
  * )
  */
 class TextField extends FieldPluginBase {
