Colleagues frequently leave for new roles, but leaving all of these unused-users in projects can be a security risk, so they need to be removed efficiently. Manually editing roles and permissions on multiple projects is the opposite of efficient, so using a script to do this for you can save lots of time and effort.
I recently wrote a couple of scripts for removing unneeded users from websites hosted on Heroku and using WordPress.
This script utilises the WordPress CLI which is included in the root of our WordPress projects. The CLI command for removing a WordPress user is actually very simple, and will be the main command used in the script:
wp user delete USER_EMAIL
To reassign content that was attached to this user, you can append the --reassign=USER_ID
flag to this command. This is important because content which is not reassigned is deleted.
The script needs to check if the user we are removing actually exists, and if it does, reassign the content automatically to another user on the same domain. For example, the user being deleted is test@example.com
so we want to reassign content to another user with an @example.com
email address.
As it runs, a message will be returned to show whether a user was deleted, or if they didn’t exist on a specific project.
EMAIL=$1
USERS="$(wp user list --fields=ID,user_email | grep @example)"
Using the --fields
flag we can get the ID and email address of the current WordPress users. We pipe this result into a grep that returns only the users with @example
in the address.
In my case, I wasn’t more specific, because some users have .co.uk
and others have .com
.
$USERS
array.if grep -qs "${EMAIL}" <<< "$USERS"; then
...
fi
Grep checks if the target exists in the array. -qs
will quiet the output and suppress errors - this is just acting as a conditional check so we dont need any output.
USER="$(echo "$USERS" | sed -n 1p)"
USER_ID=$(echo "$USER" | awk '{print $1}')
USER_EMAIL=$(echo "$USER" | awk '{print $2}')
sed -n 1p
will extract the first line (get our first matching user). The -n
option will supress the unmatched text, and the p
means “print the matching lines”. The result would look like 1 user@example.com
awk
gets the content from the specified column, so USER_ID
would be 1, and USER_EMAIL
would be user@example.com
.
if grep -qs "${EMAIL}" <<< "$USER_EMAIL"; then
USER_ID="$(echo "$USERS" | sed -n 2p | awk '{print $1;}')"
fi
These commands work in the same way as the previous checks, except this time sed -n 2p
returns the second line, rather than the first.
wp user delete "${EMAIL}" --reassign="${USER_ID}"
#!/bin/bash
# Email address of the user to delete. Save it to a variable for checks.
EMAIL=$1
USERS="$(wp user list --fields=ID,user_email | grep @example)"
# Check if the specified email address is a user on the site
if grep -qs "${EMAIL}" <<< "$USERS"; then
# Find the first example user. Set variables for the ID and Email address
USER="$(echo "$USERS" | sed -n 1p)"
USER_ID=$(echo "$USER" | awk '{print $1}')
USER_EMAIL=$(echo "$USER" | awk '{print $2}')
# If the email input matches the queried user, reassign the USER_ID variable to the next valid user
if grep -qs "${EMAIL}" <<< "$USER_EMAIL"; then
USER_ID="$(echo "$USERS" | sed -n 2p | awk '{print $1;}')"
fi
# Delete the user and reassign
wp user delete "${EMAIL}" --reassign="${USER_ID}"
printf "User: %s deleted. Content reassigned to %s" "${EMAIL}" "${USER_EMAIL}"
else
printf "Selected user does not exist"
fi
Finish off with some user feedback, a success message if the user was deleted, or a message if the user doesn’t exist.
This script was designed to be run across multiple WordPress installations on different Heroku servers and we have 70+ projects which would require this kind of maintenance.
To run this script on Heroku, the Heroku CLI comes in handy. The are a couple of prerequisites before running it.
apps-table
plugin with heroku plugins:install apps-table
. This allows you to interact with all apps in your account without having to specify the name of an app. Read the documentation for available commands.The command to run the script across all of your Heroku apps looks like this:
heroku apps:table \
--filter="App Name=-example" \
--columns="app name" \
--no-header | \
xargs -n 1 heroku run "bash <(curl -s https://raw.githubusercontent.com/username/script-name.sh) user@example.com" -a
This command will loop through all of the heroku apps whose name contains -live
.
--filter
flag is flexible. You can speficy an exact app name, or you can use partials such as -example
to get all apps named *-example
. If you append your app names with something like -example
you can easily group them--columns
only shows provided columns--no-header
hides the heroku table header from outputxargs
then passes this list of apps through to the next commandheroku run "bash <(curl -s https://raw.githubusercontent.com/username/script-name.sh) user@example.com"
Each Heroku app will run the script from its remote location, in this case a github repo-a
flag specifies that we want to run the script on every app in the list.This script allows you to loop through all of your Heroku hosted WordPress sites and delete a specified user from all of them quickly.
In the next post we’ll write a script to remove users from multiple Heroku teams.