Overview
This guide covers the complete user lifecycle in Drupal: adding new users, managing user accounts, resetting passwords, blocking users, and properly removing user access. Learn best practices for user onboarding and offboarding.
Adding Users
Method 1: Manual User Creation (Admin UI)
Prerequisites:
- Permission:
Administer users - Access to:
/admin/people
Steps:
- Navigate to Admin → People (
/admin/people) - Click Add user button
- Fill in required fields:
- Username: Unique identifier
- Email address: Must be unique
- Password: Or check "Generate password"
- Confirm password: (if entering manually)
- Configure optional settings:
- Status: Active or Blocked
- Roles: Select appropriate roles
- Notify user: Send account creation email
- Click Create new account
Best Practices:
Good:
- Use email as username for easier identification
- Assign minimal required roles
- Enable "Notify user of new account"
- Generate strong password
- Document reason for account creation
✗ Avoid:
- Creating accounts without notification
- Using weak passwords
- Giving administrator role immediately
- Creating accounts without proper approval
Method 2: User Self-Registration
Configure Registration Settings:
Navigate to: /admin/config/people/accounts
Registration Options:
1. Administrators only (Default, most secure)
☑ Only site administrators can create new user accounts.
2. Visitors (with admin approval)
☑ Visitors can create accounts but administrator approval is required.
- User registers at
/user/register - Account created but blocked
- Admin receives notification email
- Admin must approve at
/admin/people
3. Visitors (open registration)
☑ Visitors can create accounts and no administrator approval is required.
- Risk of spam accounts
- Requires CAPTCHA protection
- Monitor for abuse
Enhance with CAPTCHA:
# Install CAPTCHA protection
composer require drupal/captcha drupal/recaptcha
drush en captcha recaptcha -y
Configure at: /admin/config/people/captcha
Method 3: Drush Command Line
# Create user with password
drush user:create johndoe --mail="john@example.com" --password="SecurePass123!"
# Create user and generate password
drush user:create janedoe --mail="jane@example.com"
# Create user with role
drush user:create editor1 --mail="editor@example.com" --password="Pass123" && \
drush user:role:add "content_editor" editor1
# Create multiple users in sequence
for user in user1 user2 user3; do
drush user:create "$user" --mail="$user@example.com" --password="TempPass123"
drush user:role:add "authenticated" "$user"
done
Method 4: Programmatic User Creation
Simple user creation:
<?php
use Drupal\user\Entity\User;
// Create user
$user = User::create([
'name' => 'johndoe',
'mail' => 'john@example.com',
'pass' => 'SecurePassword123!',
'status' => 1, // Active
'roles' => ['content_editor'],
'field_first_name' => 'John',
'field_last_name' => 'Doe',
]);
$user->save();
// Get user ID
$uid = $user->id();
With error handling:
<?php
use Drupal\user\Entity\User;
try {
// Check if username exists
$existing = \Drupal::entityTypeManager()
->getStorage('user')
->loadByProperties(['name' => 'johndoe']);
if (!empty($existing)) {
throw new \Exception('Username already exists');
}
// Check if email exists
$existing = \Drupal::entityTypeManager()
->getStorage('user')
->loadByProperties(['mail' => 'john@example.com']);
if (!empty($existing)) {
throw new \Exception('Email already exists');
}
// Create user
$user = User::create([
'name' => 'johndoe',
'mail' => 'john@example.com',
'pass' => 'SecurePassword123!',
'status' => 1,
'roles' => ['content_editor'],
]);
$user->save();
// Send notification email
_user_mail_notify('register_admin_created', $user);
\Drupal::logger('user_management')->info('Created user: @name', [
'@name' => $user->getAccountName()
]);
} catch (\Exception $e) {
\Drupal::logger('user_management')->error('Failed to create user: @error', [
'@error' => $e->getMessage()
]);
}
Method 5: Bulk User Import
Using Feeds module:
composer require drupal/feeds drupal/feeds_ex
drush en feeds feeds_ex -y
Configure import:
- Navigate to
/admin/structure/feeds - Add feed type: User Import
- Select parser: CSV
- Configure mappings:
- CSV Column → User field
- email → Email address
- username → Username
- role → Roles
CSV format:
username,email,role,status,first_name,last_name
john.doe,john@example.com,content_editor,1,John,Doe
jane.smith,jane@example.com,site_manager,1,Jane,Smith
bob.jones,bob@example.com,authenticated,1,Bob,Jones
Import:
- Navigate to
/import - Choose User Import
- Upload CSV file
- Review mappings
- Import users
Managing Existing Users
Editing User Accounts
Via UI:
- Navigate to
/admin/people - Find user (use filters if needed)
- Click Edit
- Modify fields:
- Email address
- Username (if allowed)
- Password
- Roles
- Status (Active/Blocked)
- Custom fields
- Click Save
Via Drush:
# Change email
drush user:edit johndoe --mail="newemail@example.com"
# Change password
drush user:password johndoe "NewSecurePass123!"
# Add role
drush user:role:add "administrator" johndoe
# Remove role
drush user:role:remove "content_editor" johndoe
Programmatically:
<?php
use Drupal\user\Entity\User;
// Load user
$user = User::load(123);
// Or by username
$users = \Drupal::entityTypeManager()
->getStorage('user')
->loadByProperties(['name' => 'johndoe']);
$user = reset($users);
// Update fields
$user->setEmail('newemail@example.com');
$user->set('field_phone', '555-1234');
$user->addRole('editor');
$user->removeRole('content_creator');
// Save changes
$user->save();
Password Management
User Password Reset (Self-Service)
User-initiated:
- User goes to
/user/login - Clicks Reset your password
- Enters email address
- Receives password reset email
- Clicks link in email
- Sets new password
Configure reset email:
Navigate to: /admin/config/people/accounts → Emails tab → Password recovery
Customize template:
[user:display-name],
A request to reset the password for your account has been made at [site:name].
You may now log in by clicking this link or copying and pasting it into your browser:
[user:one-time-login-url]
This link can only be used once to log in and will lead you to a page where you can set your password.
If you did not request this email, please ignore it.
-- [site:name] team
Admin Password Reset
Via UI:
- Navigate to
/admin/people - Find user
- Click Edit
- Enter new password in Password field
- Confirm password
- Check "Notify user of new password" (optional)
- Click Save
Via Drush:
# Set specific password
drush user:password johndoe "NewPassword123"
# Generate random password
drush user:password johndoe --password=$(openssl rand -base64 12)
# Generate one-time login link
drush user:login johndoe
# Returns: http://example.com/user/reset/1/1234567890/abcdef...
Programmatically:
<?php
use Drupal\user\Entity\User;
// Load user
$user = User::load(123);
// Set password
$user->setPassword('NewSecurePassword123!');
$user->save();
// Or use password hasher
$password_hasher = \Drupal::service('password');
$hashed = $password_hasher->hash('PlainTextPassword');
$user->pass = $hashed;
$user->save();
// Generate one-time login link
$timestamp = \Drupal::time()->getRequestTime();
$login_link = user_pass_reset_url($user);
Force Password Change
Using Password Policy module:
composer require drupal/password_policy
drush en password_policy password_policy_expire -y
Configure at: /admin/config/security/password-policy
Or manually:
<?php
use Drupal\user\Entity\User;
// Force password change on next login
$user = User::load(123);
$user->set('field_force_password_change', TRUE);
$user->save();
// Check on login
function mymodule_user_login(User $account) {
if ($account->get('field_force_password_change')->value) {
\Drupal::messenger()->addWarning('You must change your password.');
// Redirect to password change form
return new RedirectResponse('/user/' . $account->id() . '/edit');
}
}
Blocking Users
Why Block Instead of Delete
Block when:
- Temporary suspension needed
- User may return
- Content ownership must be preserved
- Investigation required
Delete when:
- Permanent removal requested
- Compliance requirements (GDPR)
- Spam/bot accounts
Blocking via UI
Single user:
- Navigate to
/admin/people - Find user
- Click Edit
- Under Status, select Blocked
- Click Save
Bulk blocking:
- Navigate to
/admin/people - Select users (checkboxes)
- Choose Block the selected user(s) from dropdown
- Click Apply to selected items
Blocking via Drush
# Block user
drush user:block johndoe
# Unblock user
drush user:unblock johndoe
# Check user status
drush user:information johndoe
Blocking Programmatically
<?php
use Drupal\user\Entity\User;
// Block user
$user = User::load(123);
$user->block();
$user->save();
// Unblock user
$user->activate();
$user->save();
// Check status
if ($user->isBlocked()) {
// User is blocked
}
Automatic Blocking
Block after failed login attempts:
composer require drupal/flood_control
drush en flood_control -y
Configure at: /admin/config/system/flood-control
Block inactive accounts:
composer require drupal/autoblock
drush en autoblock -y
Configure at: /admin/config/people/autoblock
Removing Users
Understanding Deletion Options
Navigate to: /admin/config/people/accounts → Account Cancellation tab
Options:
1. Disable the account and keep its content
☐ Disable account and keep content
- Account blocked (not deleted)
- All content remains attributed to user
- Can be re-enabled later
2. Disable the account and unpublish its content
☐ Disable account and unpublish content
- Account blocked
- All created content unpublished
- Content still owned by user
3. Delete the account and make its content belong to Anonymous
☐ Delete account, reassign content to Anonymous
- Account permanently deleted
- Content ownership transferred to Anonymous
- Cannot be reversed
4. Delete the account and its content
☐ Delete account and all content
- Complete removal
- All content deleted
- Cannot be reversed
- Use with extreme caution
Deleting via UI
Single user:
- Navigate to
/admin/people - Find user
- Click Edit
- Scroll to bottom
- Click Cancel account
- Select deletion method
- Confirm deletion
Bulk deletion:
- Navigate to
/admin/people - Select users (checkboxes)
- Choose Cancel the selected user accounts from dropdown
- Click Apply to selected items
- Select deletion method
- Confirm
Deleting via Drush
# Cancel user (requires confirmation)
drush user:cancel johndoe
# Force deletion without confirmation
drush user:cancel johndoe --delete-content -y
# Cancel multiple users
drush user:cancel user1,user2,user3 -y
Deleting Programmatically
<?php
use Drupal\user\Entity\User;
// Load user
$user = User::load(123);
// Delete user and keep content
$user->delete();
// Delete user and content
// First, delete user's content
$nids = \Drupal::entityQuery('node')
->condition('uid', $user->id())
->accessCheck(FALSE)
->execute();
$storage = \Drupal::entityTypeManager()->getStorage('node');
$nodes = $storage->loadMultiple($nids);
$storage->delete($nodes);
// Then delete user
$user->delete();
Proper User Offboarding Checklist
Before deletion:
- Document reason for removal
- Transfer content ownership to active user
- Export user data if needed for records
- Reassign tasks/workflows to other users
- Revoke API keys if any
- Remove from external systems (SSO, LDAP, etc.)
- Notify stakeholders of account removal
- Wait period for any objections (e.g., 30 days)
During deletion:
- Choose appropriate deletion method
- Block account first (test impact)
- Export final content list owned by user
- Document deletion in ticket/log
- Screenshot proof of deletion
After deletion:
- Verify removal from user list
- Check content attribution correct
- Audit permissions removed
- Update documentation (remove from contact lists, etc.)
User Data Export (GDPR Compliance)
Export User Data
Using Views Data Export:
composer require drupal/views_data_export
drush en views_data_export -y
Create export view:
- Navigate to
/admin/structure/views/add - Show: Users
- Display: Data export
- Format: CSV or JSON
- Fields: Email, Username, Created, Roles, Custom fields
- Path:
/admin/reports/user-export - Save
Export specific user:
# Via Drush (custom command)
drush user:export johndoe > user-johndoe-export.json
Programmatically:
<?php
use Drupal\user\Entity\User;
// Load user
$user = User::load(123);
// Gather all user data
$export = [
'account' => [
'uid' => $user->id(),
'name' => $user->getAccountName(),
'mail' => $user->getEmail(),
'created' => $user->getCreatedTime(),
'changed' => $user->getChangedTime(),
'status' => $user->isActive(),
'roles' => $user->getRoles(),
],
'fields' => [],
];
// Export custom fields
foreach ($user->getFields() as $field_name => $field) {
if (!in_array($field_name, ['uid', 'name', 'mail', 'pass', 'init'])) {
$export['fields'][$field_name] = $user->get($field_name)->getValue();
}
}
// Export user's content
$nids = \Drupal::entityQuery('node')
->condition('uid', $user->id())
->accessCheck(FALSE)
->execute();
$export['content'] = [
'count' => count($nids),
'node_ids' => array_values($nids),
];
// Convert to JSON
$json = json_encode($export, JSON_PRETTY_PRINT);
file_put_contents("user-{$user->id()}-export.json", $json);
Bulk Operations
Using Views Bulk Operations
composer require drupal/views_bulk_operations
drush en views_bulk_operations -y
Features:
- Select multiple users
- Apply actions in bulk
- Custom actions
- Batch processing
Custom Bulk Actions
Create bulk add role action:
<?php
namespace Drupal\mymodule\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Session\AccountInterface;
/**
* Adds a specific role to users.
*
* @Action(
* id = "add_custom_role",
* label = @Translation("Add Marketing role"),
* type = "user"
* )
*/
class AddMarketingRole extends ActionBase {
public function execute($account = NULL) {
$account->addRole('marketing_manager');
$account->save();
}
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
return $object->access('update', $account, $return_as_object);
}
}
Automation & Workflows
Automatic User Creation on Form Submission
<?php
use Drupal\user\Entity\User;
use Drupal\webform\WebformSubmissionInterface;
/**
* Implements hook_webform_submission_insert().
*/
function mymodule_webform_submission_insert(WebformSubmissionInterface $submission) {
$webform_id = $submission->getWebform()->id();
if ($webform_id === 'user_registration') {
$data = $submission->getData();
// Create user from form data
$user = User::create([
'name' => $data['username'],
'mail' => $data['email'],
'pass' => user_password(12),
'status' => 0, // Blocked until approved
'field_company' => $data['company'],
'field_phone' => $data['phone'],
]);
$user->save();
// Notify admin
$mailManager = \Drupal::service('plugin.manager.mail');
$mailManager->mail(
'mymodule',
'new_user_pending',
'admin@example.com',
'en',
['user' => $user]
);
}
}
Scheduled User Cleanup
<?php
/**
* Implements hook_cron().
*/
function mymodule_cron() {
// Delete users inactive for 1 year
$one_year_ago = strtotime('-1 year');
$uids = \Drupal::entityQuery('user')
->condition('access', $one_year_ago, '<')
->condition('uid', 1, '>')
->condition('status', 1)
->accessCheck(FALSE)
->execute();
if (!empty($uids)) {
$storage = \Drupal::entityTypeManager()->getStorage('user');
$users = $storage->loadMultiple($uids);
foreach ($users as $user) {
// Block first (don't delete immediately)
$user->block();
$user->save();
// Log for review
\Drupal::logger('user_cleanup')->info('Blocked inactive user: @name', [
'@name' => $user->getAccountName()
]);
}
}
}
Troubleshooting
Can't Delete User
Error: "User cannot be deleted"
Causes:
- User owns content
- User ID 1 (super admin)
- User referenced in other entities
Solution:
- Reassign content first
- Remove references
- Use cancellation instead of deletion
Duplicate Email
Error: "Email already in use"
Solution:
# Find user with email
drush user:list --mail="duplicate@example.com"
# Either delete duplicate or change email
drush user:edit user1 --mail="new-email@example.com"
Can't Log In After Creation
Causes:
- Account blocked
- Password not set
- Email not verified
Solution:
# Check user status
drush user:information username
# Unblock if needed
drush user:unblock username
# Generate login link
drush user:login username
Resources
Next Steps
Quick Reference
Essential Commands
# Create user
drush user:create USERNAME --mail="EMAIL" --password="PASS"
# Add role
drush user:role:add "ROLE" USERNAME
# Block user
drush user:block USERNAME
# Unblock user
drush user:unblock USERNAME
# Delete user
drush user:cancel USERNAME --delete-content -y
# Reset password
drush user:password USERNAME "NEWPASS"
# One-time login
drush user:login USERNAME
# List users
drush user:list