diff --git a/migration_templates/d6_sms_number.yml b/migration_templates/d6_sms_number.yml
new file mode 100644
index 0000000..9ad8211
--- /dev/null
+++ b/migration_templates/d6_sms_number.yml
@@ -0,0 +1,27 @@
+id: d6_sms_number
+label: SMS User Number
+migration_tags:
+  - Drupal 6
+  - SMS Framework
+source:
+  plugin: d6_sms_number
+process:
+  'entity/target_type':
+    plugin: default_value
+    default_value: user
+  'entity/target_id':
+    plugin: migration
+    migration: d6_user
+    source: uid
+  phone: number
+  status:
+    plugin: sms_verification_status
+    source: status
+  code: code
+destination:
+  plugin: entity:sms_phone_number_verification
+migration_dependencies:
+  required:
+    - phone_number_settings
+    - d6_user
+  optional: { }
diff --git a/migration_templates/d7_sms_number.yml b/migration_templates/d7_sms_number.yml
new file mode 100644
index 0000000..ce7a374
--- /dev/null
+++ b/migration_templates/d7_sms_number.yml
@@ -0,0 +1,27 @@
+id: d7_sms_number
+label: SMS User Number
+migration_tags:
+  - Drupal 7
+  - SMS Framework
+source:
+  plugin: d7_sms_number
+process:
+  'entity/target_type':
+    plugin: default_value
+    default_value: user
+  'entity/target_id':
+    plugin: migration
+    migration: d7_user
+    source: uid
+  phone: number
+  status:
+    plugin: sms_verification_status
+    source: status
+  code: code
+destination:
+  plugin: entity:sms_phone_number_verification
+migration_dependencies:
+  required:
+    - d7_user
+    - phone_number_settings
+  optional: { }
diff --git a/migration_templates/phone_number_settings.yml b/migration_templates/phone_number_settings.yml
new file mode 100644
index 0000000..c6baf33
--- /dev/null
+++ b/migration_templates/phone_number_settings.yml
@@ -0,0 +1,37 @@
+id: phone_number_settings
+label: 'Phone number verification settings from D6 / D7 sms_user settings.'
+migration_tags:
+  - Drupal 6
+  - Drupal 7
+  - SMS Framework
+source:
+  plugin: variable
+  variables:
+    - sms_user_confirmation_message
+    - sms_user_registration_form
+    - sms_user_sleep
+  constants:
+    config_id: user.user
+    entity_type: user
+    bundle: user
+    field_names:
+      phone_number: phone_number
+process:
+  id: 'constants/config_id'
+  entity_type: 'constants/entity_type'
+  bundle: 'constants/bundle'
+  verification_message:
+    plugin: phone_number_settings
+    source: sms_user_confirmation_message
+  verification_code_lifetime:
+    plugin: default_value
+    default_value: 600
+  purge_verification_phone_number:
+    plugin: default_value
+    default_value: true
+  fields: 'constants/field_names'
+destination:
+  plugin: entity:phone_number_settings
+migration_dependencies:
+  required: { }
+  optional: { }
diff --git a/src/Plugin/migrate/destination/PhoneNumberSettings.php b/src/Plugin/migrate/destination/PhoneNumberSettings.php
new file mode 100644
index 0000000..4905d1d
--- /dev/null
+++ b/src/Plugin/migrate/destination/PhoneNumberSettings.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace Drupal\sms\Plugin\migrate\destination;
+
+use Drupal\Core\Entity\Entity\EntityFormDisplay;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\migrate\Plugin\migrate\destination\EntityConfigBase;
+use Drupal\migrate\Row;
+use Drupal\sms\Entity\PhoneNumberSettingsInterface;
+use Drupal\sms\Form\PhoneNumberSettingsForm;
+
+/**
+ * Destination plugin for SMS phone number verifications.
+ *
+ * @MigrateDestination(
+ *   id = "entity:phone_number_settings"
+ * )
+ */
+class PhoneNumberSettings extends EntityConfigBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function import(Row $row, array $old_destination_id_values = []) {
+    $return = parent::import($row, $old_destination_id_values);
+    if ($return) {
+      // After successful import of the phone_number_setting, the phone number
+      // field should be created and attached to the user entity type.
+      /** @var \Drupal\sms\Entity\PhoneNumberVerification $verification */
+      $phone_number_setting = $this->storage->load(reset($return));
+      $this->createPhoneNumberField($phone_number_setting);
+    }
+    return $return;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function rollback(array $destination_identifier) {
+    /** @var \Drupal\sms\Entity\PhoneNumberSettingsInterface $phone_number_settings */
+    $phone_number_settings = $this->storage->load(reset($destination_identifier));
+
+    $entity_type_id = $phone_number_settings->getPhoneNumberEntityTypeId();
+    $bundle = $phone_number_settings->getPhoneNumberBundle();
+    $field_name = $phone_number_settings->getFieldName('phone_number');
+
+    // Delete entity form display component.
+    $entity_form_display = EntityFormDisplay::load($entity_type_id . '.' . $bundle . '.default');
+    if ($entity_form_display) {
+      $entity_form_display->removeComponent($field_name);
+    }
+
+    // Delete the field storage and field instance.
+    FieldStorageConfig::loadByName($entity_type_id, $field_name)->delete();
+//    FieldConfig::loadByName($entity_type_id, $bundle, $field_name)->delete();
+
+    // Remove the phone number settings.
+    parent::rollback($destination_identifier);
+  }
+
+  /**
+   * @param \Drupal\sms\Entity\PhoneNumberSettingsInterface $phone_number_settings
+   *   The phone number settings for a given entity type.
+   */
+  protected function createPhoneNumberField(PhoneNumberSettingsInterface $phone_number_settings) {
+    PhoneNumberSettingsForm::createNewField(
+      $phone_number_settings->getPhoneNumberEntityTypeId(),
+      $phone_number_settings->getPhoneNumberBundle(),
+      $phone_number_settings->getFieldName('phone_number')
+    );
+  }
+
+}
diff --git a/src/Plugin/migrate/destination/SmsVerification.php b/src/Plugin/migrate/destination/SmsVerification.php
new file mode 100644
index 0000000..1954011
--- /dev/null
+++ b/src/Plugin/migrate/destination/SmsVerification.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace Drupal\sms\Plugin\migrate\destination;
+
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Field\FieldTypePluginManagerInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\migrate\Plugin\migrate\destination\EntityContentBase;
+use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\migrate\Row;
+use Drupal\sms\Entity\PhoneNumberVerificationInterface as EntityPhoneNumberVerificationInterface;
+use Drupal\sms\Provider\PhoneNumberVerificationInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Destination plugin for SMS phone number verifications.
+ *
+ * @MigrateDestination(
+ *   id = "entity:sms_phone_number_verification"
+ * )
+ */
+class SmsVerification extends EntityContentBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * The phone number verification service.
+   *
+   * @var \Drupal\sms\Provider\PhoneNumberVerificationInterface
+   */
+  protected $phoneNumberVerificationService;
+
+  /**
+   * Builds a phone number verification entity destination.
+   *
+   * @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\migrate\Plugin\MigrationInterface $migration
+   *   The migration.
+   * @param \Drupal\Core\Entity\EntityStorageInterface $storage
+   *   The storage for this entity type.
+   * @param array $bundles
+   *   The list of bundles this entity type has.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager service.
+   * @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
+   *   The field type plugin manager service.
+   * @param \Drupal\sms\Provider\PhoneNumberVerificationInterface $verification
+   *   The phone number verification service.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, FieldTypePluginManagerInterface $field_type_manager, PhoneNumberVerificationInterface $verification) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_manager, $field_type_manager);
+    $this->phoneNumberVerificationService = $verification;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
+    $entity_type = static::getEntityTypeId($plugin_id);
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $migration,
+      $container->get('entity_type.manager')->getStorage($entity_type),
+      array_keys($container->get('entity_type.bundle.info')->getBundleInfo($entity_type)),
+      $container->get('entity.manager'),
+      $container->get('plugin.manager.field.field_type'),
+      $container->get('sms.phone_number.verification')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function import(Row $row, array $old_destination_id_values = []) {
+    $return = parent::import($row, $old_destination_id_values);
+    if ($return) {
+      // After successful import of the verification data, the phone number
+      // should be updated on the corresponding user entity.
+      /** @var \Drupal\sms\Entity\PhoneNumberVerification $verification */
+      $verification = $this->storage->load(reset($return));
+      $this->setVerifiedValue($verification, $row->getSourceProperty('delta'));
+    }
+    return $return;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function rollback(array $destination_identifier) {
+    /** @var \Drupal\sms\Entity\PhoneNumberVerification $verification */
+    $verification = $this->storage->load(reset($destination_identifier));
+    $this->unsetVerifiedValue($verification);
+    // Remove the verified user phone number.
+    parent::rollback($destination_identifier);
+  }
+
+  /**
+   * Sets the verified value for the user entity.
+   *
+   * @param \Drupal\sms\Entity\PhoneNumberVerificationInterface $verification
+   *   The phone number verification for a given user entity.
+   * @param int $delta
+   *   The specific item of the phone number field to set.
+   */
+  protected function setVerifiedValue(EntityPhoneNumberVerificationInterface $verification, $delta) {
+    if (!isset($delta)) {
+      $delta = 0;
+    }
+    $user_entity = $verification->getEntity();
+    $phone_number_settings = $this->phoneNumberVerificationService
+      ->getPhoneNumberSettingsForEntity($user_entity);
+    if ($user_entity && $phone_number_settings) {
+      $phone_field_name = $phone_number_settings->getFieldName('phone_number');
+      $user_entity->{$phone_field_name}[$delta] = $verification->getPhoneNumber();
+      $user_entity->save();
+    }
+  }
+
+  /**
+   * Unsets the verified value for the user entity.
+   *
+   * @param \Drupal\sms\Entity\PhoneNumberVerificationInterface $verification
+   *   The phone number verification for a given user entity.
+   */
+  protected function unsetVerifiedValue(EntityPhoneNumberVerificationInterface $verification) {
+    $user_entity = $verification->getEntity();
+    $phone_number_settings = $this->phoneNumberVerificationService
+      ->getPhoneNumberSettingsForEntity($user_entity);
+    if ($user_entity && $phone_number_settings) {
+      $phone_field_name = $phone_number_settings->getFieldName('phone_number');
+      $user_entity->{$phone_field_name} = '';
+      $user_entity->save();
+    }
+  }
+
+}
diff --git a/src/Plugin/migrate/process/PhoneNumberSettings.php b/src/Plugin/migrate/process/PhoneNumberSettings.php
new file mode 100644
index 0000000..f1b4886
--- /dev/null
+++ b/src/Plugin/migrate/process/PhoneNumberSettings.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Drupal\sms\Plugin\migrate\process;
+
+use Drupal\migrate\MigrateExecutableInterface;
+use Drupal\migrate\ProcessPluginBase;
+use Drupal\migrate\Row;
+
+/**
+ * Updates SMS verification code message to D8 format.
+ *
+ * @MigrateProcessPlugin(
+ *   id = "phone_number_settings"
+ * )
+ */
+class PhoneNumberSettings extends ProcessPluginBase {
+
+  const DEFAULT_LEGACY_VERIFICATION_MESSAGE = '[site:name] confirmation code: ';
+//  const DEFAULT_LEGACY_VERIFICATION_MESSAGE = '[site-name] confirmation code: [confirm-code]';
+  const DEFAULT_VERIFICATION_MESSAGE = "Your verification code is '[sms-message:verification-code]'.\nGo to [sms:verification-url] to verify your phone number.\n- [site:name]";
+
+  /**
+   * {@inheritdoc}
+   */
+  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
+    if ($row->getSourceProperty('id') === 'sms_user_confirmation_message') {
+      // Convert D6 message tokens to D7 token format.
+      $value = str_replace('confirm-code', 'confirm:code', str_replace('site-name', 'site:name', $value));
+
+      // If still using the D6/D7 default message, swap for the new D8 default.
+      if (empty($value) || $value == static::DEFAULT_LEGACY_VERIFICATION_MESSAGE) {
+        $value = static::DEFAULT_VERIFICATION_MESSAGE;
+      }
+      else {
+        $value = str_replace('[confirm:code]', '[sms-message:verification-code]', $value);
+      }
+    }
+    return $value;
+  }
+
+}
diff --git a/src/Plugin/migrate/process/VerificationStatus.php b/src/Plugin/migrate/process/VerificationStatus.php
new file mode 100644
index 0000000..7d0e290
--- /dev/null
+++ b/src/Plugin/migrate/process/VerificationStatus.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Drupal\sms\Plugin\migrate\process;
+
+use Drupal\migrate\MigrateExecutableInterface;
+use Drupal\migrate\ProcessPluginBase;
+use Drupal\migrate\Row;
+
+/**
+ * Changes verification status from D6/D7 format [0, 1, 2] to D8 [true, false].
+ *
+ * @MigrateProcessPlugin(
+ *   id = "sms_verification_status"
+ * )
+ */
+class VerificationStatus extends ProcessPluginBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
+    return $value == 2;
+  }
+
+}
diff --git a/src/Plugin/migrate/source/D6SmsNumber.php b/src/Plugin/migrate/source/D6SmsNumber.php
new file mode 100644
index 0000000..5ba1752
--- /dev/null
+++ b/src/Plugin/migrate/source/D6SmsNumber.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Drupal\sms\Plugin\migrate\source;
+
+use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
+
+/**
+ * Source plugin for D6 sms_user phone number.
+ *
+ * @MigrateSource(
+ *   id = "d6_sms_number"
+ * )
+ */
+class D6SmsNumber extends DrupalSqlBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fields() {
+    return [
+      'uid' => $this->t('User ID'),
+      'delta' => $this->t('Delta'),
+      'number' => $this->t('Phone number'),
+      'status' => $this->t('Verification Status'),
+      'code' => $this->t('Verification code'),
+      'gateway' => $this->t('Verification gateway'),
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getIds() {
+    return [
+      'uid' => [
+        'type' => 'integer',
+         'alias' => 'su',
+      ],
+      'delta' => [
+        'type' => 'integer',
+         'alias' => 'su',
+      ],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function query() {
+    return $this->select('sms_user', 'su')->fields('su', array_keys($this->fields()));
+  }
+
+}
diff --git a/src/Plugin/migrate/source/D7SmsNumber.php b/src/Plugin/migrate/source/D7SmsNumber.php
new file mode 100644
index 0000000..ca83672
--- /dev/null
+++ b/src/Plugin/migrate/source/D7SmsNumber.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace Drupal\sms\Plugin\migrate\source;
+
+use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
+
+/**
+ * Source plugin for D7 sms_user phone number.
+ *
+ * @MigrateSource(
+ *   id = "d7_sms_number"
+ * )
+ */
+class D7SmsNumber extends DrupalSqlBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fields() {
+    return [
+      'uid' => $this->t('User ID'),
+      'number' => $this->t('Phone number'),
+      'status' => $this->t('Verification Status'),
+      'code' => $this->t('Verification code'),
+      'gateway' => $this->t('Verification gateway'),
+      'sleep_enabled' => $this->t('Sleep enabled'),
+      'sleep_start_time' => $this->t('Sleep start time'),
+      'sleep_end_time' => $this->t('Sleep end time'),
+      'sms_user_opt_out' => $this->t('Opt out of SMS'),
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getIds() {
+    return [
+      'uid' => [
+        'type' => 'integer',
+         'alias' => 'su',
+      ],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function query() {
+    return $this->select('sms_user', 'su')->fields('su', array_keys($this->fields()));
+  }
+
+}
diff --git a/tests/fixtures/migrate/sms_confirmation_message_d6.php b/tests/fixtures/migrate/sms_confirmation_message_d6.php
new file mode 100644
index 0000000..c14a074
--- /dev/null
+++ b/tests/fixtures/migrate/sms_confirmation_message_d6.php
@@ -0,0 +1,18 @@
+<?php
+// @codingStandardsIgnoreFile
+/**
+ * @file
+ * A database agnostic dump for testing purposes.
+ *
+ * This file was generated by the Drupal 8.0 db-tools.php script.
+ */
+
+use Drupal\Core\Database\Database;
+
+$connection = Database::getConnection();
+$custom_message = 'This is a custom confirmation message from [site-name]. Confirmation code: [confirm-code]';
+
+$connection->insert('variable')
+  ->fields(['name', 'value'])
+  ->values(['sms_user_confirmation_message', serialize($custom_message)])
+  ->execute();
diff --git a/tests/fixtures/migrate/sms_confirmation_message_d7.php b/tests/fixtures/migrate/sms_confirmation_message_d7.php
new file mode 100644
index 0000000..cc5b842
--- /dev/null
+++ b/tests/fixtures/migrate/sms_confirmation_message_d7.php
@@ -0,0 +1,18 @@
+<?php
+// @codingStandardsIgnoreFile
+/**
+ * @file
+ * A database agnostic dump for testing purposes.
+ *
+ * This file was generated by the Drupal 8.0 db-tools.php script.
+ */
+
+use Drupal\Core\Database\Database;
+
+$connection = Database::getConnection();
+$custom_message = 'This is a custom confirmation message from [site:name]. Confirmation code: [confirm:code]';
+
+$connection->insert('variable')
+  ->fields(['name', 'value'])
+  ->values(['sms_user_confirmation_message', serialize($custom_message)])
+  ->execute();
diff --git a/tests/fixtures/migrate/sms_user_drupal6.php b/tests/fixtures/migrate/sms_user_drupal6.php
new file mode 100644
index 0000000..9a814b3
--- /dev/null
+++ b/tests/fixtures/migrate/sms_user_drupal6.php
@@ -0,0 +1,129 @@
+<?php
+// @codingStandardsIgnoreFile
+/**
+ * @file
+ * A database agnostic dump for testing purposes.
+ *
+ * This file was generated by the Drupal 8.0 db-tools.php script.
+ */
+
+use Drupal\Core\Database\Database;
+
+$connection = Database::getConnection();
+
+$connection->schema()->createTable('sms_user', array(
+  'fields' => array(
+    'uid' => array(
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'normal',
+      'unsigned' => TRUE,
+    ),
+    'delta' => array(
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'normal',
+      'unsigned' => TRUE,
+    ),
+    'number' => array(
+      'type' => 'varchar',
+      'not null' => TRUE,
+      'length' => '32',
+    ),
+    'status' => array(
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'normal',
+      'unsigned' => TRUE,
+    ),
+    'code' => array(
+      'type' => 'varchar',
+      'not null' => TRUE,
+      'length' => '16',
+    ),
+    'gateway' => array(
+      'type' => 'text',
+      'not null' => FALSE,
+      'size' => 'normal',
+    ),
+  ),
+  'primary key' => array(
+    'uid',
+    'delta',
+  ),
+  'mysql_character_set' => 'utf8',
+));
+
+$connection->insert('sms_user')
+->fields(array(
+  'uid',
+  'delta',
+  'number',
+  'status',
+  'code',
+  'gateway',
+))
+->values(array(
+  'uid' => '40',
+  'delta' => '0',
+  'number' => '1234567890',
+  'status' => '2',
+  'code' => '',
+  'gateway' => 'N;',
+))
+->values(array(
+  'uid' => '41',
+  'delta' => '0',
+  'number' => '87654321190',
+  'status' => '1',
+  'code' => '8002',
+  'gateway' => 'N;',
+))
+->execute();
+
+$connection->insert('users')
+->fields(array(
+  'uid',
+  'name',
+  'pass',
+  'mail',
+  'created',
+  'access',
+  'login',
+  'status',
+  'init',
+))
+->values(array(
+  'uid' => '40',
+  'name' => 'joe',
+  'pass' => '63a9f0ea7bb98050796b649e85481845',
+  'mail' => 'joe@localhost',
+  'created' => '1505771205',
+  'access' => '1505772205',
+  'login' => '1505771593',
+  'status' => '1',
+  'init' => 'joe@localhost',
+))
+->values(array(
+  'uid' => '41',
+  'name' => 'jill',
+  'pass' => '671cc45b3e2c6eb751d6a554dc5a5fe7',
+  'mail' => 'jill@example.com',
+  'created' => '1391150052',
+  'access' => '1391259672',
+  'login' => '1391152253',
+  'status' => '1',
+  'init' => 'jill@example.com',
+))
+->values(array(
+  'uid' => '42',
+  'name' => 'jack',
+  'pass' => '93a70546e6c032c135499fed70cfe438',
+  'mail' => 'jack@example.com',
+  'created' => '1391150053',
+  'access' => '1391259673',
+  'login' => '1391152254',
+  'status' => '1',
+  'init' => 'jack@example.com',
+))
+->execute();
diff --git a/tests/fixtures/migrate/sms_user_drupal7.php b/tests/fixtures/migrate/sms_user_drupal7.php
new file mode 100644
index 0000000..b4bd3d5
--- /dev/null
+++ b/tests/fixtures/migrate/sms_user_drupal7.php
@@ -0,0 +1,165 @@
+<?php
+// @codingStandardsIgnoreFile
+/**
+ * @file
+ * A database agnostic dump for testing purposes.
+ *
+ * This file was generated by the Drupal 8.0 db-tools.php script.
+ */
+
+use Drupal\Core\Database\Database;
+
+$connection = Database::getConnection();
+
+$connection->schema()->createTable('sms_user', array(
+  'fields' => array(
+    'uid' => array(
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'normal',
+      'unsigned' => TRUE,
+    ),
+    'number' => array(
+      'type' => 'varchar',
+      'not null' => TRUE,
+      'length' => '32',
+    ),
+    'status' => array(
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'normal',
+      'unsigned' => TRUE,
+    ),
+    'code' => array(
+      'type' => 'varchar',
+      'not null' => FALSE,
+      'length' => '16',
+      'default' => '',
+    ),
+    'gateway' => array(
+      'type' => 'text',
+      'not null' => FALSE,
+      'size' => 'normal',
+    ),
+    'sleep_enabled' => array(
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'tiny',
+      'default' => '0',
+      'unsigned' => TRUE,
+    ),
+    'sleep_start_time' => array(
+      'type' => 'int',
+      'not null' => FALSE,
+      'size' => 'tiny',
+      'default' => '0',
+      'unsigned' => TRUE,
+    ),
+    'sleep_end_time' => array(
+      'type' => 'int',
+      'not null' => FALSE,
+      'size' => 'tiny',
+      'default' => '0',
+      'unsigned' => TRUE,
+    ),
+    'sms_user_opt_out' => array(
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'tiny',
+      'default' => '0',
+      'unsigned' => TRUE,
+    ),
+  ),
+  'primary key' => array(
+    'number',
+  ),
+  'indexes' => array(
+    'uid' => array(
+      'uid',
+    ),
+  ),
+  'mysql_character_set' => 'utf8',
+));
+
+$connection->insert('sms_user')
+->fields(array(
+  'uid',
+  'number',
+  'status',
+  'code',
+  'gateway',
+  'sleep_enabled',
+  'sleep_start_time',
+  'sleep_end_time',
+  'sms_user_opt_out',
+))
+->values(array(
+  'uid' => '40',
+  'number' => '1234567890',
+  'status' => '2',
+  'code' => '8309',
+  'gateway' => 'a:0:{}',
+  'sleep_enabled' => '1',
+  'sleep_start_time' => '12',
+  'sleep_end_time' => '16',
+  'sms_user_opt_out' => '1',
+))
+->values(array(
+  'uid' => '41',
+  'number' => '87654321190',
+  'status' => '1',
+  'code' => '8002',
+  'gateway' => 'a:0:{}',
+  'sleep_enabled' => '0',
+  'sleep_start_time' => '0',
+  'sleep_end_time' => '0',
+  'sms_user_opt_out' => '0',
+))
+->execute();
+
+$connection->insert('users')
+->fields(array(
+  'uid',
+  'name',
+  'pass',
+  'mail',
+  'created',
+  'access',
+  'login',
+  'status',
+  'init',
+))
+->values(array(
+  'uid' => '40',
+  'name' => 'joe',
+  'pass' => '$S$D/HVkgCg1Hvi7DN5KVSgNl.2C5g8W6oe/OoIRMUlyjkmPugQRhoB',
+  'mail' => 'joe@localhost',
+  'created' => '1505771205',
+  'access' => '1505772205',
+  'login' => '1505771593',
+  'status' => '1',
+  'init' => 'joe@localhost',
+))
+->values(array(
+  'uid' => '41',
+  'name' => 'jill',
+  'pass' => '$S$DGFZUE.FhrXbe4y52eC7p0ZVRGD/gOPtVctDlmC89qkujnBokAlJ',
+  'mail' => 'jill@example.com',
+  'created' => '1391150052',
+  'access' => '1391259672',
+  'login' => '1391152253',
+  'status' => '1',
+  'init' => 'jill@example.com',
+))
+->values(array(
+  'uid' => '42',
+  'name' => 'jack',
+  'pass' => '$S$DGFZUE.FhrXbe4y52eC7p0ZVRGD/gOPtVctDlmC89qkujnBokAlJ',
+  'mail' => 'jack@example.com',
+  'created' => '1391150053',
+  'access' => '1391259673',
+  'login' => '1391152254',
+  'status' => '1',
+  'init' => 'jack@example.com',
+))
+->execute();
diff --git a/tests/src/Kernel/Migrate/MigrateD6SmsPhoneNumberTest.php b/tests/src/Kernel/Migrate/MigrateD6SmsPhoneNumberTest.php
new file mode 100644
index 0000000..622b95b
--- /dev/null
+++ b/tests/src/Kernel/Migrate/MigrateD6SmsPhoneNumberTest.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Drupal\Tests\sms\Kernel\Migrate;
+
+use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
+
+class MigrateD6SmsPhoneNumberTest extends MigrateDrupal6TestBase {
+
+  use MigratePhoneNumberTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'sms',
+    'telephone',
+    'dynamic_entity_reference',
+  ];
+
+  /**
+   * Execute the D6 sms_user phone number migration.
+   */
+  protected function getMigrationsToTest() {
+    return [
+      'd6_filter_format',
+      'd6_user_role',
+      'd6_user',
+      'phone_number_settings',
+      'd6_sms_number',
+    ];
+  }
+
+  protected function getMigrationsToRollback() {
+    return [
+      'd6_sms_number',
+      'phone_number_settings',
+    ];
+  }
+
+  /**
+   * File path to the DB fixture for sms_user table and records.
+   */
+  protected function smsUserFixtureFilePath() {
+    return __DIR__ . '/../../../fixtures/migrate/sms_user_drupal6.php';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function confirmationMessageFixturePath() {
+    return __DIR__ . '/../../../fixtures/migrate/sms_confirmation_message_d6.php';
+  }
+
+}
diff --git a/tests/src/Kernel/Migrate/MigrateD7SmsPhoneNumberTest.php b/tests/src/Kernel/Migrate/MigrateD7SmsPhoneNumberTest.php
new file mode 100644
index 0000000..53440d2
--- /dev/null
+++ b/tests/src/Kernel/Migrate/MigrateD7SmsPhoneNumberTest.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace Drupal\Tests\sms\Kernel\Migrate;
+
+use Drupal\migrate\Exception\RequirementsException;
+use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
+
+class MigrateD7SmsPhoneNumberTest extends MigrateDrupal7TestBase {
+
+  use MigratePhoneNumberTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'sms',
+    'telephone',
+    'dynamic_entity_reference',
+  ];
+
+  /**
+   * Tests that the requirements for the d7_sms_number migration are enforced.
+   */
+  public function _testMigrationRequirements() {
+    $this->setExpectedException(RequirementsException::class, 'Missing migrations d7_user, phone_number_settings.');
+    $this->getMigration('d7_sms_number')->checkRequirements();
+  }
+
+  /**
+   * Execute the D7 sms_user phone number migration.
+   */
+  protected function getMigrationsToTest() {
+    return [
+      'd7_filter_format',
+      'd7_user_role',
+      'd7_user',
+      'phone_number_settings',
+      'd7_sms_number',
+    ];
+  }
+
+  protected function getMigrationsToRollback() {
+    return [
+      'd7_sms_number',
+      'phone_number_settings',
+    ];
+  }
+
+  /**
+   * File path to the DB fixture for sms_user table and records.
+   */
+  protected function smsUserFixtureFilePath() {
+    return __DIR__ . '/../../../fixtures/migrate/sms_user_drupal7.php';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function confirmationMessageFixturePath() {
+    return __DIR__ . '/../../../fixtures/migrate/sms_confirmation_message_d7.php';
+  }
+
+}
diff --git a/tests/src/Kernel/Migrate/MigratePhoneNumberTestTrait.php b/tests/src/Kernel/Migrate/MigratePhoneNumberTestTrait.php
new file mode 100644
index 0000000..00bf98e
--- /dev/null
+++ b/tests/src/Kernel/Migrate/MigratePhoneNumberTestTrait.php
@@ -0,0 +1,189 @@
+<?php
+
+namespace Drupal\Tests\sms\Kernel\Migrate;
+
+use Drupal\Core\Entity\Entity\EntityFormDisplay;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\migrate\MigrateExecutable;
+use Drupal\sms\Entity\PhoneNumberSettings;
+use Drupal\sms\Entity\PhoneNumberVerification;
+use Drupal\sms\Plugin\Migrate\process\PhoneNumberSettings as PhoneNumberSettingsPlugin;
+use Drupal\user\Entity\User;
+use Drupal\user\UserInterface;
+
+trait MigratePhoneNumberTestTrait {
+
+  /**
+   * Tests migration of phone number settings based on legacy configuration.
+   */
+  public function testPhoneSettingsMigration() {
+    $settings = PhoneNumberSettings::loadMultiple();
+    $this->assertEquals([], $settings);
+
+    // Execute the phone number settings migration and confirm.
+    $this->executeMigration('phone_number_settings');
+
+    // Confirm new phone number settings is created.
+    $settings = PhoneNumberSettings::loadMultiple();
+    $this->assertEquals(1, count($settings));
+    /** @var \Drupal\sms\Entity\PhoneNumberSettingsInterface $setting */
+    $setting = reset($settings);
+    $this->assertEquals(PhoneNumberSettingsPlugin::DEFAULT_VERIFICATION_MESSAGE, $setting->getVerificationMessage());
+    $this->assertEquals('phone_number', $setting->getFieldName('phone_number'));
+    $this->assertEquals(true, $setting->getPurgeVerificationPhoneNumber());
+    $this->assertEquals('user', $setting->getPhoneNumberBundle());
+    $this->assertEquals('user', $setting->getPhoneNumberEntityTypeId());
+    $this->assertEquals(600, $setting->getVerificationCodeLifetime());
+
+    // Confirm that a new phone number field is created.
+    $field_storage = FieldStorageConfig::load('user.phone_number');
+    $this->assertEquals('user.phone_number', $field_storage->id());
+    $this->assertEquals('phone_number', $field_storage->getName());
+    $this->assertEquals('user', $field_storage->getTargetEntityTypeId());
+    $this->assertEquals('telephone', $field_storage->getType());
+
+    $field_config = FieldConfig::load('user.user.phone_number');
+    $this->assertEquals('user', $field_config->getTargetEntityTypeId());
+    $this->assertEquals('user', $field_config->getTargetBundle());
+  }
+
+  /**
+   * Tests phone number migration with custom phone number verification message.
+   */
+  public function testPhoneSettingsMigrationWithCustomVerificationMessage() {
+    $this->loadFixture($this->confirmationMessageFixturePath());
+
+    // Execute the phone number settings migration and confirm.
+    $this->executeMigration('phone_number_settings');
+
+    $settings = PhoneNumberSettings::loadMultiple();
+    $this->assertEquals(1, count($settings));
+    /** @var \Drupal\sms\Entity\PhoneNumberSettingsInterface $setting */
+    $setting = reset($settings);
+    $expected_message = 'This is a custom confirmation message from [site:name]. Confirmation code: [sms-message:verification-code]';
+
+    $this->assertEquals($expected_message, $setting->getVerificationMessage());
+  }
+
+  /**
+   * Tests that the users' phone numbers verification status is migrated.
+   */
+  public function testPhoneNumberMigration() {
+    $this->loadFixture($this->smsUserFixtureFilePath());
+
+    // Set up phone number verifications.
+    $this->installEntitySchema('sms');
+    $this->installEntitySchema('sms_phone_number_verification');
+
+    $this->executeMigrations($this->getMigrationsToTest());
+
+    $user = User::load(40);
+    $this->assertEquals('1234567890', $user->get('phone_number')->value);
+    $this->assertVerifiedPhoneNumber($user, '1234567890');
+
+    $user = User::load(41);
+    $this->assertEquals('87654321190', $user->get('phone_number')->value);
+    $this->assertUnVerifiedPhoneNumber($user, '87654321190');
+    $this->assertVerificationCode('87654321190', '8002');
+
+    // No phone number for user 15.
+    $user = User::load(42);
+    $this->assertEquals('', $user->get('phone_number')->value);
+    $this->assertNoVerifiedPhoneNumber($user);
+  }
+
+  /**
+   * Tests that conditions are reverted after rollback.
+   */
+  public function testRollBack() {
+    $this->loadFixture($this->smsUserFixtureFilePath());
+    $this->installEntitySchema('sms');
+    $this->installEntitySchema('sms_phone_number_verification');
+
+    // Create an entity form display
+    EntityFormDisplay::create([
+      'targetEntityType' => 'user',
+      'bundle' => 'user',
+      'mode' => 'default',
+    ])->setStatus(TRUE)->save();
+
+    $this->executeMigrations($this->getMigrationsToTest());
+
+    $this->assertVerifiedPhoneNumber(User::load(40), '1234567890');
+    // Test that the default entity form display has the field added.
+    $entity_form_display = EntityFormDisplay::load('user.user.default');
+    $this->assertNotNull($entity_form_display->getComponent('phone_number'));
+
+    // Rollback migration and check that verifications, phone number settings
+    // and phone number fields are removed.
+    $this->rollBackMigrations($this->getMigrationsToRollBack());
+
+    // Assert no phone number verifications, phone number settings or phone
+    // number fields exist.
+    $this->assertEquals([], PhoneNumberVerification::loadMultiple());
+    $this->assertEquals([], PhoneNumberSettings::loadMultiple());
+    $this->assertNull(FieldConfig::loadByName('user', 'user', 'phone_number'));
+    $this->assertNull(FieldStorageConfig::loadByName('user', 'phone_number'));
+
+    // Test that the display field is removed.
+    $entity_form_display = EntityFormDisplay::load('user.user.default');
+    $this->assertNull($entity_form_display->getComponent('phone_number'));
+  }
+
+  /**
+   * Asserts that the specified user has a verified phone number.
+   */
+  protected function assertVerifiedPhoneNumber(UserInterface $user, $number) {
+    $phone_numbers = $this->container->get('sms.phone_number')->getPhoneNumbers($user, TRUE);
+    $phone_number = reset($phone_numbers);
+    return $this->assertEquals($phone_number, $number, "Phone number '$number' is verified.");
+  }
+
+  /**
+   * Asserts that the specified user has an unverified phone number.
+   */
+  protected function assertUnVerifiedPhoneNumber(UserInterface $user, $number) {
+    $phone_numbers = $this->container->get('sms.phone_number')->getPhoneNumbers($user, FALSE);
+    $phone_number = reset($phone_numbers);
+    return $this->assertEquals($phone_number, $number, "Phone number '$number' is unverified.");
+  }
+
+  /**
+   * Asserts that the specified user has no phone number verified or unverified.
+   */
+  protected function assertNoVerifiedPhoneNumber(UserInterface $user) {
+    $phone_numbers = $this->container->get('sms.phone_number')->getPhoneNumbers($user);
+    return $this->assertEquals([], $phone_numbers, "No phone numbers for user {$user->id()}.");
+  }
+
+  /**
+   * Asserts that the specified number has a pending verification code.
+   */
+  protected function assertVerificationCode($number, $code) {
+    $verification = $this->container->get('sms.phone_number.verification')->getPhoneVerificationByPhoneNumber($number, FALSE);
+    $verification = reset($verification);
+    return $this->assertEquals($code, $verification->getCode());
+  }
+
+  /**
+   * Rolls back a specified migration.
+   */
+  protected function rollBackMigrations(array $ids) {
+    foreach ($ids as $id) {
+      $this->migration = $this->getMigration($id);
+      $this->prepareMigration($this->migration);
+      (new MigrateExecutable($this->migration, $this))->rollback();
+    }
+  }
+
+  /**
+   * Implementing classes must implement this to provide database query paths.
+   */
+  abstract protected function confirmationMessageFixturePath();
+
+  abstract protected function getMigrationsToTest();
+
+  abstract protected function getMigrationsToRollback();
+
+}
