Bulk updating Okta users managers
At work, our identity provider is our single source of truth.
Recently we had a new manager start leading an existing team. Within Okta, every user has the manager field filled out with the manager’s email address. This is then propagated to other apps within our ecosystem using SCIM provisioning.
The manual process to update a user’s manager is:
- Search for the user in the Okta web interface
- Click “Profile”
- Click “Edit”
- Scroll down to the manager field
- Enter the new manager’s email address
- Click “Save”
While that’s fine to do once, when trying to update a whole team’s worth of people it’s not so fun.
One thing that really sparks joy for me is a task that I don’t have to do. If I can automate a process, then you’d better believe that’s what I’m doing. So I wrote a short Ruby script to easily update multiple users at once.
require 'oktakit'
@dry_run ||= true
@production ||= false
users_to_update = {
'han.solo@example.com': 'mon.mothma@example.com',
'luke.skywalker@example.com': 'mon.mothma@example.com',
}
def client
options =
if @production
{
token: `op read "op://Employee/Okta API token/credential"`.strip,
organization: 'company' # for company.okta.com
}
else
{
token: `op read "op://Employee/Okta Test API token/credential"`.strip,
organization: 'company-test'
}
end
@client ||= Oktakit.new(**options)
end
#######################################
# No need to edit anything below here #
#######################################
def update_manager_email(user_email, new_manager_email)
user = client.get_user(user_email)
unless user.last == 200
@errors.push "Error (#{user.last}) fetching user #{user_email}"
return
end
current_manager_email = user.first[:profile][:manager]
puts "#{user_email}, #{current_manager_email}, #{new_manager_email}"
return if @dry_run
return if current_manager_email == new_manager_email
client.update_user(user_email, partial: true, profile: { manager: new_manager_email })
end
if @dry_run
puts 'DRY RUN. No changes will be made to Okta.'
puts '-----------------------------------------'
puts
end
@errors = []
puts 'user, old_manager_email, new_manager_email'
users_to_update.each do |user, new_manager_email|
update_manager_email(user, new_manager_email)
end
return if @errors.empty?
puts
puts '-----------------------------------------'
puts 'ERRORS'
puts @errors.join("\n")
The script uses the Oktakit gem made by the people at Shopify.
The users to update are defined within the users_to_update hash with the key being the user who is going to be updated and the value being the new manager’s email address. In the example script, Mon Mothma is the new manager of Han Solo and Luke Skywalker.
There are some environment-specific parts, such as how the access token is passed in. We use 1Password so the script is pulling the token from there.
When the script is run the output will show what will be changed if the @dry_run variable is true or what was changed if @dry_run is false.
Any errors, such as not being able to find a user with a given email address, are shown below all of the other output.
$ ruby update_bulk_managers.rb
DRY RUN. No changes will be made to Okta.
-----------------------------------------
user, old_manager_email, new_manager_email
han.solo@example.com, han.solo@example.com, mon.mothma@example.com
-----------------------------------------
ERRORS
Error (404) fetching user luke.skywalker@example.com
This isn’t a particularly complicated script. It could definitely be done manually and probably more efficiently too. However it is the kind of single-purpose script that I like to create to simplify my life and reduce the chance of human error.
Going further
Taking this further and making a more comprehensive script, maybe you want to add the ability to define the values in a CSV file and update more than just the manager.
Here’s one I prepared earlier.
okta-update-attributes.rb is similar to what is described above, with a few extra safeguards.
It allows you to specify a target group and any user records that aren’t within the group are ignored. This might be useful if you wanted to have a pilot group, for example.
As always, don’t just trust random code you find on the internet. Inspect the code yourself first, then run it in a sandbox/test environment, and then run it in production.