Proper user lifecycle management ensures secure access control and compliance in Magento 2. This guide covers creating admin users, managing account settings, implementing secure offboarding procedures, and maintaining access audits.
Adding New Users
Via Admin Panel
Step-by-Step Process
1. Navigate to User Management:
System > Permissions > All Users > Add New User
2. Account Information Tab:
Required Fields:
- User Name: Unique identifier (lowercase, no spaces)
- Example:
john.smithorjsmith
- Example:
- First Name: User's first name
- Last Name: User's last name
- Email: Valid email address for notifications
- Password: Strong password meeting policy requirements
- Password Confirmation: Must match password
Optional Fields:
- Interface Locale: Language preference (English, French, etc.)
- This account is: Active/Inactive status
3. User Role Tab:
Assign Role:
- Select appropriate role from dropdown
- Only one role per user
- Create custom role if needed
4. Current User Identity Verification:
Security Check:
- Your Password: Enter your current admin password
- Required for audit trail
5. Save User:
- Click "Save User" button
- User appears in grid
- Confirmation message displayed
Admin Panel Example
Creating Marketing User:
User Name: jane.marketing
First Name: Jane
Last Name: Doe
Email: jane.doe@company.com
Password: [Generate strong password]
Interface Locale: English (United States)
Status: Active
User Role: Marketing Manager
Your Password: [Your admin password]
Via Command Line (CLI)
Create Admin User
Basic Command:
php bin/magento admin:user:create \
--admin-user="username" \
--admin-password="StrongPass123!" \
--admin-email="user@example.com" \
--admin-firstname="First" \
--admin-lastname="Last"
With Role Assignment:
php bin/magento admin:user:create \
--admin-user="catalog.manager" \
--admin-password="SecurePass456!" \
--admin-email="catalog@company.com" \
--admin-firstname="Catalog" \
--admin-lastname="Manager" \
--admin-role="Catalog Manager"
Interactive Mode:
# Run command without parameters for prompts
php bin/magento admin:user:create
# System will prompt for each field:
Admin user: catalog.editor
Admin password: ********
Admin email: editor@company.com
Admin first name: Catalog
Admin last name: Editor
Verify Creation
List all admin users:
php bin/magento admin:user:list
Output:
+----+------------------+-----------+----------+----------------------+
| ID | Username | First | Last | Email |
+----+------------------+-----------+----------+----------------------+
| 1 | admin | Admin | User | admin@company.com |
| 2 | catalog.manager | Catalog | Manager | catalog@company.com |
| 3 | jane.marketing | Jane | Doe | jane@company.com |
+----+------------------+-----------+----------+----------------------+
Programmatic User Creation
Custom Module Implementation
File: app/code/Vendor/Module/Model/UserCreator.php
<?php
namespace Vendor\Module\Model;
use Magento\User\Model\UserFactory;
use Magento\Authorization\Model\RoleFactory;
use Magento\Framework\Exception\LocalizedException;
use Psr\Log\LoggerInterface;
class UserCreator
{
protected $userFactory;
protected $roleFactory;
protected $logger;
public function __construct(
UserFactory $userFactory,
RoleFactory $roleFactory,
LoggerInterface $logger
) {
$this->userFactory = $userFactory;
$this->roleFactory = $roleFactory;
$this->logger = $logger;
}
/**
* Create admin user with role
*
* @param array $userData
* @param string $roleName
* @return \Magento\User\Model\User
* @throws LocalizedException
*/
public function createUser($userData, $roleName)
{
try {
// Validate required fields
$this->validateUserData($userData);
// Create user
$user = $this->userFactory->create();
$user->setData([
'username' => $userData['username'],
'firstname' => $userData['firstname'],
'lastname' => $userData['lastname'],
'email' => $userData['email'],
'password' => $userData['password'],
'is_active' => $userData['is_active'] ?? 1,
'interface_locale' => $userData['locale'] ?? 'en_US'
]);
$user->save();
// Assign role
$role = $this->getRoleByName($roleName);
if ($role) {
$user->setRoleId($role->getId());
$user->save();
}
$this->logger->info('Admin user created: ' . $userData['username']);
return $user;
} catch (\Exception $e) {
$this->logger->error('User creation failed: ' . $e->getMessage());
throw new LocalizedException(__('Unable to create user: %1', $e->getMessage()));
}
}
protected function validateUserData($data)
{
$required = ['username', 'firstname', 'lastname', 'email', 'password'];
foreach ($required as $field) {
if (empty($data[$field])) {
throw new LocalizedException(__('Missing required field: %1', $field));
}
}
// Validate email
if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
throw new LocalizedException(__('Invalid email address'));
}
// Check username uniqueness
$existingUser = $this->userFactory->create()->loadByUsername($data['username']);
if ($existingUser->getId()) {
throw new LocalizedException(__('Username already exists'));
}
}
protected function getRoleByName($roleName)
{
$role = $this->roleFactory->create();
$role->load($roleName, 'role_name');
return $role->getId() ? $role : null;
}
}
Usage:
$userCreator = $objectManager->get(\Vendor\Module\Model\UserCreator::class);
$userData = [
'username' => 'new.user',
'firstname' => 'New',
'lastname' => 'User',
'email' => 'newuser@company.com',
'password' => 'SecurePassword123!',
'is_active' => 1,
'locale' => 'en_US'
];
$user = $userCreator->createUser($userData, 'Marketing Manager');
Bulk User Import
CSV Import Script
CSV Format: users.csv
username,firstname,lastname,email,password,role,active
john.smith,John,Smith,john@company.com,Pass123!,Marketing Manager,1
jane.doe,Jane,Doe,jane@company.com,Pass456!,Content Editor,1
bob.johnson,Bob,Johnson,bob@company.com,Pass789!,Catalog Manager,1
Import Script:
<?php
use Magento\Framework\App\Bootstrap;
require __DIR__ . '/app/bootstrap.php';
$bootstrap = Bootstrap::create(BP, $_SERVER);
$objectManager = $bootstrap->getObjectManager();
$state = $objectManager->get(\Magento\Framework\App\State::class);
$state->setAreaCode(\Magento\Framework\App\Area::AREA_ADMINHTML);
$userCreator = $objectManager->get(\Vendor\Module\Model\UserCreator::class);
$csvFile = 'users.csv';
$handle = fopen($csvFile, 'r');
// Skip header row
$header = fgetcsv($handle);
while (($row = fgetcsv($handle)) !== false) {
$userData = [
'username' => $row[0],
'firstname' => $row[1],
'lastname' => $row[2],
'email' => $row[3],
'password' => $row[4],
'is_active' => (int)$row[6]
];
$roleName = $row[5];
try {
$user = $userCreator->createUser($userData, $roleName);
echo "Created user: {$userData['username']}\n";
} catch (\Exception $e) {
echo "Error creating {$userData['username']}: {$e->getMessage()}\n";
}
}
fclose($handle);
Run Import:
php import_users.php
Managing Existing Users
Updating User Information
Via Admin Panel:
System > Permissions > All Users > [Select User] > Edit
Update Fields:
- First/Last Name
- Password (optional)
- Interface Locale
- Account Status (Active/Inactive)
- Role Assignment
Via CLI:
# Unlock user account
php bin/magento admin:user:unlock username
# Reset password
php bin/magento admin:user:reset-password username
Programmatically:
$user = $userFactory->create()->loadByUsername('john.smith');
$user->setFirstname('John');
$user->setLastname('Smith-Johnson');
$user->setEmail('john.new@company.com');
$user->save();
Password Reset
Force Password Reset:
Admin Panel:
- Edit user
- Check "Send Password Reset Email"
- Save user
- User receives email with reset link
CLI:
php bin/magento admin:user:reset-password john.smith
Programmatic:
$user = $userFactory->create()->loadByUsername('john.smith');
$user->sendPasswordResetNotificationEmail();
Activating/Deactivating Users
Temporary Deactivation:
Admin Panel:
- Edit user
- Set "This account is" to "Inactive"
- Save user
CLI (Custom Command):
# Create custom command for deactivation
php bin/magento user:deactivate john.smith
Programmatic:
$user = $userFactory->create()->loadByUsername('john.smith');
$user->setIsActive(0); // 0 = Inactive, 1 = Active
$user->save();
Removing Users
Secure Offboarding Process
Step 1: Disable Account
Immediately suspend access:
System > Permissions > All Users > [Select User] > Edit
Set "This account is" to "Inactive"
Save User
Or via CLI:
# Disable user
php bin/magento admin:user:deactivate username
Step 2: Transfer Ownership
Before deletion, transfer:
- Content ownership (CMS pages, blocks)
- Scheduled tasks
- Custom reports
- Integration tokens
- API credentials
Database Query to Find Owned Content:
-- Find CMS pages created by user
SELECT page_id, title, creation_time
FROM cms_page
WHERE author_id = (SELECT user_id FROM admin_user WHERE username = 'john.smith');
-- Find scheduled operations
SELECT schedule_id, job_code, created_at
FROM cron_schedule
WHERE user_id = (SELECT user_id FROM admin_user WHERE username = 'john.smith');
Reassign Content:
// Reassign CMS pages
$newOwner = $userFactory->create()->loadByUsername('new.owner');
$oldOwner = $userFactory->create()->loadByUsername('john.smith');
$pageCollection = $pageCollectionFactory->create();
$pageCollection->addFieldToFilter('author_id', $oldOwner->getId());
foreach ($pageCollection as $page) {
$page->setAuthorId($newOwner->getId());
$page->save();
}
Step 3: Revoke API Access
Remove integrations:
System > Extensions > Integrations
- Find integrations owned by user
- Delete or reassign
Revoke OAuth tokens:
-- Revoke user's OAuth tokens
DELETE FROM oauth_token
WHERE admin_id = (SELECT user_id FROM admin_user WHERE username = 'john.smith');
-- Revoke integration tokens
DELETE FROM integration
WHERE email = 'john.smith@company.com';
Step 4: Export Audit Trail
Before deletion, export:
System > Action Logs > Report
- Filter by user
- Export to CSV
- Save for compliance
SQL Export:
-- Export user's action log
SELECT *
FROM logging_event
WHERE user_id = (SELECT user_id FROM admin_user WHERE username = 'john.smith')
ORDER BY time DESC
INTO OUTFILE '/tmp/user_audit_john_smith.csv';
Step 5: Delete User Account
Via Admin Panel:
System > Permissions > All Users > [Select User] > Delete
Via Database (Not Recommended):
-- Only use if admin panel is unavailable
DELETE FROM admin_user WHERE username = 'john.smith';
DELETE FROM authorization_role WHERE user_id = [user_id];
Via CLI (Custom Command):
php bin/magento admin:user:delete john.smith
Step 6: Document Offboarding
Create offboarding ticket:
- User Name
- Termination Date
- Reason for Removal
- Content Reassigned
- API Tokens Revoked
- Audit Trail Exported
- Approver Name
Bulk User Management
Deactivate Multiple Users
SQL Script:
-- Deactivate all users except admin
UPDATE admin_user
SET is_active = 0
WHERE username != 'admin'
AND user_id > 1;
Programmatic:
$userCollection = $userCollectionFactory->create();
$userCollection->addFieldToFilter('username', ['neq' => 'admin']);
foreach ($userCollection as $user) {
$user->setIsActive(0);
$user->save();
echo "Deactivated: " . $user->getUsername() . "\n";
}
Delete Inactive Users
Find inactive users (30+ days):
SELECT username, email, logdate, is_active
FROM admin_user
WHERE is_active = 0
AND logdate < DATE_SUB(NOW(), INTERVAL 30 DAY);
Delete after verification:
$userCollection = $userCollectionFactory->create();
$userCollection->addFieldToFilter('is_active', 0);
$userCollection->addFieldToFilter(
'logdate',
['lt' => date('Y-m-d H:i:s', strtotime('-30 days'))]
);
foreach ($userCollection as $user) {
echo "Deleting: " . $user->getUsername() . "\n";
$user->delete();
}
Access Audits
Regular Access Reviews
Quarterly Review Process:
Generate User Report:
php bin/magento admin:user:list > users_$(date +%Y%m%d).txtCheck Last Login:
SELECT username, email, logdate, lognum FROM admin_user ORDER BY logdate DESC;Identify Stale Accounts:
-- Users who haven't logged in for 90 days SELECT username, email, logdate FROM admin_user WHERE logdate < DATE_SUB(NOW(), INTERVAL 90 DAY) AND is_active = 1 ORDER BY logdate ASC;Review Roles:
SELECT u.username, u.email, r.role_name FROM admin_user u LEFT JOIN authorization_role r ON u.user_id = r.user_id WHERE u.is_active = 1 ORDER BY r.role_name, u.username;Document Review:
- Export results
- Manager approval for each active user
- Remove unnecessary access
- Update documentation
Compliance Reporting
Generate compliance report:
<?php
class AccessAuditReport
{
public function generateReport()
{
$report = [
'generated_at' => date('Y-m-d H:i:s'),
'total_users' => 0,
'active_users' => 0,
'inactive_users' => 0,
'roles' => [],
'users_by_role' => []
];
$userCollection = $this->userCollectionFactory->create();
$report['total_users'] = $userCollection->getSize();
$activeUsers = clone $userCollection;
$activeUsers->addFieldToFilter('is_active', 1);
$report['active_users'] = $activeUsers->getSize();
$inactiveUsers = clone $userCollection;
$inactiveUsers->addFieldToFilter('is_active', 0);
$report['inactive_users'] = $inactiveUsers->getSize();
// Group by role
foreach ($activeUsers as $user) {
$role = $user->getRole();
$roleName = $role ? $role->getRoleName() : 'No Role';
if (!isset($report['users_by_role'][$roleName])) {
$report['users_by_role'][$roleName] = [];
}
$report['users_by_role'][$roleName][] = [
'username' => $user->getUsername(),
'email' => $user->getEmail(),
'last_login' => $user->getLogdate()
];
}
return $report;
}
}
Security Best Practices
Password Requirements
Enforce strong passwords:
Stores > Configuration > Advanced > Admin > Security
- Minimum Length: 12+ characters
- Required Characters: Numbers, symbols, upper/lower case
- Password Lifetime: 90 days
- Lock after failures: 5 attempts
Two-Factor Authentication (2FA)
Mandatory 2FA for new users:
# Enable 2FA for user
php bin/magento security:tfa:google:set-secret john.smith [SECRET_KEY]
Session Management
Configure sessions:
Stores > Configuration > Advanced > Admin > Security
- Session Lifetime: 900 seconds (15 min)
- Validate HTTP_USER_AGENT: Yes
- Validate REMOTE_ADDR: Yes
Access Logging
Enable action logs:
Stores > Configuration > Advanced > Admin > Admin Actions Log Archiving
- Enable Archiving: Yes
- Log Lifetime: 365 days
Troubleshooting
Cannot Create User
Error: "User name already exists"
Solution:
-- Check for existing username
SELECT * FROM admin_user WHERE username = 'desired_username';
-- If exists, choose different username or delete old account
User Cannot Login
Possible Causes:
Account Inactive:
UPDATE admin_user SET is_active = 1 WHERE username = 'john.smith';Account Locked:
php bin/magento admin:user:unlock john.smithWrong Password:
php bin/magento admin:user:reset-password john.smith
Permissions Not Working After Creation
Solution:
# Clear ACL cache
php bin/magento cache:clean config
php bin/magento cache:flush
Automation Examples
Automated User Provisioning
SSO Integration (Example with SAML):
public function createUserFromSaml($samlData)
{
$username = $samlData['username'];
$email = $samlData['email'];
// Check if user exists
$user = $this->userFactory->create()->loadByUsername($username);
if (!$user->getId()) {
// Create new user
$user = $this->userCreator->createUser([
'username' => $username,
'firstname' => $samlData['firstname'],
'lastname' => $samlData['lastname'],
'email' => $email,
'password' => bin2hex(random_bytes(16)), // Random password
'is_active' => 1
], $samlData['role']);
}
return $user;
}
Scheduled User Cleanup
Cron Job: Remove inactive users after 90 days
<!-- etc/crontab.xml -->
<config>
<group id="default">
<job name="cleanup_inactive_users" instance="Vendor\Module\Cron\CleanupUsers" method="execute">
<schedule>0 2 * * 0</schedule> <!-- Weekly on Sunday at 2 AM -->
</job>
</group>
</config>
<?php
namespace Vendor\Module\Cron;
class CleanupUsers
{
public function execute()
{
$collection = $this->userCollectionFactory->create();
$collection->addFieldToFilter('is_active', 0);
$collection->addFieldToFilter(
'logdate',
['lt' => date('Y-m-d H:i:s', strtotime('-90 days'))]
);
foreach ($collection as $user) {
$this->logger->info('Auto-deleting inactive user: ' . $user->getUsername());
$user->delete();
}
}
}
Next Steps
- Roles and Permissions - Configure custom roles
- User Management Overview - Security best practices
Additional Resources
- Magento User Management - Official guide
- CLI Commands - Command reference
- Security Best Practices - Security guidelines