diff --git a/src/Annotation/YamlContentProcess.php b/src/Annotation/YamlContentProcess.php
new file mode 100644
index 0000000..218d083
--- /dev/null
+++ b/src/Annotation/YamlContentProcess.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Drupal\yaml_content\Annotation;
+
+use Drupal\Component\Annotation\Plugin;
+
+/**
+ * Class YamlContentProcess.
+ *
+ * @see plugin_api
+ *
+ * @Annotation
+ */
+class YamlContentProcess extends Plugin {
+
+}
diff --git a/src/ContentLoader/ContentLoader.php b/src/ContentLoader/ContentLoader.php
index aba7d5a..f1e12c5 100644
--- a/src/ContentLoader/ContentLoader.php
+++ b/src/ContentLoader/ContentLoader.php
@@ -2,11 +2,12 @@
 
 namespace Drupal\yaml_content\ContentLoader;
 
-use Drupal\Core\Config\ConfigValueException;
+use Drupal\Component\Plugin\Exception\PluginNotFoundException;
 use Drupal\Core\Entity\FieldableEntityInterface;
 use Drupal\Core\Field\EntityReferenceFieldItemList;
 use Drupal\Core\Field\FieldException;
 use Drupal\Core\TypedData\Exception\MissingDataException;
+use Drupal\yaml_content\Plugin\ProcessingContext;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\Yaml\Parser;
 use Drupal\yaml_content\Event\YamlContentEvents;
@@ -15,7 +16,6 @@
 use Drupal\yaml_content\Event\EntityPostSaveEvent;
 use Drupal\yaml_content\Event\FieldImportEvent;
 use Drupal\yaml_content\Event\EntityImportEvent;
-use Drupal\Component\Render\PlainTextOutput;
 
 /**
  * ContentLoader class for parsing and importing YAML content.
@@ -25,7 +25,7 @@ class ContentLoader implements ContentLoaderInterface {
   /**
    * Dependency injection container.
    *
-   * @var \Symfony\Component\DependencyInjection\ContainerInterface $container
+   * @var \Symfony\Component\DependencyInjection\ContainerInterface
    */
   protected $container;
 
@@ -92,6 +92,11 @@ class ContentLoader implements ContentLoaderInterface {
    */
   protected $contentFile;
 
+  /**
+   * @var \Drupal\yaml_content\Plugin\YamlContentProcessManager
+   */
+  protected $processManager;
+
   /**
    * ContentLoader constructor.
    *
@@ -157,6 +162,22 @@ protected function getEntityLoadHelper() {
     return $this->entityLoadHelper;
   }
 
+  /**
+   * Get the ProcessManager service.
+   *
+   * @return \Drupal\yaml_content\Plugin\YamlContentProcessManager
+   *   The ProcessManager service.
+   */
+  protected function getProcessManager() {
+    // Lazy load the entity load helper service.
+    if (!isset($this->processManager)) {
+      $this->processManager = $this->container
+        ->get('plugin.manager.yaml_content.process');
+    }
+
+    return $this->processManager;
+  }
+
   /**
    * Get the module handler service.
    *
@@ -196,6 +217,13 @@ public function setContentPath($path) {
     $this->path = $path;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getContentPath() {
+    return $this->path;
+  }
+
   /**
    * Returns whether or not the system should check for previous demo content.
    *
@@ -435,7 +463,7 @@ public function populateEntityFields(FieldableEntityInterface $entity, array $fi
       catch (MissingDataException $exception) {
         watchdog_exception('yaml_content', $exception);
       }
-      catch (ConfigValueException $exception) {
+      catch (PluginNotFoundException $exception) {
         watchdog_exception('yaml_content', $exception);
       }
     }
@@ -497,8 +525,15 @@ public function populateField($field, array &$field_data) {
 
     // Iterate over each field data value and process it.
     foreach ($field_data as &$item_data) {
+      if (isset($field_data['#process']['dependency'])) {
+        $dependency = $field_data['#process']['dependency'];
+        $this->processDependency($dependency);
+      }
       // Preprocess the field data.
-      $this->preprocessFieldData($field, $item_data);
+      $context = new ProcessingContext();
+      $context->setField($field);
+      $context->setContentLoader($this);
+      $this->getProcessManager()->preprocessFieldData($context, $item_data);
 
       // Check if the field is a reference field. If so, build the entity ref.
       $is_reference = isset($item_data['entity']);
@@ -524,66 +559,6 @@ public function populateField($field, array &$field_data) {
     }
   }
 
-  /**
-   * Run any designated preprocessors on the provided field data.
-   *
-   * Preprocessors are expected to be provided in the following format:
-   *
-   * ```yaml
-   *   '#process':
-   *     callback: '<callback string>'
-   *     args:
-   *       - <callback argument 1>
-   *       - <callback argument 2>
-   *       - <...>
-   * ```
-   *
-   * The callback function receives the following arguments:
-   *
-   *   - `$field`
-   *   - `$field_data`
-   *   - <callback argument 1>
-   *   - <callback argument 2>
-   *   - <...>
-   *
-   * The `$field_data` array is passed by reference and may be modified directly
-   * by the callback implementation.
-   *
-   * @param object $field
-   *   The entity field object.
-   * @param array|string $field_data
-   *   The field data.
-   */
-  protected function preprocessFieldData($field, &$field_data) {
-    // Break here if the field data is not an array since there can be no
-    // processing instructions included.
-    if (!is_array($field_data)) {
-      return;
-    }
-
-    // Check for a callback processor defined at the value level.
-    if (isset($field_data['#process']) && isset($field_data['#process']['callback'])) {
-      $callback_type = $field_data['#process']['callback'];
-      $process_method = $callback_type . 'EntityLoad';
-      if (isset($field_data['#process']['dependency'])) {
-        $dependency = $field_data['#process']['dependency'];
-        $this->processDependency($dependency);
-      }
-      // Check to see if this class has a method in the format of
-      // '{fieldType}EntityLoad'.
-      if (method_exists($this, $process_method)) {
-        // Append callback arguments to field object and value data.
-        $args = array_merge([$field, &$field_data], isset($field_data['#process']['args']) ? $field_data['#process']['args'] : []);
-        // Pass in the arguments and call the method.
-        call_user_func_array([$this, $process_method], $args);
-      }
-      // If the method does not exist, throw an exception.
-      else {
-        throw new ConfigValueException('Unknown type specified: ' . $callback_type);
-      }
-    }
-  }
-
   /**
    * Process a file flagged as a dependency.
    *
@@ -596,114 +571,6 @@ protected function processDependency($dependency) {
     $sub_loader->loadContent($dependency, $this->existenceCheck());
   }
 
-  /**
-   * Processor function for querying and loading a referenced entity.
-   *
-   * @param object $field
-   *   The entity field object.
-   * @param array $field_data
-   *   The field data.
-   * @param string $entity_type
-   *   The entity type.
-   * @param array $filter_params
-   *   The filters for the query conditions.
-   *
-   * @return array|int
-   *   The entity id.
-   *
-   * @throws \Drupal\Core\TypedData\Exception\MissingDataException
-   *   Error for missing data.
-   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
-   *
-   * @see ContentLoader::preprocessFieldData()
-   */
-  protected function referenceEntityLoad($field, array &$field_data, $entity_type, array $filter_params) {
-    // Use query factory to create a query object for the node of entity_type.
-    $query = \Drupal::entityQuery($entity_type);
-
-    // Apply filter parameters.
-    foreach ($filter_params as $property => $value) {
-      $query->condition($property, $value);
-    }
-
-    $entity_ids = $query->execute();
-
-    if (empty($entity_ids)) {
-      $entity = $this->getEntityStorage($entity_type)->create($filter_params);
-      $entity_ids = [$entity->id()];
-    }
-
-    if (empty($entity_ids)) {
-      return $this->throwParamError('Unable to find referenced content', $entity_type, $filter_params);
-    }
-
-    // Use the first match for our value.
-    $field_data['target_id'] = array_shift($entity_ids);
-
-    // Remove process data to avoid issues when setting the value.
-    unset($field_data['#process']);
-
-    return $entity_ids;
-  }
-
-  /**
-   * Processor function for processing and loading a file attachment.
-   *
-   * @param object $field
-   *   The entity field object.
-   * @param array $field_data
-   *   The field data.
-   * @param string $entity_type
-   *   The entity type.
-   * @param array $filter_params
-   *   The filters for the query conditions.
-   *
-   * @return array|int
-   *   The entity id.
-   *
-   * @throws \Drupal\Core\TypedData\Exception\MissingDataException
-   *   Error for missing data.
-   *
-   * @see ContentLoader::preprocessFieldData()
-   */
-  protected function fileEntityLoad($field, array &$field_data, $entity_type, array $filter_params) {
-    $filename = $filter_params['filename'];
-    $directory = '/data_files/';
-    // If the entity type is an image, look in to the /images directory.
-    if ($entity_type == 'image') {
-      $directory = '/images/';
-    }
-    $output = file_get_contents($this->path . $directory . $filename);
-    if ($output !== FALSE) {
-      $destination = $field->getSetting('uri_scheme') . '://';
-      // Look-up the field's directory configuation.
-      if ($directory = $field->getSetting('file_directory')) {
-        $directory = trim($directory, '/');
-        $directory = PlainTextOutput::renderFromHtml(\Drupal::token()->replace($directory));
-        if ($directory) {
-          $destination .= $directory . '/';
-        }
-      }
-
-      // Create the destination directory if it does not already exist.
-      file_prepare_directory($destination, FILE_CREATE_DIRECTORY);
-
-      // Save the file data or return an existing file.
-      $file = file_save_data($output, $destination . $filename, FILE_EXISTS_REPLACE);
-
-      // Use the newly created file id as the value.
-      $field_data['target_id'] = $file->id();
-
-      // Remove process data to avoid issues when setting the value.
-      unset($field_data['#process']);
-
-      return $file->id();
-    }
-    else {
-      return $this->throwParamError('Unable to process file content', $entity_type, $filter_params);
-    }
-  }
-
   /**
    * Query if a target entity already exists and should be updated.
    *
@@ -753,29 +620,4 @@ public function entityExists($entity_type, array $content_data) {
     return isset($entity) ? $entity : FALSE;
   }
 
-  /**
-   * Prepare an error message and throw error.
-   *
-   * @param string $error_message
-   *   The error message to display.
-   * @param string $entity_type
-   *   The entity type.
-   * @param array $filter_params
-   *   The filters for the query conditions.
-   */
-  protected function throwParamError($error_message, $entity_type, array $filter_params) {
-    // Build parameter output description for error message.
-    $error_params = [
-      '[',
-      '  "entity_type" => ' . $entity_type . ',',
-    ];
-    foreach ($filter_params as $key => $value) {
-      $error_params[] = sprintf("  '%s' => '%s',", $key, $value);
-    }
-    $error_params[] = ']';
-    $param_output = implode("\n", $error_params);
-
-    throw new MissingDataException(__CLASS__ . ': ' . $error_message . ': ' . $param_output);
-  }
-
 }
diff --git a/src/ContentLoader/ContentLoaderInterface.php b/src/ContentLoader/ContentLoaderInterface.php
index 7085982..1d9680d 100644
--- a/src/ContentLoader/ContentLoaderInterface.php
+++ b/src/ContentLoader/ContentLoaderInterface.php
@@ -17,6 +17,14 @@
    */
   public function setContentPath($path);
 
+  /**
+   * Get a path prefix for all content files to be loaded from.
+   *
+   * @return string
+   *   The path for where all content files will be loaded from.
+   */
+  public function getContentPath();
+
   /**
    * Parse the given yaml content file into an array.
    *
diff --git a/src/Plugin/ProcessingContext.php b/src/Plugin/ProcessingContext.php
new file mode 100644
index 0000000..95910d2
--- /dev/null
+++ b/src/Plugin/ProcessingContext.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace Drupal\yaml_content\Plugin;
+
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\yaml_content\ContentLoader\ContentLoaderInterface;
+
+/**
+ * The contextual data for content being actively loaded.
+ */
+class ProcessingContext {
+
+  /**
+   * The field currently being processed.
+   *
+   * @var \Drupal\Core\Field\FieldItemListInterface
+   */
+  protected $field;
+
+  /**
+   * The active content loader instance.
+   *
+   * @var \Drupal\yaml_content\ContentLoader\ContentLoaderInterface
+   */
+  protected $contentLoader;
+
+  /**
+   * Set the field context.
+   *
+   * @param \Drupal\Core\Field\FieldItemListInterface $field
+   *   The field currently being processed.
+   */
+  public function setField(FieldItemListInterface $field) {
+    $this->field = $field;
+  }
+
+  /**
+   * Get the field context.
+   *
+   * @return \Drupal\Core\Field\FieldItemListInterface
+   *   The field currently being processed.
+   */
+  public function getField() {
+    if (!isset($this->field)) {
+      // @todo Impelment a more specific exception.
+      throw new \Exception('Missing field context.');
+    }
+    return $this->field;
+  }
+
+  /**
+   * Set the content loader context.
+   *
+   * @param \Drupal\yaml_content\ContentLoader\ContentLoaderInterface $contentLoader
+   *   The content loader instance actively loading content.
+   */
+  public function setContentLoader(ContentLoaderInterface $contentLoader) {
+    $this->contentLoader = $contentLoader;
+  }
+
+  /**
+   * Get the content loader context.
+   *
+   * @return \Drupal\yaml_content\ContentLoader\ContentLoaderInterface
+   *   The content loader instance actively loading content.
+   */
+  public function getContentLoader() {
+    if (!isset($this->contentLoader)) {
+      // @todo Impelment a more specific exception.
+      throw new \Exception('Missing content loader context.');
+    }
+    return $this->contentLoader;
+  }
+
+}
diff --git a/src/Plugin/YamlContentProcessBase.php b/src/Plugin/YamlContentProcessBase.php
new file mode 100644
index 0000000..a610a9b
--- /dev/null
+++ b/src/Plugin/YamlContentProcessBase.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Drupal\yaml_content\Plugin;
+
+use Drupal\Core\Plugin\PluginBase;
+use Drupal\Core\TypedData\Exception\MissingDataException;
+
+/**
+ * Defines a base processor implementation that most processors will extend.
+ */
+abstract class YamlContentProcessBase extends PluginBase implements YamlContentProcessInterface {
+
+  /**
+   * Prepare an error message and throw error.
+   *
+   * @param string $error_message
+   *   The error message to display.
+   * @param string $entity_type
+   *   The entity type.
+   * @param array $filter_params
+   *   The filters for the query conditions.
+   */
+  protected function throwParamError($error_message, $entity_type, array $filter_params) {
+    // Build parameter output description for error message.
+    $error_params = [
+      '[',
+      '  "entity_type" => ' . $entity_type . ',',
+    ];
+    foreach ($filter_params as $key => $value) {
+      $error_params[] = sprintf("  '%s' => '%s',", $key, $value);
+    }
+    $error_params[] = ']';
+    $param_output = implode("\n", $error_params);
+
+    throw new MissingDataException(__CLASS__ . ': ' . $error_message . ': ' . $param_output);
+  }
+
+}
diff --git a/src/Plugin/YamlContentProcessInterface.php b/src/Plugin/YamlContentProcessInterface.php
new file mode 100644
index 0000000..d30fbbe
--- /dev/null
+++ b/src/Plugin/YamlContentProcessInterface.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Drupal\yaml_content\Plugin;
+
+/**
+ * An interface for all YAML Content process plugins to implement.
+ */
+interface YamlContentProcessInterface {
+
+  /**
+   * Processes field data.
+   *
+   * @param \Drupal\yaml_content\Plugin\ProcessingContext $context
+   *   The processing context.
+   * @param array $field_data
+   *   The field data.
+   *
+   * @return array|int
+   *   The entity id.
+   *
+   * @throws \Drupal\Core\TypedData\Exception\MissingDataException
+   *   Error for missing data.
+   *
+   * @see \Drupal\yaml_content\Plugin\YamlContentProcessManager::preprocessFieldData()
+   */
+  public function process(ProcessingContext $context, array &$field_data);
+
+}
diff --git a/src/Plugin/YamlContentProcessManager.php b/src/Plugin/YamlContentProcessManager.php
new file mode 100644
index 0000000..2659811
--- /dev/null
+++ b/src/Plugin/YamlContentProcessManager.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace Drupal\yaml_content\Plugin;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
+use Drupal\yaml_content\Annotation\YamlContentProcess;
+
+/**
+ * Manages discovery and instantiation of YAML Content process plugins.
+ */
+class YamlContentProcessManager extends DefaultPluginManager {
+
+  /**
+   * Constructs a YamlContentProcessManager object.
+   *
+   * @param \Traversable $namespaces
+   *   An object that implements \Traversable which contains the root paths
+   *   keyed by the corresponding namespace to look for plugin implementations.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   Cache backend instance to use.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   */
+  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
+    parent::__construct("Plugin/yaml_content/process", $namespaces, $module_handler,
+      YamlContentProcessInterface::class,
+      YamlContentProcess::class);
+    $this->setCacheBackend($cache_backend, 'yaml_content_process_plugins');
+  }
+
+  /**
+   * Run any designated preprocessors on the provided field data.
+   *
+   * Preprocessors are expected to be provided in the following format:
+   *
+   * ```yaml
+   *   '#process':
+   *     callback: '<callback string>'
+   *     args:
+   *       - <callback argument 1>
+   *       - <callback argument 2>
+   *       - <...>
+   * ```
+   *
+   * The callback function receives the following arguments:
+   *
+   *   - `$field`
+   *   - `$field_data`
+   *   - <callback argument 1>
+   *   - <callback argument 2>
+   *   - <...>
+   *
+   * The `$field_data` array is passed by reference and may be modified directly
+   * by the callback implementation.
+   *
+   * @param \Drupal\yaml_content\Plugin\ProcessingContext $context
+   *   The processing context.
+   * @param array|string $field_data
+   *   The field data.
+   *
+   * @throws \Drupal\Core\TypedData\Exception\MissingDataException
+   */
+  public function preprocessFieldData(ProcessingContext $context, &$field_data) {
+    // Break here if the field data is not an array since there can be no
+    // processing instructions included.
+    if (!is_array($field_data)) {
+      return;
+    }
+
+    // If there is no process element skip trying to process.
+    if (!isset($field_data['#process'])) {
+      return;
+    }
+
+    // If there is no process element skip trying to process.
+    if (isset($field_data['#process'])) {
+      $process_config = $field_data['#process'];
+      if (isset($process_config['callback'])) {
+        $plugin_id = $process_config['callback'];
+        /** @var \Drupal\yaml_content\Plugin\YamlContentProcessInterface $plugin */
+        $plugin = $this->createInstance($plugin_id, $process_config['args']);
+        $plugin->process($context, $field_data);
+      }
+    }
+  }
+
+}
diff --git a/src/Plugin/yaml_content/process/File.php b/src/Plugin/yaml_content/process/File.php
new file mode 100644
index 0000000..651003f
--- /dev/null
+++ b/src/Plugin/yaml_content/process/File.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace Drupal\yaml_content\Plugin\yaml_content\process;
+
+use Drupal\Component\Render\PlainTextOutput;
+use Drupal\yaml_content\Plugin\ProcessingContext;
+use Drupal\yaml_content\Plugin\YamlContentProcessBase;
+use Drupal\yaml_content\Plugin\YamlContentProcessInterface;
+
+/**
+ * Plugin for processing and loading a file attachment.
+ *
+ * @YamlContentProcess(
+ *   id = "file",
+ *   title = @Translation("File Processor"),
+ *   description = @Translation("Processing and loading a file attachment.")
+ * )
+ */
+class File extends YamlContentProcessBase implements YamlContentProcessInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function process(ProcessingContext $context, array &$field_data) {
+    $entity_type = $this->configuration[0];
+    $filter_params = $this->configuration[1];
+
+    $filename = $filter_params['filename'];
+    $directory = '/data_files/';
+    // If the entity type is an image, look in to the /images directory.
+    if ($entity_type == 'image') {
+      $directory = '/images/';
+    }
+    // TODO path not set.
+    $output = file_get_contents($context->getContentLoader()->getContentPath() . $directory . $filename);
+    if ($output !== FALSE) {
+      $destination = 'public://';
+      // Look-up the field's directory configuation.
+      if ($directory = $context->getField()->getSetting('file_directory')) {
+        $directory = trim($directory, '/');
+        $directory = PlainTextOutput::renderFromHtml(\Drupal::token()->replace($directory));
+        if ($directory) {
+          $destination .= $directory . '/';
+        }
+      }
+
+      // Create the destination directory if it does not already exist.
+      file_prepare_directory($destination, FILE_CREATE_DIRECTORY);
+
+      // Save the file data or return an existing file.
+      $file = file_save_data($output, $destination . $filename, FILE_EXISTS_REPLACE);
+
+      // Use the newly created file id as the value.
+      $field_data['target_id'] = $file->id();
+
+      // Remove process data to avoid issues when setting the value.
+      unset($field_data['#process']);
+
+      return $file->id();
+    }
+    $this->throwParamError('Unable to process file content', $entity_type, $filter_params);
+  }
+
+}
diff --git a/src/Plugin/yaml_content/process/Reference.php b/src/Plugin/yaml_content/process/Reference.php
new file mode 100644
index 0000000..95010e8
--- /dev/null
+++ b/src/Plugin/yaml_content/process/Reference.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace Drupal\yaml_content\Plugin\yaml_content\process;
+
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\yaml_content\Plugin\ProcessingContext;
+use Drupal\yaml_content\Plugin\YamlContentProcessBase;
+use Drupal\yaml_content\Plugin\YamlContentProcessInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Plugin for querying and loading a referenced entity.
+ *
+ * @YamlContentProcess(
+ *   id = "reference",
+ *   title = @Translation("Entity Reference Processor"),
+ *   description = @Translation("Attach an entity reference.")
+ * )
+ */
+class Reference extends YamlContentProcessBase implements YamlContentProcessInterface, ContainerFactoryPluginInterface {
+
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * Constructs a new EntityView.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin ID for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_manager
+   *   The entity manager.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+
+    $this->entityTypeManager = $entity_type_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('entity_type.manager')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function process(ProcessingContext $context, array &$field_data) {
+    $entity_type = $this->configuration[0];
+    $filter_params = $this->configuration[1];
+
+    $entity_storage = $this->entityTypeManager->getStorage($entity_type);
+
+    // Use query factory to create a query object for the node of entity_type.
+    $query = $entity_storage->getQuery('AND');
+
+    // Apply filter parameters.
+    foreach ($filter_params as $property => $value) {
+      $query->condition($property, $value);
+    }
+
+    $entity_ids = $query->execute();
+
+    if (empty($entity_ids)) {
+      $entity = $entity_storage->create($filter_params);
+      $entity_ids = [$entity->id()];
+    }
+
+    if (!empty($entity_ids)) {
+      // Use the first match for our value.
+      $field_data['target_id'] = array_shift($entity_ids);
+
+      // Remove process data to avoid issues when setting the value.
+      unset($field_data['#process']);
+
+      return $entity_ids;
+    }
+    $this->throwParamError('Unable to find referenced content', $entity_type, $filter_params);
+  }
+
+}
diff --git a/tests/data_files/test_file.txt b/tests/data_files/test_file.txt
new file mode 100644
index 0000000..16b14f5
--- /dev/null
+++ b/tests/data_files/test_file.txt
@@ -0,0 +1 @@
+test file
diff --git a/tests/src/Functional/Plugin/yaml_content/process/FileTest.php b/tests/src/Functional/Plugin/yaml_content/process/FileTest.php
new file mode 100644
index 0000000..d828f51
--- /dev/null
+++ b/tests/src/Functional/Plugin/yaml_content/process/FileTest.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Drupal\Tests\yaml_content\Kernel\Plugin\yaml_content\process;
+
+use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\Field\FieldItemList;
+use Drupal\Core\TypedData\DataDefinition;
+use Drupal\Tests\BrowserTestBase;
+use Drupal\Tests\yaml_content\Traits\LoadFixturesTrait;
+use Drupal\yaml_content\ContentLoader\ContentLoader;
+use Drupal\yaml_content\Plugin\ProcessingContext;
+use Drupal\yaml_content\Plugin\yaml_content\process\File;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Test file processing.
+ *
+ * Note: This only tests writing a successful file.
+ *
+ * TODO: Test image special case?
+ *
+ * @group yaml_content
+ * @coversDefaultClass \Drupal\yaml_content\Plugin\yaml_content\process\File
+ */
+class FileTest extends BrowserTestBase {
+
+  use LoadFixturesTrait;
+
+  protected static $modules = ['file'];
+
+  /**
+   * Test file processing.
+   *
+   * @covers ::process
+   */
+  public function testProcess() {
+    $args = [
+      'my_entity',
+      ['filename' => 'test_file.txt'],
+    ];
+    $file = new File($args, '', []);
+    $context = new ProcessingContext();
+    $context->setContentLoader(new ContentLoader($this->prophesize(ContainerInterface::class)->reveal()));
+    $context->getContentLoader()->setContentPath(realpath($this->getFixturePath() . '/../../'));
+    $field = new FieldItemList(new BaseFieldDefinition([], new DataDefinition()));
+    $context->setField($field);
+
+    $data = [];
+    $expected_fid = $file->process($context, $data);
+    $this->assertEquals(['target_id' => $expected_fid], $data);
+
+    $file = file_load($expected_fid);
+    $this->assertEquals($args[1]['filename'], $file->getFilename());
+    $this->assertFileExists($file->getFileUri());
+  }
+
+}
diff --git a/tests/src/Unit/ContentLoader/ContentLoaderTest.php b/tests/src/Unit/ContentLoader/ContentLoaderTest.php
index fe71093..bde6f17 100644
--- a/tests/src/Unit/ContentLoader/ContentLoaderTest.php
+++ b/tests/src/Unit/ContentLoader/ContentLoaderTest.php
@@ -3,6 +3,8 @@
 namespace Drupal\Tests\yaml_content\Unit\ContentLoader;
 
 use Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher;
+use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\Field\FieldItemList;
 use Drupal\yaml_content\ContentLoader\ContentLoader;
 
 /**
@@ -90,4 +92,40 @@ public function testLoadContent() {
     $this->markTestIncomplete();
   }
 
+  /**
+   * @covers ::populateField
+   */
+  public function testPopulateFieldCardinalityZero() {
+    $field_definition = new BaseFieldDefinition();
+    $field_definition->setCardinality(0);
+    $field = new FieldItemList($field_definition, 'foobar');
+    $field_data = [];
+    $this->setExpectedException(\InvalidArgumentException::class, "'foobar' cannot hold any values.");
+    $this->contentLoader->populateField($field, $field_data);
+  }
+
+  /**
+   * @covers ::populateField
+   */
+  public function testPopulateFieldCardinalityTooMuchData() {
+    $field_definition = new BaseFieldDefinition();
+    $field_definition->setCardinality(1);
+    $field = new FieldItemList($field_definition, 'foobar');
+    $field_data = [[], [] , []];
+    $this->setExpectedException(\InvalidArgumentException::class, "'foobar' cannot hold more than 1 values. 3 values were parsed from the YAML file.");
+    $this->contentLoader->populateField($field, $field_data);
+  }
+
+  /**
+   * @covers ::populateField
+   */
+  public function testPopulateFieldProcess() {
+    $field_definition = new BaseFieldDefinition();
+    $field_definition->setCardinality(1);
+    $field = new FieldItemList($field_definition, 'foobar');
+    $field_data = [[]];
+    $this->markTestIncomplete('We cannot easily test processing is triggered because we cannot inject a Plugin Manager yet.');
+    $this->contentLoader->populateField($field, $field_data);
+  }
+
 }
diff --git a/tests/src/Unit/Plugin/yaml_content/process/FileTest.php b/tests/src/Unit/Plugin/yaml_content/process/FileTest.php
new file mode 100644
index 0000000..6d448ed
--- /dev/null
+++ b/tests/src/Unit/Plugin/yaml_content/process/FileTest.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace Drupal\Tests\yaml_content\Unit\Plugin\yaml_content\process;
+
+use Drupal\Tests\UnitTestCase;
+use Drupal\Tests\yaml_content\Traits\LoadFixturesTrait;
+use Drupal\yaml_content\ContentLoader\ContentLoader;
+use Drupal\yaml_content\Plugin\ProcessingContext;
+use Drupal\yaml_content\Plugin\yaml_content\process\File;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Test file processing.
+ *
+ * Note: This only tests failures because to pass we have have modules and
+ * write to the filesystem.
+ *
+ * @group yaml_content
+ *
+ * @coversDefaultClass \Drupal\yaml_content\Plugin\yaml_content\process\File
+ */
+class FileTest extends UnitTestCase {
+
+  use LoadFixturesTrait;
+
+  /**
+   * The file process plugin being tested.
+   *
+   * @var \Drupal\yaml_content\Plugin\yaml_content\process\File
+   */
+  protected $filePlugin;
+
+  /**
+   * Setup the file process plugin for all tests.
+   */
+  public function setUp() {
+    parent::setUp();
+    $args = [
+      'my_entity',
+      ['filename' => 'test_file.txt'],
+    ];
+    $this->filePlugin = new File($args, '', []);
+  }
+
+  /**
+   * @covers ::process
+   */
+  public function testProcessMissingLoader() {
+    $context = new ProcessingContext();
+    $data = [];
+    $this->setExpectedException(\Exception::class, 'Missing content loader context.');
+    $this->filePlugin->process($context, $data);
+  }
+
+  /**
+   * @covers ::process
+   */
+  public function testProcessMissingField() {
+    $context = new ProcessingContext();
+    $context->setContentLoader(new ContentLoader($this->prophesize(ContainerInterface::class)->reveal()));
+    $context->getContentLoader()->setContentPath(realpath($this->getFixturePath() . '/../../'));
+    $data = [];
+    $this->setExpectedException(\Exception::class, 'Missing field context.');
+    $this->filePlugin->process($context, $data);
+  }
+
+}
diff --git a/tests/src/Unit/Plugin/yaml_content/process/ReferenceTest.php b/tests/src/Unit/Plugin/yaml_content/process/ReferenceTest.php
new file mode 100644
index 0000000..ae5c993
--- /dev/null
+++ b/tests/src/Unit/Plugin/yaml_content/process/ReferenceTest.php
@@ -0,0 +1,152 @@
+<?php
+
+namespace Drupal\Tests\yaml_content\Unit\Plugin\yaml_content\process;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Entity\Query\QueryInterface;
+use Drupal\Tests\UnitTestCase;
+use Drupal\Tests\yaml_content\Traits\LoadFixturesTrait;
+use Drupal\yaml_content\Plugin\ProcessingContext;
+use Drupal\yaml_content\Plugin\yaml_content\process\Reference;
+use Prophecy\Argument;
+
+/**
+ * Test entity reference processing.
+ *
+ * @group yaml_content
+ *
+ * @coversDefaultClass \Drupal\yaml_content\Plugin\yaml_content\process\File
+ */
+class ReferenceTest extends UnitTestCase {
+
+  /**
+   * The entity type manager service mock.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\Prophecy\Prophecy\ObjectProphecy
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The entity storage handler mock.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface|\Prophecy\Prophecy\ObjectProphecy
+   */
+  protected $entityStorageHandler;
+
+  /**
+   * The reference process plugin being tested.
+   *
+   * @var \Drupal\yaml_content\Plugin\yaml_content\process\Reference
+   */
+  protected $reference;
+
+  /**
+   * Setup mocks and a reference plugin for all tests.
+   */
+  public function setUp() {
+    parent::setUp();
+    $this->entityTypeManager = $this->prophesize(EntityTypeManagerInterface::class);
+    $this->entityStorageHandler = $this->prophesize(EntityStorageInterface::class);
+    $this->entityTypeManager->getStorage(Argument::type('string'))
+      ->willReturn($this->entityStorageHandler->reveal());
+    $this->reference = new Reference([
+      'my_entity',
+      [
+        'title' => 'My First Blog Post',
+      ],
+    ], 'reference', [], $this->entityTypeManager->reveal());
+  }
+
+  /**
+   * Reference processing returns a reference to existing matching entities.
+   *
+   * @covers ::process
+   */
+  public function testProcessExisting() {
+    $data = ['#process' => ['callback' => 'reference']];
+
+    // Mock a query so we can control and assert the mapping of arguments to entity.
+    $query = $this->prophesize(QueryInterface::class);
+    $query->condition('title', 'My First Blog Post')
+      ->shouldBeCalled();
+    $query->execute()
+      ->shouldBeCalled()
+      ->willReturn([1]);
+    $this->entityStorageHandler->getQuery('AND')
+      ->shouldBeCalled()
+      ->willReturn($query->reveal());
+
+    // Create should not be called.
+    $this->entityStorageHandler->create(Argument::any())
+      ->shouldNotBeCalled();
+
+    $this->reference->process(new ProcessingContext(), $data);
+    $this->assertArrayEquals(['target_id' => 1], $data);
+  }
+
+  /**
+   * Reference processing should not create new entity if no matches are found.
+   *
+   * @covers ::process
+   */
+  public function testProcessCreate() {
+    $data = ['#process' => ['callback' => 'reference']];
+
+    // Mock a query so we can control and assert the mapping of arguments to entity.
+    $query = $this->prophesize(QueryInterface::class);
+    $query->condition('title', 'My First Blog Post')
+      ->shouldBeCalled();
+    $query->execute()
+      ->shouldBeCalled()
+      ->willReturn();
+    $this->entityStorageHandler->getQuery('AND')
+      ->shouldBeCalled()
+      ->willReturn($query->reveal());
+
+    // Stub an existing entity since entities are complex and db bound.
+    $entity = $this->prophesize(EntityInterface::class);
+    $entity->id()->willReturn(2);
+
+    // Create should not be called.
+    $this->entityStorageHandler->create(Argument::any())
+      ->shouldBeCalled()
+      ->willReturn($entity);
+
+    $this->reference->process(new ProcessingContext(), $data);
+    $this->assertArrayEquals(['target_id' => 2], $data);
+  }
+
+  /**
+   * Test reference processing failure.
+   */
+  public function testProcessCreateFail() {
+    $data = ['#process' => ['callback' => 'reference']];
+
+    // Mock a query so we can control and assert the mapping of arguments to entity.
+    $query = $this->prophesize(QueryInterface::class);
+    $query->condition('title', 'My First Blog Post')
+      ->shouldBeCalled();
+    $query->execute()
+      ->shouldBeCalled()
+      ->willReturn();
+    $this->entityStorageHandler->getQuery('AND')
+      ->shouldBeCalled()
+      ->willReturn($query->reveal());
+
+    // Stub an existing entity since entities are complex and db bound.
+    $entity = $this->prophesize(EntityInterface::class);
+    $entity->id()->willReturn(NULL);
+    $this->markTestIncomplete('How do we fail? Id will always be called and the array will always have something and not be empty.');
+
+    // Create should not be called.
+    $this->entityStorageHandler->create(Argument::any())
+      ->shouldBeCalled()
+      ->willReturn($entity);
+
+    $this->reference->process(new ProcessingContext(), $data);
+    $this->assertArrayEquals(['target_id' => 2], $data);
+  }
+
+}
diff --git a/yaml_content.services.yml b/yaml_content.services.yml
index 49cffef..2bc1d4c 100644
--- a/yaml_content.services.yml
+++ b/yaml_content.services.yml
@@ -1,4 +1,7 @@
 services:
+  plugin.manager.yaml_content.process:
+    class: Drupal\yaml_content\Plugin\YamlContentProcessManager
+    arguments: ['@container.namespaces', '@cache.discovery', '@module_handler']
   yaml_content.content_loader:
     class: Drupal\yaml_content\ContentLoader\ContentLoader
     arguments:
