diff --git a/core/core.console.services.yml b/core/core.console.services.yml
index 6f70991..70189a6 100644
--- a/core/core.console.services.yml
+++ b/core/core.console.services.yml
@@ -20,3 +20,9 @@ services:
     arguments: ['@console.bootstrap']
     tags:
       - { name: console.command }
+
+  console.command.hash_password:
+    class: Drupal\Core\Console\Command\HashPassword
+    arguments: ['@console.bootstrap']
+    tags:
+      - { name: console.command }
diff --git a/core/lib/Drupal/Core/Console/Command/HashPassword.php b/core/lib/Drupal/Core/Console/Command/HashPassword.php
new file mode 100644
index 0000000..7a5db71
--- /dev/null
+++ b/core/lib/Drupal/Core/Console/Command/HashPassword.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Console\Command\HashPassword.
+ */
+
+namespace Drupal\Core\Console\Command;
+
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * Hashes a password.
+ *
+ * @todo Use a hidden question helper once we've upgraded to Symfony 2.5.
+ */
+class HashPassword extends CommandBootstrapBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function configure() {
+    parent::configure();
+    $this
+      ->setName('drupal:hash-password')
+      ->setDescription('Hashes a password.')
+      ->addArgument('password', InputArgument::REQUIRED, 'Password');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function execute(InputInterface $input, OutputInterface $output) {
+    $kernel = $this->bootstrap->bootstrap($this, $input, $output);
+    if ($kernel) {
+      /** @var \Drupal\Core\Password\PasswordInterface $password_hasher */
+      $password_hasher = $kernel->getContainer()->get('password');
+      $output->writeln($password_hasher->hash($input->getArgument('password')));
+    }
+  }
+
+}
diff --git a/core/scripts/password-hash.sh b/core/scripts/password-hash.sh
deleted file mode 100755
index f80d75c..0000000
--- a/core/scripts/password-hash.sh
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/usr/bin/env php
-<?php
-
-/**
- * Drupal hash script - to generate a hash from a plaintext password
- *
- * @param password1 [password2 [password3 ...]]
- *  Plain-text passwords in quotes (or with spaces backslash escaped).
- */
-
-use Drupal\Core\DrupalKernel;
-
-// Check for $_SERVER['argv'] instead of PHP_SAPI === 'cli' to allow this script
-// to be tested with the Simpletest UI test runner.
-// @see \Drupal\system\Tests\System\ScriptTest
-if (!isset($_SERVER['argv']) || !is_array($_SERVER['argv'])) {
-  return;
-}
-
-if (version_compare(PHP_VERSION, "5.4.2", "<")) {
-  $version  = PHP_VERSION;
-  echo <<<EOF
-
-ERROR: This script requires at least PHP version 5.4.2. You invoked it with
-       PHP version {$version}.
-\n
-EOF;
-  exit;
-}
-
-$script = basename(array_shift($_SERVER['argv']));
-
-if (in_array('--help', $_SERVER['argv']) || empty($_SERVER['argv'])) {
-  echo <<<EOF
-
-Generate Drupal password hashes from the shell.
-
-Usage:        {$script} [OPTIONS] "<plan-text password>"
-Example:      {$script} "mynewpassword"
-
-All arguments are long options.
-
-  --help      Print this page.
-
-  "<password1>" ["<password2>" ["<password3>" ...]]
-
-              One or more plan-text passwords enclosed by double quotes. The
-              output hash may be manually entered into the {users}.pass field to
-              change a password via SQL to a known value.
-
-
-EOF;
-  exit;
-}
-
-// Password list to be processed.
-$passwords = $_SERVER['argv'];
-
-$core = dirname(__DIR__);
-require_once $core . '/vendor/autoload.php';
-require_once $core . '/includes/bootstrap.inc';
-
-// Bootstrap the code so we have the container.
-drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
-
-$kernel = new DrupalKernel('prod', drupal_classloader(), FALSE);
-$kernel->boot();
-
-$password_hasher = $kernel->getContainer()->get('password');
-
-foreach ($passwords as $password) {
-  print("\npassword: $password \t\thash: ". $password_hasher->hash($password) ."\n");
-}
-print("\n");
-
diff --git a/core/tests/Drupal/Tests/Core/Console/Command/HashPasswordTest.php b/core/tests/Drupal/Tests/Core/Console/Command/HashPasswordTest.php
new file mode 100644
index 0000000..98a3bd3
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Console/Command/HashPasswordTest.php
@@ -0,0 +1,100 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Console\Command\HashPasswordTest.
+ */
+
+namespace Drupal\Tests\Core\Console\Command;
+
+use Drupal\Core\Console\Command\HashPassword;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Console\Command\HashPassword
+ */
+class HashPasswordTest extends UnitTestCase {
+
+  /**
+   * The bootstrap service.
+   *
+   * @var \Drupal\Core\Console\BootstrapInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $bootstrap;
+
+  /**
+   * The command under test.
+   *
+   * @var \Drupal\Core\Console\Command\HashPassword
+   */
+  protected $command;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'description' => '',
+      'name' => '\Drupal\Core\Console\HashPassword unit test',
+      'group' => 'Console',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * @covers ::__construct
+   * @covers ::configure
+   */
+  public function setUp() {
+    $this->bootstrap = $this->getMock('\Drupal\Core\Console\BootstrapInterface');
+
+    $this->command = new HashPassword($this->bootstrap);
+  }
+
+  /**
+   * @covers ::execute
+   */
+  public function testExecute() {
+    $password = $this->randomName();
+    $hash = $this->randomName();
+
+    $input = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+    $input->expects($this->atLeastOnce())
+      ->method('getArgument')
+      ->with('password')
+      ->will($this->returnValue($password));
+
+    $output = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+    $output->expects($this->atLeastOnce())
+      ->method('writeln')
+      ->with($hash);
+
+    $password_hasher = $this->getMock('\Drupal\Core\Password\PasswordInterface');
+    $password_hasher->expects($this->once())
+      ->method('hash')
+      ->with($password)
+      ->will($this->returnValue($hash));
+
+    $container = $this->getMock('\Symfony\Component\DependencyInjection\ContainerInterface');
+    $container->expects($this->once())
+      ->method('get')
+      ->with('password')
+      ->will($this->returnValue($password_hasher));
+
+    $kernel = $this->getMock('\Drupal\Core\DrupalKernelInterface');
+    $kernel->expects($this->atLeastOnce())
+      ->method('getContainer')
+      ->will($this->returnValue($container));
+
+    $this->bootstrap->expects($this->once())
+      ->method('bootstrap')
+      ->with($this->command, $input, $output)
+      ->will($this->returnValue($kernel));
+
+    $method = new \ReflectionMethod($this->command, 'execute');
+    $method->setAccessible(TRUE);
+    $method->invoke($this->command, $input, $output);
+  }
+
+}
