diff --git a/core/modules/content_moderation/src/Plugin/WorkflowType/ContentModeration.php b/core/modules/content_moderation/src/Plugin/WorkflowType/ContentModeration.php
index a5d040190e04a43f23636cff7e7c56cd60d6303e..67743d41efc532c7b9fa6dada94d9ab9d961ca82 100644
--- a/core/modules/content_moderation/src/Plugin/WorkflowType/ContentModeration.php
+++ b/core/modules/content_moderation/src/Plugin/WorkflowType/ContentModeration.php
@@ -17,12 +17,30 @@
  * @WorkflowType(
  *   id = "content_moderation",
  *   label = @Translation("Content moderation"),
+ *   required_states = {
+ *     "draft",
+ *     "published",
+ *   },
  * )
  */
 class ContentModeration extends WorkflowTypeBase {
 
   use StringTranslationTrait;
 
+  /**
+   * {@inheritdoc}
+   */
+  public function initializeWorkflow(WorkflowInterface $workflow) {
+    $workflow
+      ->addState('draft', $this->t('Draft'))
+      ->setStateWeight('draft', -5)
+      ->addState('published', $this->t('Published'))
+      ->setStateWeight('published', 0)
+      ->addTransition('create_new_draft', $this->t('Create New Draft'), ['draft', 'published'], 'draft')
+      ->addTransition('publish', $this->t('Publish'), ['draft', 'published'], 'published');
+    return $workflow;
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -51,12 +69,15 @@ public function decorateState(StateInterface $state) {
    */
   public function buildStateConfigurationForm(FormStateInterface $form_state, WorkflowInterface $workflow, StateInterface $state = NULL) {
     /** @var \Drupal\content_moderation\ContentModerationState $state */
+    $is_required_state = isset($state) ? in_array($state->id(), $this->getRequiredStates(), TRUE) : FALSE;
+
     $form = [];
     $form['published'] = [
       '#type' => 'checkbox',
       '#title' => $this->t('Published'),
       '#description' => $this->t('When content reaches this state it should be published.'),
       '#default_value' => isset($state) ? $state->isPublishedState() : FALSE,
+      '#disabled' => $is_required_state,
     ];
 
     $form['default_revision'] = [
@@ -64,6 +85,7 @@ public function buildStateConfigurationForm(FormStateInterface $form_state, Work
       '#title' => $this->t('Default revision'),
       '#description' => $this->t('When content reaches this state it should be made the default revision; this is implied for published states.'),
       '#default_value' => isset($state) ? $state->isDefaultRevisionState() : FALSE,
+      '#disabled' => $is_required_state,
       // @todo Add form #state to force "make default" on when "published" is
       // on for a state.
       // @see https://www.drupal.org/node/2645614
@@ -156,7 +178,16 @@ public function addEntityTypeAndBundle($entity_type_id, $bundle_id) {
   public function defaultConfiguration() {
     // This plugin does not store anything per transition.
     return [
-      'states' => [],
+      'states' => [
+        'draft' => [
+          'published' => FALSE,
+          'default_revision' => FALSE,
+        ],
+        'published' => [
+          'published' => TRUE,
+          'default_revision' => TRUE,
+        ],
+      ],
       'entity_types' => [],
     ];
   }
@@ -169,4 +200,15 @@ public function calculateDependencies() {
     return [];
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfiguration() {
+    $configuration = parent::getConfiguration();
+    // Ensure that states and entity types are ordered consistently.
+    ksort($configuration['states']);
+    ksort($configuration['entity_types']);
+    return $configuration;
+  }
+
 }
diff --git a/core/modules/content_moderation/src/Tests/ModerationStateNodeTest.php b/core/modules/content_moderation/src/Tests/ModerationStateNodeTest.php
index a89ec9fd240a8e96179772d5b570eb3e6d367438..fe36336176f0c1d7f06d0fccbe953445c6800ab4 100644
--- a/core/modules/content_moderation/src/Tests/ModerationStateNodeTest.php
+++ b/core/modules/content_moderation/src/Tests/ModerationStateNodeTest.php
@@ -4,7 +4,6 @@
 
 use Drupal\Core\Url;
 use Drupal\node\Entity\Node;
-use Drupal\workflows\Entity\Workflow;
 
 /**
  * Tests general content moderation workflow for nodes.
@@ -71,21 +70,6 @@ public function testCreatingContent() {
       $this->fail('Non-moderated test node was not saved correctly.');
     }
     $this->assertEqual(NULL, $node->moderation_state->value);
-
-    // \Drupal\content_moderation\Form\BundleModerationConfigurationForm()
-    // should not list workflows with no states.
-    $workflow = Workflow::create(['id' => 'stateless', 'label' => 'Stateless', 'type' => 'content_moderation']);
-    $workflow->save();
-
-    $this->drupalGet('admin/structure/types/manage/moderated_content/moderation');
-    $this->assertNoText('Stateless');
-    $workflow
-      ->addState('draft', 'Draft')
-      ->addState('published', 'Published')
-      ->addTransition('publish', 'Publish', ['draft', 'published'], 'published')
-      ->save();
-    $this->drupalGet('admin/structure/types/manage/moderated_content/moderation');
-    $this->assertText('Stateless');
   }
 
   /**
diff --git a/core/modules/content_moderation/tests/src/Functional/ContentModerationWorkflowTypeTest.php b/core/modules/content_moderation/tests/src/Functional/ContentModerationWorkflowTypeTest.php
index b49439bf6da87a6cd10215e6c84a41a53b453898..ff37bd11a99d69be6d35b54ba49feaefcc915b14 100644
--- a/core/modules/content_moderation/tests/src/Functional/ContentModerationWorkflowTypeTest.php
+++ b/core/modules/content_moderation/tests/src/Functional/ContentModerationWorkflowTypeTest.php
@@ -43,13 +43,19 @@ public function testNewWorkflow() {
       'id' => 'test_workflow',
       'workflow_type' => 'content_moderation',
     ], 'Save');
-    $this->assertSession()->pageTextContains('Created the Test Workflow Workflow. In order for the workflow to be enabled there needs to be at least one state.');
+
+    // Make sure the test workflow includes the default states and transitions.
+    $this->assertSession()->pageTextContains('Draft');
+    $this->assertSession()->pageTextContains('Published');
+    $this->assertSession()->pageTextContains('Create New Draft');
+    $this->assertSession()->pageTextContains('Publish');
 
     // Ensure after a workflow is created, the bundle information can be
     // refreshed.
     $entity_bundle_info->clearCachedBundles();
     $this->assertNotEmpty($entity_bundle_info->getAllBundleInfo());
 
+    $this->clickLink('Add a new state');
     $this->submitForm([
       'label' => 'Test State',
       'id' => 'test_state',
@@ -57,6 +63,16 @@ public function testNewWorkflow() {
       'type_settings[content_moderation][default_revision]' => FALSE,
     ], 'Save');
     $this->assertSession()->pageTextContains('Created Test State state.');
+
+    // Ensure that the published settings cannot be changed.
+    $this->drupalGet('admin/config/workflow/workflows/manage/test_workflow/state/published');
+    $this->assertSession()->fieldDisabled('type_settings[content_moderation][published]');
+    $this->assertSession()->fieldDisabled('type_settings[content_moderation][default_revision]');
+
+    // Ensure that the draft settings cannot be changed.
+    $this->drupalGet('admin/config/workflow/workflows/manage/test_workflow/state/draft');
+    $this->assertSession()->fieldDisabled('type_settings[content_moderation][published]');
+    $this->assertSession()->fieldDisabled('type_settings[content_moderation][default_revision]');
   }
 
 }
diff --git a/core/modules/content_moderation/tests/src/Kernel/ContentModerationPermissionsTest.php b/core/modules/content_moderation/tests/src/Kernel/ContentModerationPermissionsTest.php
index b20da5b47b51e33caa8b3ea9ab4e9830b63f895c..3a19d22d7a519893d8a17706a4148287fde258ef 100644
--- a/core/modules/content_moderation/tests/src/Kernel/ContentModerationPermissionsTest.php
+++ b/core/modules/content_moderation/tests/src/Kernel/ContentModerationPermissionsTest.php
@@ -70,11 +70,13 @@ public function permissionsTestCases() {
             ],
           ],
           'states' => [
-            'published' => [
-              'label' => 'Published',
-            ],
             'draft' => [
               'label' => 'Draft',
+              'weight' => -5,
+            ],
+            'published' => [
+              'label' => 'Published',
+              'weight' => 0,
             ],
           ],
         ],
@@ -101,11 +103,13 @@ public function permissionsTestCases() {
             ],
           ],
           'states' => [
-            'tired' => [
-              'label' => 'Tired',
-            ],
             'awake' => [
               'label' => 'Awake',
+              'weight' => -5,
+            ],
+            'tired' => [
+              'label' => 'Tired',
+              'weight' => -0,
             ],
           ],
         ],
diff --git a/core/modules/workflows/src/Annotation/WorkflowType.php b/core/modules/workflows/src/Annotation/WorkflowType.php
index 2aa3ff9eb2953fcb24ead3bd1e3613588364ff73..6e578ed4b9ed313836557815194be8f6246189b8 100644
--- a/core/modules/workflows/src/Annotation/WorkflowType.php
+++ b/core/modules/workflows/src/Annotation/WorkflowType.php
@@ -41,4 +41,13 @@ class WorkflowType extends Plugin {
    */
   public $label = '';
 
+  /**
+   * States required to exist.
+   *
+   * Normally supplied by WorkflowType::defaultConfiguration().
+   *
+   * @var array
+   */
+  public $required_states = [];
+
 }
diff --git a/core/modules/workflows/src/Entity/Workflow.php b/core/modules/workflows/src/Entity/Workflow.php
index 995ad3116535271d28e67dc7646845efd66c79d9..330c3922e59f26066763ab616eaa43560fb4a208 100644
--- a/core/modules/workflows/src/Entity/Workflow.php
+++ b/core/modules/workflows/src/Entity/Workflow.php
@@ -3,8 +3,10 @@
 namespace Drupal\workflows\Entity;
 
 use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
 use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection;
+use Drupal\workflows\Exception\RequiredStateMissingException;
 use Drupal\workflows\State;
 use Drupal\workflows\Transition;
 use Drupal\workflows\WorkflowInterface;
@@ -112,6 +114,18 @@ class Workflow extends ConfigEntityBase implements WorkflowInterface, EntityWith
    */
   protected $pluginCollection;
 
+  /**
+   * {@inheritdoc}
+   */
+  public function preSave(EntityStorageInterface $storage) {
+    $workflow_type = $this->getTypePlugin();
+    $missing_states = array_diff($workflow_type->getRequiredStates(), array_keys($this->getStates()));
+    if (!empty($missing_states)) {
+      throw new RequiredStateMissingException(sprintf("Workflow type '{$workflow_type->label()}' requires states with the ID '%s' in workflow '{$this->id()}'", implode("', '", $missing_states)));
+    }
+    parent::preSave($storage);
+  }
+
   /**
    * {@inheritdoc}
    */
diff --git a/core/modules/workflows/src/Exception/RequiredStateMissingException.php b/core/modules/workflows/src/Exception/RequiredStateMissingException.php
new file mode 100644
index 0000000000000000000000000000000000000000..7e88bbfe0e046513d98d62054ddc3de8f8bf1adc
--- /dev/null
+++ b/core/modules/workflows/src/Exception/RequiredStateMissingException.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace Drupal\workflows\Exception;
+
+use Drupal\Core\Config\ConfigException;
+
+/**
+ * Indicates that a workflow does not contain a required state.
+ */
+class RequiredStateMissingException extends ConfigException {
+}
diff --git a/core/modules/workflows/src/Form/WorkflowAddForm.php b/core/modules/workflows/src/Form/WorkflowAddForm.php
index c779b8fec1a4a3ef97d346c815db7f1c1a649157..56cbf699617659b53779af7a3038a6538633423a 100644
--- a/core/modules/workflows/src/Form/WorkflowAddForm.php
+++ b/core/modules/workflows/src/Form/WorkflowAddForm.php
@@ -84,11 +84,22 @@ public function form(array $form, FormStateInterface $form_state) {
   public function save(array $form, FormStateInterface $form_state) {
     /* @var \Drupal\workflows\WorkflowInterface $workflow */
     $workflow = $this->entity;
-    $workflow->save();
-    drupal_set_message($this->t('Created the %label Workflow. In order for the workflow to be enabled there needs to be at least one state.', [
-      '%label' => $workflow->label(),
-    ]));
-    $form_state->setRedirectUrl($workflow->toUrl('add-state-form'));
+    // Initialize the workflow using the selected type plugin.
+    $workflow = $workflow->getTypePlugin()->initializeWorkflow($workflow);
+    $return = $workflow->save();
+    if (empty($workflow->getStates())) {
+      drupal_set_message($this->t('Created the %label Workflow. In order for the workflow to be enabled there needs to be at least one state.', [
+        '%label' => $workflow->label(),
+      ]));
+      $form_state->setRedirectUrl($workflow->toUrl('add-state-form'));
+    }
+    else {
+      drupal_set_message($this->t('Created the %label Workflow.', [
+        '%label' => $workflow->label(),
+      ]));
+      $form_state->setRedirectUrl($workflow->toUrl('edit-form'));
+    }
+    return $return;
   }
 
   /**
diff --git a/core/modules/workflows/src/Form/WorkflowEditForm.php b/core/modules/workflows/src/Form/WorkflowEditForm.php
index d8f99f935bf97f9a743b546ab875d48fdb7e0ba0..6b01920ada967828f4d5cb133dc41b8f91a83eeb 100644
--- a/core/modules/workflows/src/Form/WorkflowEditForm.php
+++ b/core/modules/workflows/src/Form/WorkflowEditForm.php
@@ -78,14 +78,15 @@ public function form(array $form, FormStateInterface $form_state) {
       );
     }
 
-    $delete_state_access = $this->entity->access('delete-state');
     foreach ($states as $state) {
-      $links['edit'] = [
-        'title' => $this->t('Edit'),
-        'url' => Url::fromRoute('entity.workflow.edit_state_form', ['workflow' => $workflow->id(), 'workflow_state' => $state->id()]),
-        'attributes' => ['aria-label' => $this->t('Edit @state state', ['@state' => $state->label()])],
+      $links = [
+        'edit' => [
+          'title' => $this->t('Edit'),
+          'url' => Url::fromRoute('entity.workflow.edit_state_form', ['workflow' => $workflow->id(), 'workflow_state' => $state->id()]),
+          'attributes' => ['aria-label' => $this->t('Edit @state state', ['@state' => $state->label()])],
+        ]
       ];
-      if ($delete_state_access) {
+      if ($this->entity->access('delete-state:' . $state->id())) {
         $links['delete'] = [
           'title' => t('Delete'),
           'url' => Url::fromRoute('entity.workflow.delete_state_form', [
diff --git a/core/modules/workflows/src/Form/WorkflowStateEditForm.php b/core/modules/workflows/src/Form/WorkflowStateEditForm.php
index b326404a416b229e491b76193085a18d56a21387..75af1d6b40eb831d5117a10853ebefa0b313539c 100644
--- a/core/modules/workflows/src/Form/WorkflowStateEditForm.php
+++ b/core/modules/workflows/src/Form/WorkflowStateEditForm.php
@@ -151,7 +151,7 @@ protected function actions(array $form, FormStateInterface $form_state) {
     $actions['delete'] = [
       '#type' => 'link',
       '#title' => $this->t('Delete'),
-      '#access' => $this->entity->access('delete-state'),
+      '#access' => $this->entity->access('delete-state:' . $this->stateId),
       '#attributes' => [
         'class' => ['button', 'button--danger'],
       ],
diff --git a/core/modules/workflows/src/Plugin/WorkflowTypeBase.php b/core/modules/workflows/src/Plugin/WorkflowTypeBase.php
index 1ed9fcaf134ab15433d2eb87f99ca21ec87ab220..15ced3e25a67152e1fe1f48c997f2da01a6bb317 100644
--- a/core/modules/workflows/src/Plugin/WorkflowTypeBase.php
+++ b/core/modules/workflows/src/Plugin/WorkflowTypeBase.php
@@ -31,6 +31,13 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
     $this->setConfiguration($configuration);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function initializeWorkflow(WorkflowInterface $workflow) {
+    return $workflow;
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -107,6 +114,13 @@ public function setConfiguration(array $configuration) {
     );
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getRequiredStates() {
+    return $this->getPluginDefinition()['required_states'];
+  }
+
   /**
    * {@inheritDoc}
    */
diff --git a/core/modules/workflows/src/WorkflowAccessControlHandler.php b/core/modules/workflows/src/WorkflowAccessControlHandler.php
index dbeedf521ef7c6f92c9b4b6d944eb6a3855e8f46..156f0091ccf85331c09eba6b8ff8db4339ae271e 100644
--- a/core/modules/workflows/src/WorkflowAccessControlHandler.php
+++ b/core/modules/workflows/src/WorkflowAccessControlHandler.php
@@ -56,17 +56,21 @@ public function __construct(EntityTypeInterface $entity_type, PluginManagerInter
    * {@inheritdoc}
    */
   protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
-    if ($operation === 'delete-state') {
+    /** @var \Drupal\workflows\Entity\Workflow $entity */
+    $workflow_type = $entity->getTypePlugin();
+    if (strpos($operation, 'delete-state') === 0) {
+      list(, $state_id) = explode(':', $operation, 2);
       // Deleting a state is editing a workflow, but also we should forbid
       // access if there is only one state.
-      /** @var \Drupal\workflows\Entity\Workflow $entity */
-      $admin_access = AccessResult::allowedIf(count($entity->getStates()) > 1)->andIf(parent::checkAccess($entity, 'edit', $account))->addCacheableDependency($entity);
+      $admin_access = AccessResult::allowedIf(count($entity->getStates()) > 1)
+        ->andIf(parent::checkAccess($entity, 'edit', $account))
+        ->andIf(AccessResult::allowedIf(!in_array($state_id, $workflow_type->getRequiredStates(), TRUE)))
+        ->addCacheableDependency($entity);
     }
     else {
       $admin_access = parent::checkAccess($entity, $operation, $account);
     }
-    /** @var \Drupal\workflows\WorkflowInterface $entity */
-    return $entity->getTypePlugin()->checkWorkflowAccess($entity, $operation, $account)->orIf($admin_access);
+    return $workflow_type->checkWorkflowAccess($entity, $operation, $account)->orIf($admin_access);
   }
 
   /**
diff --git a/core/modules/workflows/src/WorkflowDeleteAccessCheck.php b/core/modules/workflows/src/WorkflowDeleteAccessCheck.php
new file mode 100644
index 0000000000000000000000000000000000000000..df3b7dd27017c86bc00eaea23eec7487525d68ba
--- /dev/null
+++ b/core/modules/workflows/src/WorkflowDeleteAccessCheck.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Drupal\workflows;
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Routing\Access\AccessInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Session\AccountInterface;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Provides a access checker for deleting a workflow state.
+ */
+class WorkflowDeleteAccessCheck implements AccessInterface {
+
+  /**
+   * Checks access to deleting a workflow state for a particular route.
+   *
+   * The value of '_workflow_state_delete_access' is ignored. The route must
+   * have the parameters 'workflow' and 'workflow_state'. For example:
+   * @code
+   * pattern: '/foo/{workflow}/bar/{workflow_state}/delete'
+   * requirements:
+   *   _workflow_state_delete_access: 'true'
+   * @endcode
+   * @see \Drupal\Core\ParamConverter\EntityConverter
+   *
+   * @param \Symfony\Component\Routing\Route $route
+   *   The route to check against.
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The parametrized route
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The currently logged in account.
+   *
+   * @return \Drupal\Core\Access\AccessResultInterface
+   *   The access result.
+   */
+  public function access(Route $route, RouteMatchInterface $route_match, AccountInterface $account) {
+    // If there is valid entity of the given entity type, check its access.
+    $parameters = $route_match->getParameters();
+    if ($parameters->has('workflow') && $parameters->has('workflow_state')) {
+      $entity = $parameters->get('workflow');
+      if ($entity instanceof EntityInterface) {
+        return $entity->access('delete-state:' . $parameters->get('workflow_state'), $account, TRUE);
+      }
+    }
+    // No opinion, so other access checks should decide if access should be
+    // allowed or not.
+    return AccessResult::neutral();
+  }
+
+}
diff --git a/core/modules/workflows/src/WorkflowTypeInterface.php b/core/modules/workflows/src/WorkflowTypeInterface.php
index 17fddecb98e71b35e488e4a1bc052c8c3e52ac1a..511cb9d211d413ced4e5b8cef237f2934ccfc668 100644
--- a/core/modules/workflows/src/WorkflowTypeInterface.php
+++ b/core/modules/workflows/src/WorkflowTypeInterface.php
@@ -17,6 +17,21 @@
  */
 interface WorkflowTypeInterface extends PluginInspectionInterface, DerivativeInspectionInterface, ConfigurablePluginInterface {
 
+  /**
+   * Initializes a workflow.
+   *
+   * Used to create required states and default transitions.
+   *
+   * @param \Drupal\workflows\WorkflowInterface $workflow
+   *   The workflow to initialize.
+   *
+   * @return \Drupal\workflows\WorkflowInterface $workflow
+   *   The initialized workflow.
+   *
+   * @see \Drupal\workflows\Form\WorkflowAddForm::save()
+   */
+  public function initializeWorkflow(WorkflowInterface $workflow);
+
   /**
    * Gets the label for the workflow type.
    *
@@ -117,4 +132,16 @@ public function buildStateConfigurationForm(FormStateInterface $form_state, Work
    */
   public function buildTransitionConfigurationForm(FormStateInterface $form_state, WorkflowInterface $workflow, TransitionInterface $transition = NULL);
 
+  /**
+   * Gets the required states of workflow type.
+   *
+   * This are usually configured in the workflow type annotation.
+   *
+   * @return array[]
+   *   The required states.
+   *
+   * @see \Drupal\workflows\Annotation\WorkflowType
+   */
+  public function getRequiredStates();
+
 }
diff --git a/core/modules/workflows/tests/modules/workflow_type_test/config/schema/workflow_type_test.schema.yml b/core/modules/workflows/tests/modules/workflow_type_test/config/schema/workflow_type_test.schema.yml
index 4f12fdd1f459a51e2b77cfd4a874939923441cd6..84e7a4169eaeb1c233b6210c8df2fe3b7c22e6f9 100644
--- a/core/modules/workflows/tests/modules/workflow_type_test/config/schema/workflow_type_test.schema.yml
+++ b/core/modules/workflows/tests/modules/workflow_type_test/config/schema/workflow_type_test.schema.yml
@@ -7,6 +7,15 @@ workflow.type_settings.workflow_type_test:
       sequence:
         type: ignore
 
+workflow.type_settings.workflow_type_required_state_test:
+  type: mapping
+  label: 'Workflow test type settings'
+  mapping:
+    states:
+      type: sequence
+      sequence:
+        type: ignore
+
 workflow.type_settings.workflow_type_complex_test:
   type: mapping
   label: 'Workflow complex test type settings'
diff --git a/core/modules/workflows/tests/modules/workflow_type_test/src/Plugin/WorkflowType/RequiredStateTestType.php b/core/modules/workflows/tests/modules/workflow_type_test/src/Plugin/WorkflowType/RequiredStateTestType.php
new file mode 100644
index 0000000000000000000000000000000000000000..493355715a000de1530f72447e1022afff6ab946
--- /dev/null
+++ b/core/modules/workflows/tests/modules/workflow_type_test/src/Plugin/WorkflowType/RequiredStateTestType.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Drupal\workflow_type_test\Plugin\WorkflowType;
+
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\workflows\Plugin\WorkflowTypeBase;
+use Drupal\workflows\WorkflowInterface;
+
+/**
+ * Test workflow type.
+ *
+ * @WorkflowType(
+ *   id = "workflow_type_required_state_test",
+ *   label = @Translation("Required State Type Test"),
+ *   required_states = {
+ *     "fresh",
+ *     "rotten",
+ *   }
+ * )
+ */
+class RequiredStateTestType extends WorkflowTypeBase {
+
+  use StringTranslationTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function initializeWorkflow(WorkflowInterface $workflow) {
+    $workflow
+      ->addState('fresh', $this->t('Fresh'))
+      ->setStateWeight('fresh', -5)
+      ->addState('rotten', $this->t('Rotten'))
+      ->addTransition('rot', $this->t('Rot'), ['fresh'], 'rotten');
+    return $workflow;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    // No configuration is stored for the test type.
+    return [];
+  }
+
+}
diff --git a/core/modules/workflows/tests/modules/workflow_type_test/src/Plugin/WorkflowType/TestType.php b/core/modules/workflows/tests/modules/workflow_type_test/src/Plugin/WorkflowType/TestType.php
index d328d63d9996d2ae21f8872cb378a5ba2e8fb2a4..2ff68a84adca3be50e926883b39eee79343f915d 100644
--- a/core/modules/workflows/tests/modules/workflow_type_test/src/Plugin/WorkflowType/TestType.php
+++ b/core/modules/workflows/tests/modules/workflow_type_test/src/Plugin/WorkflowType/TestType.php
@@ -22,4 +22,13 @@ public function defaultConfiguration() {
     return [];
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getRequiredStates() {
+    // Normally this is obtained from the annotation but we get from state to
+    // allow dynamic testing.
+    return \Drupal::state()->get('workflow_type_test.required_states', []);
+  }
+
 }
diff --git a/core/modules/workflows/tests/src/Functional/WorkflowUiTest.php b/core/modules/workflows/tests/src/Functional/WorkflowUiTest.php
index 37de109de8ba3245981f305d56283cc003012f26..91c69b73f7fe0b502a2a4d92b225eae2c7343ad9 100644
--- a/core/modules/workflows/tests/src/Functional/WorkflowUiTest.php
+++ b/core/modules/workflows/tests/src/Functional/WorkflowUiTest.php
@@ -66,10 +66,16 @@ public function testAccess() {
       $this->assertSession()->statusCodeEquals(200);
     }
 
+    // Ensure that default states can not be deleted.
+    \Drupal::state()->set('workflow_type_test.required_states', ['published']);
+    $this->drupalGet('admin/config/workflow/workflows/manage/test/state/published/delete');
+    $this->assertSession()->statusCodeEquals(403);
+    \Drupal::state()->set('workflow_type_test.required_states', []);
+
     // Delete one of the states and ensure the other test cannot be deleted.
     $this->drupalGet('admin/config/workflow/workflows/manage/test/state/published/delete');
     $this->submitForm([], 'Delete');
-    $this->drupalGet('admin/config/workflow/workflows/manage/test/state/published/delete');
+    $this->drupalGet('admin/config/workflow/workflows/manage/test/state/draft/delete');
     $this->assertSession()->statusCodeEquals(403);
   }
 
@@ -189,9 +195,28 @@ public function testWorkflowCreation() {
     // the draft state.
     $published_delete_link = Url::fromRoute('entity.workflow.delete_state_form', [
       'workflow' => $workflow->id(),
-      'workflow_state' => 'published'
+      'workflow_state' => 'published',
+    ])->toString();
+    $draft_delete_link = Url::fromRoute('entity.workflow.delete_state_form', [
+      'workflow' => $workflow->id(),
+      'workflow_state' => 'draft',
     ])->toString();
+    $this->assertSession()->elementContains('css', 'tr[data-drupal-selector="edit-states-published"]', 'Delete');
     $this->assertSession()->linkByHrefExists($published_delete_link);
+    $this->assertSession()->linkByHrefExists($draft_delete_link);
+
+    // Make the published state a default state and ensure it is no longer
+    // linked.
+    \Drupal::state()->set('workflow_type_test.required_states', ['published']);
+    $this->getSession()->reload();
+    $this->assertSession()->linkByHrefNotExists($published_delete_link);
+    $this->assertSession()->linkByHrefExists($draft_delete_link);
+    $this->assertSession()->elementNotContains('css', 'tr[data-drupal-selector="edit-states-published"]', 'Delete');
+    \Drupal::state()->set('workflow_type_test.required_states', []);
+    $this->getSession()->reload();
+    $this->assertSession()->elementContains('css', 'tr[data-drupal-selector="edit-states-published"]', 'Delete');
+    $this->assertSession()->linkByHrefExists($published_delete_link);
+    $this->assertSession()->linkByHrefExists($draft_delete_link);
 
     // Delete the Draft state.
     $this->clickLink('Delete');
@@ -211,6 +236,20 @@ public function testWorkflowCreation() {
     $this->assertSession()->pageTextContains('Workflow Test deleted.');
     $this->assertSession()->pageTextContains('There is no Workflow yet.');
     $this->assertNull($workflow_storage->loadUnchanged('test'), 'The test workflow has been deleted');
+
+    // Ensure that workflow types that implement
+    // \Drupal\workflows\WorkflowTypeInterface::initializeWorkflow() are
+    // initialized correctly.
+    $this->drupalGet('admin/config/workflow/workflows');
+    $this->clickLink('Add workflow');
+    $this->submitForm(['label' => 'Test 2', 'id' => 'test2', 'workflow_type' => 'workflow_type_required_state_test'], 'Save');
+    $this->assertSession()->addressEquals('admin/config/workflow/workflows/manage/test2');
+    $workflow = $workflow_storage->loadUnchanged('test2');
+    $this->assertTrue($workflow->hasState('fresh'), 'The workflow has the "fresh" state');
+    $this->assertTrue($workflow->hasState('rotten'), 'The workflow has the "rotten" state');
+    $this->assertTrue($workflow->hasTransition('rot'), 'The workflow has the "rot" transition');
+    $this->assertSession()->pageTextContains('Fresh');
+    $this->assertSession()->pageTextContains('Rotten');
   }
 
   /**
diff --git a/core/modules/workflows/tests/src/Kernel/RequiredStatesTest.php b/core/modules/workflows/tests/src/Kernel/RequiredStatesTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..18a2c488265ead9089889ea24222f031d0bbf6d5
--- /dev/null
+++ b/core/modules/workflows/tests/src/Kernel/RequiredStatesTest.php
@@ -0,0 +1,121 @@
+<?php
+
+namespace Drupal\Tests\workflows\Kernel;
+
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\workflows\Entity\Workflow;
+
+/**
+ * Tests Workflow type's required states and configuration initialization.
+ *
+ * @coversDefaultClass \Drupal\workflows\Plugin\WorkflowTypeBase
+ *
+ * @group workflows
+ */
+class RequiredStatesTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['workflows', 'workflow_type_test'];
+
+  /**
+   * @covers ::getRequiredStates
+   * @covers ::initializeWorkflow
+   * @covers ::__construct
+   */
+  public function testGetRequiredStates() {
+    $workflow = new Workflow([
+      'id' => 'test',
+      'type' => 'workflow_type_required_state_test',
+    ], 'workflow');
+    $workflow = $workflow->getTypePlugin()->initializeWorkflow($workflow);
+    $workflow->save();
+    $this->assertEquals(['fresh', 'rotten'], $workflow->getTypePlugin()
+      ->getRequiredStates());
+
+    // Ensure that the workflow has the default configuration.
+    $this->assertTrue($workflow->hasState('rotten'));
+    $this->assertTrue($workflow->hasState('fresh'));
+    $this->assertTrue($workflow->hasTransitionFromStateToState('fresh', 'rotten'));
+  }
+
+  /**
+   * @covers \Drupal\workflows\Entity\Workflow::preSave
+   * @expectedException \Drupal\workflows\Exception\RequiredStateMissingException
+   * @expectedExceptionMessage Required State Type Test' requires states with the ID 'fresh' in workflow 'test'
+   */
+  public function testDeleteRequiredStateAPI() {
+    $workflow = new Workflow([
+      'id' => 'test',
+      'type' => 'workflow_type_required_state_test',
+    ], 'workflow');
+    $workflow = $workflow->getTypePlugin()->initializeWorkflow($workflow);
+    $workflow->save();
+    // Ensure that required states can't be deleted.
+    $workflow->deleteState('fresh')->save();
+  }
+
+  /**
+   * @covers \Drupal\workflows\Entity\Workflow::preSave
+   * @expectedException \Drupal\workflows\Exception\RequiredStateMissingException
+   * @expectedExceptionMessage Required State Type Test' requires states with the ID 'fresh', 'rotten' in workflow 'test'
+   */
+  public function testNoStatesRequiredStateAPI() {
+    $workflow = new Workflow([
+      'id' => 'test',
+      'type' => 'workflow_type_required_state_test',
+    ], 'workflow');
+    $workflow->save();
+  }
+
+  /**
+   * Ensures that initialized configuration can be changed.
+   */
+  public function testChangeRequiredStateAPI() {
+    $workflow = new Workflow([
+      'id' => 'test',
+      'type' => 'workflow_type_required_state_test',
+    ], 'workflow');
+    $workflow = $workflow->getTypePlugin()->initializeWorkflow($workflow);
+    $workflow->save();
+
+    // Ensure states added by default configuration can be changed.
+    $this->assertEquals('Fresh', $workflow->getState('fresh')->label());
+    $workflow
+      ->setStateLabel('fresh', 'Fresher')
+      ->save();
+    $this->assertEquals('Fresher', $workflow->getState('fresh')->label());
+
+    // Ensure transitions can be altered.
+    $workflow
+      ->addState('cooked', 'Cooked')
+      ->setTransitionFromStates('rot', ['fresh', 'cooked'])
+      ->save();
+    $this->assertTrue($workflow->hasTransitionFromStateToState('fresh', 'rotten'));
+    $this->assertTrue($workflow->hasTransitionFromStateToState('cooked', 'rotten'));
+
+    $workflow
+      ->setTransitionFromStates('rot', ['cooked'])
+      ->save();
+    $this->assertFalse($workflow->hasTransitionFromStateToState('fresh', 'rotten'));
+    $this->assertTrue($workflow->hasTransitionFromStateToState('cooked', 'rotten'));
+
+    // Ensure the default configuration does not cause ordering issues.
+    $workflow->addTransition('cook', 'Cook', ['fresh'], 'cooked')->save();
+    $this->assertSame([
+      'cooked',
+      'fresh',
+      'rotten',
+    ], array_keys($workflow->get('states')));
+    $this->assertSame([
+      'cook',
+      'rot',
+    ], array_keys($workflow->get('transitions')));
+
+    // Ensure that transitions can be deleted.
+    $workflow->deleteTransition('rot')->save();
+    $this->assertFalse($workflow->hasTransition('rot'));
+  }
+
+}
diff --git a/core/modules/workflows/workflows.routing.yml b/core/modules/workflows/workflows.routing.yml
index 377e572202d9684520b1bb3b830558f572922ce3..329ed10c484718c8c2a1d918fba2d9ebc47ad670 100644
--- a/core/modules/workflows/workflows.routing.yml
+++ b/core/modules/workflows/workflows.routing.yml
@@ -20,7 +20,7 @@ entity.workflow.delete_state_form:
     _form: '\Drupal\workflows\Form\WorkflowStateDeleteForm'
     _title: 'Delete state'
   requirements:
-    _entity_access: 'workflow.delete-state'
+    _workflow_state_delete_access: 'true'
 
 entity.workflow.add_transition_form:
   path: '/admin/config/workflow/workflows/manage/{workflow}/add_transition'
diff --git a/core/modules/workflows/workflows.services.yml b/core/modules/workflows/workflows.services.yml
index 772bab71d2a5adb78fd3f361e11e39637c430b07..7d32420242d57d1b88d554e7f3229a02eeb93321 100644
--- a/core/modules/workflows/workflows.services.yml
+++ b/core/modules/workflows/workflows.services.yml
@@ -3,4 +3,8 @@ services:
     class: Drupal\workflows\WorkflowTypeManager
     parent: default_plugin_manager
     tags:
-      - { name: plugin_manager_cache_clear }
\ No newline at end of file
+      - { name: plugin_manager_cache_clear }
+  workflows.access_check.delete_state:
+    class: \Drupal\workflows\WorkflowDeleteAccessCheck
+    tags:
+      - { name: access_check, applies_to: _workflow_state_delete_access }