I do this all the time, making use of migration configuration entities (provided by the [migrate_plus module][1]). Define a migration plugin in your migration module's config/install directory, using the CSV source plugin from the [migrate_source_csv module][2] - omitting the 'path' source configuration, which will be filled in from the form. Let's say the ID of this migration is example_csv. Create a form with a file upload element (named 'csv_file' in this case), and in the submitForm() method:

      public function submitForm(array &$form, FormStateInterface $form_state) {
        $all_files = $this->getRequest()->files->get('files', []);
        if (!empty($all_files['csv_file'])) {
          $validators = ['file_validate_extensions' => ['csv']];
          if ($file = file_save_upload('csv_file', $validators, 'public://', 0)) {
            $csv_migration = Migration::load('example_csv');
            $source = $csv_migration->get('source');
            $source['path'] = $file->getFileUri();
            $csv_migration->set('source', $source);
            $csv_migration->save();
            drupal_set_message($this->t('File uploaded as @uri.', ['@uri' => $file->getFileUri()]));
          }
          else {
            drupal_set_message($this->t('File upload failed.'));
          }
        }
      }


  [1]: https://www.drupal.org/project/migrate_plus
  [2]: https://www.drupal.org/project/migrate_source_csv