cancel
Showing results for 
Search instead for 
Did you mean: 

Bulk Set Local User Account Names & System Bind

jworkman
JumpCloud Employee
JumpCloud Employee

When binding JumpCloud users to devices, it could be the case that the local user account names on devices differ slightly from JumpCloud usernames within the console. Given the case below, John Smith’s JumpCloud user account username does not match its intended user account `jsmith` on their laptop.

Screen Shot 2023-03-24 at 8.05.26 AM.png

In this scenario, the `jsmith` account can still be taken over by setting John’s “local user account” before binding. Instructions to set a user’s “local user account” can be found in the KB: Taking over an existing user account with JumpCloud. As long as John Smith’s “local user account” is set to `jsmith`, John’s account will take over the `jsmith` account when the association between system and user is made in the JumpCloud console.

Screen Shot 2023-03-22 at 2.19.54 PM.png

Setting this value for multiple users in an organization could become arduous. Thankfully, the local user accounts for devices can be queried using the JumpCloud API to automate identifying JumpCloud user’s intended “local user accounts”. The intent of this post is to help JumpCloud administrators bind their user accounts to systems with differing local usernames. The scripts provided below are intended to help, not serve as an official guide.

Before we can set each user account's “local user account” name, we must first query devices to get a list of local accounts on each device. Assuming we can match a local account to a JumpCloud user account, we can then automate the binding of device/ user associations with a CSV and PowerShell script.

I’ll be breaking the solution down into three steps:

  1. (scripted) Gather information about the JumpCloud managed systems and create a CSV.
    1. Only systems without JumpCloud bound user accounts are returned.
    2. Local user accounts on systems are returned.
    3. Potential JumpCloud User matches are populated (based on last names and found local users).
  2. (manually) Identify the intended JumpCloud userID and set their “local user account” & “userID” values. Update the CSV manually.
  3. (scripted) Update each JumpCloud user with their “local user account” and bind the user to the system.

This post assumes that:

Step 1: Gather Information

Depending on the manner in which the JumpCloud agent was installed on devices, useful information about end users could be stored within the JumpCloud API. If devices were enrolled in JumpCloud with the user portal installation method, the userID of the user who logged into the JumpCloud console and installed the agent is stored within each system record. Within our API this field is stored under the `provisionMetadata.Provisioner.ProvisionerID` system record. 

This `provisionerID` field is helpful to identify the user who may have installed the JumpCloud agent. If a system doesn’t have this field, we can still identify users by last names. The script posted below will perform a search of each JumpCloud user and attempt to match local users on systems to JumpCloud users with last names.

I.e. If a device was found to have a local user account `hmurphy` the script would search JumpCloud user’s last names and return matches where the last name exists as a substring of the user account. Users like “Henry Murphy, Bob Mur” would be returned in this example

The aim of the discovery script is to return all systems without user bindings and the following data:

  • ProvisionerID
  • ProvisionerUsername
    • If the provisionerID used to install the agent no longer exists as a JumpCloud user, the provisionerUsername will display “NA”
  • Local users accounts on each system
  • SystemID
  • System Hostname
  • System Display Name
  • Potential JumpCloud Users whose last name matched a local user account on the system

The discovery script will return systems without an existing JumpCloud user association. In cases where an organization admin JumpCloud user is already bound to devices, the `$includeUserID` variable can be populated with that user's ID to include those systems in the `discovery.csv` results.

I.e. If all systems had an admin user bound but were not associated with an end-user, the ID of the admin user could be set as the `$includeUserID` variable to include those systems in the returned results.

The script to gather all this information is posted below. Save the code to a `discovery.ps1` file and invoke locally ex.

 

Connect-JCOnline yourAPIKey
./discovery.ps1

 

Discovery.ps1 Script:

 

# Discovery Script - get information about your environment
################################################################################
# TheID of the user you want to include in the search results, if left blank
# only systems w/o associations are returned
$IncludeUserID = ""
################################################################################
# Do not edit below:
################################################################################
# Get All Systems:
$systems = Get-JCSystem
# Get All Users:
$users = Get-JCUser -returnProperties firstname, lastname, email, username
# initialize an empty list
$list = @()
# counter variable for progress display
$counter = 0
# Get Systems w/o bound Users
foreach ($system in $systems) {
    # display progress
    $counter++
    Write-Progress -Activity 'Querying JumpCloud Systems' -CurrentOperation $($system.displayName) -PercentComplete (($counter / $systems.count) * 100) -Status "Processing $($system.displayName)"
    # get associations per system
    $associations = Get-JcSdkSystemAssociation -SystemId $($system.Id) -Targets user
    # if there are no system to user associations continue...
    if (!$associations -or $associations.ToId -eq $IncludeUserID) {
        # Clear Variables from previous loop
        if ($usernameString) {
            Clear-Variable -Name usernameString
        }
        if ($potentialUserString) {
            Clear-Variable -Name potentialUserString
        }
        if ($foundUser) {
            Clear-Variable -Name foundUser
        }
        if ($usersOnSystem) {
            Clear-Variable -Name usersOnSystem
        }
        if ($logError) {
            Clear-Variable -Name logError
        }
        # Gather Local User Data with System Insights
        if ($system.OS -eq "Mac OS X" ) {
            # Case for Mac
            $usersOnSystem = Get-JCSystemInsights -systemId $system.Id -Table User | where-object { $_.Directory -match "/Users" -And $_.Username }
        } elseif ($system.OS -eq "Windows") {
            # Case for Windows
            $usersOnSystem = Get-JCSystemInsights -systemId $system.Id -Table User | where-object { $_.Directory -match "\\Users\\" -And $_.Username }
        } else {
            # Case for Linux (adjust for home directory if not '/home/')
            $usersOnSystem = Get-JCSystemInsights -systemId $system.Id -Table User | where-object { $_.Directory -match "/home/" -And $_.Username }
        }
        # Build Username String
        foreach ($sysuser in $usersOnSystem) {
            $usernameString += "($($sysuser.Username));"
        }
        # trim last character
        if ($usernameString) {
            $usernameString = $usernameString.trimend(";")
        }
        # If ProvisionerID exists, get username
        if ($system.provisionMetadata.provisioner.provisionerId) {
            $foundUser = $users | where-object { $_._id -eq $($system.provisionMetadata.provisioner.provisionerId) }
            if (-not $foundUser) {
                # write-host "getting user from this key: $($system.provisionMetadata.provisioner.provisionerId)"
                $foundUser = [PSCustomObject]@{
                    Username = "NA"
                }
            }
        }
        # Build potentialUsername String
        foreach ($user in $users) {
            if ($usernameString -match $user.lastname) {
                $potentialUserString += "($($user.firstname) $($user.lastname)::$($user.username)::$($user._id));"
            }
        }
        # trim extra characters
        if ($potentialUserString) {
            $potentialUserString = $potentialUserString.trimend(";")
        }
        # populate the rows
        $list += [PSCustomObject]@{
            localAccount        = "";
            userID              = "";
            systemID            = $system.id;
            systemOS            = $system.OS;
            systemHostname      = $system.hostname;
            systemDisplayname   = $system.displayName;
            provisionerID       = $system.provisionMetadata.provisioner.provisionerId;
            provisionerUsername = $foundUser.Username
            potentialUsers      = $potentialUserString;
            localUsersOnSystem  = $usernameString;
        }
    }
}
# Write out the file discovery.csv to current working directory
$list | ConvertTo-Csv | Out-File discovery.csv

 

This script will save a file “discovery.csv” in the current working directory. In my test organization, the resulting CSV looks like this:

localAccount

userID

systemID

systemOS

systemHostname

systemDisplayname

provisionerID

provisionerUsername

potentialUsers

localUsersOnSystem

   

5e90f19ecc99d210ecb3406c

Mac OS X

mdm-system

MDM System

6148cd5e3ba7425ff0cc81fa

henry.murphy

(Bind User::bind.user::5d655de20913536f94196a7b);(Default Admin::defaultadmin::5d67fd481da3c52aa1faa883);(TOTP USER::totp.user::5dd2b828d2e70c732546201c);(Peter Parker::pparker::6094427aa183845f21bdde23);(Henry Murphy::henry.murphy::6148cd5e3ba7425ff0cc81fa);(Walter Parker::walter.parker::6148cd5f3ba7425ff0cc820b);(Isabella Parker::isabella.parker::6148cd6af607f833b1a2e1ee);(Eddy Johnson::eddy.johnson::6148cd7c3ba7425ff0cc82df);(Darcy Parker::darcy.parker::6148cd7ef607f833b1a2e21b);(Carl Murphy::carl.murphy::6148cd95f607f833b1a2e247);(Aida Parker::aida.parker::6148cd9bab01605ff4b812a4);(Julian Murphy::julian.murphy::6148cd9f64d37a6f0d4ef6b8)

(hmurphy);(admin);(it.support);(ian.johnson);(pparker);(totpuser)

   

6010bf24471b3b196e81e888

Mac OS X

defaultadmins-MacBook-Air.local

MacBook Air M1

5d67fd481da3c52aa1faa883

defaultadmin

(Default Admin::defaultadmin::5d67fd481da3c52aa1faa883)

(defaultadmin);(justin)

   

609e98bd32a14f0f822e598d

Mac OS X

bigsur

MacBook Pro

61081043087f84453191fce7

bob.fay

(Default Admin::defaultadmin::5d67fd481da3c52aa1faa883);(Bob Fay::bob.fay::61081043087f84453191fce7)

(bob.fay);(defaultadmin)

   

60afdc5821a64317675f9f87

Mac OS X

Farmer-MacBook-Air

Harrisons-MacBook-Air

6148cdab59370e3099c06f40

jack.harrison

(Joe Workman::joe.workman::5e9612442f4ad8257548b59a);(Antony Harrison::antony.harrison::6148cd6e59370e3099c06eb5);(Valeria Harris::valeria.harris::6148cd9a8cb3b635baabefea);(Jack Harrison::jack.harrison::6148cdab59370e3099c06f40)

(joe.workman);(Harrison)

   

60d347fab6a44816c875725c

Mac OS X

mdm-testing

mdm-testing

6148cd7b3ba7425ff0cc82dd

naomi.ellis

(Default Admin::defaultadmin::5d67fd481da3c52aa1faa883)

(defaultadmin);(naomie)

   

610b255f8973970707a1e439

Mac OS X

10.15-Mac

10.15-Mac

6148cd7c3ba7425ff0cc82df

eddy.johnson

(Default Admin::defaultadmin::5d67fd481da3c52aa1faa883);(Eddy Johnson::eddy.johnson::6148cd7c3ba7425ff0cc82df)

(defaultadmin);(eddyjohnson)

   

611c07f733ca967a86ee0f5d

Mac OS X

joeworkman-MacBook-Air

joeworkman-MacBook-Air

5e9612442f4ad8257548b59a

joe.workman

(Default Admin::defaultadmin::5d67fd481da3c52aa1faa883);(Joe Workman::joe.workman::5e9612442f4ad8257548b59a)

(defaultadmin);(joe.workman)

   

6123b79fdc842b15a96e5ade

Windows

NoamM-ISR-LEG-Win

NoamM-ISR-LEG-Win

6148cd959d38866f0814e88c

tiana.harper

(Default Admin::defaultadmin::5d67fd481da3c52aa1faa883);(Brianna Harper::brianna.harper::6148cd7cab01605ff4b81293);(Robert Harper::rob.harper::6148cd7dc0abe8334b6c7c84);(Tiana Harper::tia.harper::6148cd959d38866f0814e88c);(Dominik Rogers::dominik.rogers::6148cd9664d37a6f0d4ef690);(Leonardo Harper::leo.harper::6148cda69d38866f0814e893);(Edgar Rogers::edgar.rogers::6148cdac8cb3b635baabf01a)

(dom.rogers);(tharper);(general);(defaultadmin)

   

61240b820ec56816cea8f4e3

Mac OS X

defaultadmins-MacBook

Carols-MacBook

6148cdae3ba7425ff0cc8419

thomas.carroll

(Default Admin::defaultadmin::5d67fd481da3c52aa1faa883);(Alan Carroll::alan.carroll::6148cd7564d37a6f0d4ef642);(Sarah Carroll::sarah.carroll::6148cda53ba7425ff0cc83e5);(Thomas Carroll::thomas.carroll::6148cdae3ba7425ff0cc8419)

(Carroll);(defaultadmin)

   

61425a6a22902a1745dd7707

Windows

JOEWORKMAN481A

JOEWORKMAN481A

61081043087f84453191fce7

bob.fay

(Default Admin::5d67fd481da3c52aa1faa883);(Joe Workman::joe.workman::5e9612442f4ad8257548b59a);(Bob Fay::bob.fay::61081043087f84453191fce7)

(bob.fay);(jworkman);(Admin)

Step 2: Manually match the intended user of the system

After querying data and searching for possible matches the generated `discovery.csv` should contain enough information to determine the user that should be associated with the system.

The goal in this step is to set the `userID` column for the intended JumpCloud userID who should be bound to the device. If the local username of the intended JumpCloud user differs from the username in the JumpCloud console, we’ll set that value in the `localAccount` column.

For each of the systems in the generated CSV, manually perform the following steps:

  • Look at the `provisionerUsername` and whether or not a similar username appears in the `localUsersOnSystem` column.
    • If there’s a direct match between a local user’s username and the provisionerUsername, copy the `provisionerID` to the `userId` column.
    • Else, we can set the user’s “local user account” by copying the desired username to the `localAccount` column and update the `userID` field to either match the `provisionerID` or the ID found in the `potentialUsers` column.
  • If no `provisionerUsername` exists, search the `potentialUsers` column for the intended user of the system.
    • If the intended user of the system exists in the ‘potentialUsers’ column, copy the user’s ID to the `userID` column and their desired username to the `localAccount` column (assuming it differs from the user’s actual username).

Match a user with a provisionerID

localAccount

userID

systemID

systemOS

systemHostname

systemDisplayname

provisionerID

provisionerUsername

potentialUsers

localUsersOnSystem

   

5e90f19ecc99d210ecb3406c

Mac OS X

mdm-system

MDM System

6148cd5e3ba7425ff0cc81fa

henry.murphy

(Bind User::bind.user::5d655de20913536f94196a7b);(Default Admin::defaultadmin::5d67fd481da3c52aa1faa883);(TOTP USER::totp.user::5dd2b828d2e70c732546201c);(Peter Parker::pparker::6094427aa183845f21bdde23);(Henry Murphy::henry.murphy::6148cd5e3ba7425ff0cc81fa);(Walter Parker::walter.parker::6148cd5f3ba7425ff0cc820b);(Isabella Parker::isabella.parker::6148cd6af607f833b1a2e1ee);(Eddy Johnson::eddy.johnson::6148cd7c3ba7425ff0cc82df);(Darcy Parker::darcy.parker::6148cd7ef607f833b1a2e21b);(Carl Murphy::carl.murphy::6148cd95f607f833b1a2e247);(Aida Parker::aida.parker::6148cd9bab01605ff4b812a4);(Julian Murphy::julian.murphy::6148cd9f64d37a6f0d4ef6b8)

(hmurphy);(admin);(it.support);(ian.johnson);(pparker);(totpuser)

The `provisionerUsername` on this system is `henry.murphy` and the `hmurphy` username is listed as a local system account. There’s substantial evidence to support that `hmurphy's` account is the primary user of the device and the account we want to take over.

To update the row, `hmurphy's` username is added to the `localAccount` field, `henry.murphy's` `provisionerID` is added to the `userID` field like the example below:

localAccount

userID

systemID

systemOS

systemHostname

systemDisplayname

provisionerID

provisionerUsername

potentialUsers

localUsersOnSystem

hmurphy

6148cd5e3ba7425ff0cc81fa

5e90f19ecc99d210ecb3406c

Mac OS X

mdm-system

MDM System

6148cd5e3ba7425ff0cc81fa

henry.murphy

(Bind User::bind.user::5d655de20913536f94196a7b);(Default Admin::defaultadmin::5d67fd481da3c52aa1faa883);(TOTP USER::totp.user::5dd2b828d2e70c732546201c);(Peter Parker::pparker::6094427aa183845f21bdde23);(Henry Murphy::henry.murphy::6148cd5e3ba7425ff0cc81fa);(Walter Parker::walter.parker::6148cd5f3ba7425ff0cc820b);(Isabella Parker::isabella.parker::6148cd6af607f833b1a2e1ee);(Eddy Johnson::eddy.johnson::6148cd7c3ba7425ff0cc82df);(Darcy Parker::darcy.parker::6148cd7ef607f833b1a2e21b);(Carl Murphy::carl.murphy::6148cd95f607f833b1a2e247);(Aida Parker::aida.parker::6148cd9bab01605ff4b812a4);(Julian Murphy::julian.murphy::6148cd9f64d37a6f0d4ef6b8)

(Hmurphy);(admin);(it.support);(ian.johnson);(pparker);(totpuser)

When the following script is run, the ID (in this case user:`henry.murphy`) from the `userID` column will have their “local user account” name set to `hmurphy` (from the value in `localAccount` column). Their user account will be associated with the ID of the system from the `systemID` column. 

Match a user without a provisionerID

The second row looks a bit different. The user `defaultadmin` installed the JumpCloud agent from the user portal. However, the `defaultadmin` account is an IT owned account. The only other local user on the system is an account named `justin` which happens to be the primary user of the device.

localAccount

userID

systemID

systemOS

systemHostname

systemDisplayname

provisionerID

provisionerUsername

potentialUsers

localUsersOnSystem

   

6010bf24471b3b196e81e888

Mac OS X

defaultadmins-MacBook-Air.local

MacBook Air M1

5d67fd481da3c52aa1faa883

defaultadmin

(Default Admin::defaultadmin::5d67fd481da3c52aa1faa883)

(defaultadmin);(justin)

In this case, additional work would have to be completed to identify the JumpCloud user intended for this system. The two user accounts in my JumpCloud org with the first name “Justin” would then have to be contacted or identified as the primary owner of the device. 

jworkman_0-1679667323579.png

It’s possible to encounter situations where the local users on the system do not match any potential users from the previous script. (remember last names are used to identify users in the `potentialUsers` column. In this case, the system hostname or other contextual clues would need to be used to identify the primary owner.

Step 3: Set `localAccount` names and bind to systems

After manually editing the csv in the previous step, the last script in this series can be run to update all users specified from the `userID` column of the `discovery.csv` file. 

The script detailed below will iterate through each line and if an ID was set in the `UserID` field, perform a set of actions to:

  • Set the “local user account” field for the user if a value exists in the `localAccount` column
  • Bind the userID to the systemID

This script has an optional parameter `sudoBind` which is used to set the permissions of the user to system association. To bind users as admins, set the `sudoBind` parameter to `$true`, else the user bindings will be set without admin permissions (default behavior). 

The script to update all this information is posted below. Save the code to a `SetUserAndBind.ps1` file and invoke locally ex.

 

 

Connect-JCOnline yourAPIKey
./SetUserAndBind.ps1

 

 

# Set local user and bind script:

 

# Set Local User and Bind script
################################################################################
# Bind as admin settings (default false):
# set to true if users should be bound to devices as admin
$sudoBind = $false
# Get list of users from csv in previous steps
$list = Get-Content discovery.csv | ConvertFrom-Csv
################################################################################
# Do not edit below
################################################################################

# define user bind settings
$attributes = [JumpCloud.SDK.V2.Models.GraphOperationSystemAttributes]@{ 'sudo' = @{'enabled' = $sudoBind; 'withoutPassword' = $false } }
# for each item in the list
foreach ($item in $list) {
    # Add additional checks before binding
    # If the UserId, and SystemID field is populated, continue...
    if ($item.userid -and $item.systemID) {
        # If a local account was specified, update this user's localAccount name
        if ($item.LocalAccount) {
            # Build the request
            "Adding LocalAccount Username: $($item.localAccount) on userID: $($item.UserId)"
            $headers = @{
                Accept      = "application/json"
                'x-api-key' = $ENV:JCApiKey
                ContentType = 'application/json'
            }
            # Add the LocalAccount to the UserID
            $content = Invoke-WebRequest -Method PUT -Uri "https://console.jumpcloud.com/api/systemusers/$($item.userID)" -Headers $headers -Form @{systemUsername = "$($item.localAccount)" }
            # Finally Bind each user to their system if the previous IWR returns 200 response
            if ($content.StatusCode -eq '200') {
                "Binding UserID: $($item.UserId) to SystemID: $($item.SystemId) "
                Set-JcSdkUserAssociation -id $item.SystemId -userid $item.UserId -op 'add' -type 'system' -Attributes $attributes.AdditionalProperties
            }
        } else {
            # If no localAccount name needs to be set, just bind the userID to the SystemID
            "Binding UserID: $($item.UserId) to SystemID: $($item.SystemId) "
            Set-JcSdkUserAssociation -id $item.SystemId -userid $item.UserId -op 'add' -type 'system' -Attributes $attributes.AdditionalProperties
        }
    }
}

 

Given the CSV file above, running the script should produce the result output below:

 

Adding LocalAccount Username: hmurphy on userID: 6148cd5e3ba7425ff0cc81fa
Binding UserID: 6148cd5e3ba7425ff0cc81fa to SystemID: 5e90f19ecc99d210ecb3406c
Binding UserID: 61081043087f84453191fce7 to SystemID: 609e98bd32a14f0f822e598d
Adding LocalAccount Username: Harrison on userID: 6148cdab59370e3099c06f40
Binding UserID: 6148cdab59370e3099c06f40 to SystemID: 60afdc5821a64317675f9f87
Adding LocalAccount Username: naomie on userID: 6148cd7b3ba7425ff0cc82dd
Binding UserID: 6148cd7b3ba7425ff0cc82dd to SystemID: 60d347fab6a44816c875725c
Adding LocalAccount Username: eddyjohnson on userID: 6148cd7c3ba7425ff0cc82df
Binding UserID: 6148cd7c3ba7425ff0cc82df to SystemID: 610b255f8973970707a1e439
Binding UserID: 5e9612442f4ad8257548b59a to SystemID: 611c07f733ca967a86ee0f5d
Adding LocalAccount Username: tharper on userID: 6148cd959d38866f0814e88c
Binding UserID: 6148cd959d38866f0814e88c to SystemID: 6123b79fdc842b15a96e5ade
Adding LocalAccount Username: Carroll on userID: 6148cdae3ba7425ff0cc8419
Binding UserID: 6148cdae3ba7425ff0cc8419 to SystemID: 61240b820ec56816cea8f4e3
Binding UserID: 61081043087f84453191fce7 to SystemID: 61425a6a22902a1745dd7707

 


After running the last script in this process, users should have their “Local User Account” names set (if a value was added to the field) and those users should be bound to a JumpCloud managed system!

Hopefully this set of information proves to be helpful.

6 REPLIES 6

MickeyCohenDiD
Novitiate I

Can you provide a simple script to set the Local User Account and the Display Name for a username?

Hi @MickeyCohenDiD by set local user account I assume you mean you want to bind a user with a system? If so you can use either of these this commands: 

Add-JCSystemUser [-Username] <String> -SystemID <String> [-Administrator <Boolean>] 

For Example:

Add-JCSystemUser [-Username] user1 -SystemID 1a2b3c4d5e [-Administrator $false]  

OR

Add-JCSystemUser -UserID <String> -SystemID <String> [-Administrator <Boolean>] 

For example: 

Add-JCSystemUser -UserID user1 -SystemID 1a2b3c4d5e [-Administrator $false] 

And you can use this to update the Display Name: 

Set-JCUser [-Username] <String> [-displayname <String>]

 For example:

Set-JCUser [-Username] user1 [-displayname UserOne]

Hope this helps. 

MickeyCohenDiD
Novitiate I

Hi,

I do not want to bind the user to a device as of yet. Just set the "Local User Account" and "Display Name" shown in the User/Details tab.

I guess 

Set-JCUser -Username user1 -displayname "some string" -systemUsername "some local machine user name"

may do the job for user1? Is user1 the name of the user or the UserID shown as "id" field in

Get-JCUser

command ?

Hi @MickeyCohenDiD there currently isn't a way to set the Local User Account field via powershell. Set-JCUser doesn't have systemUsername available to update. I tried this with my test user as well. You can see all the editable fields in the documentation for Set-JCUser.

Screenshot 2024-02-29 at 4.30.49 PM.png

As for the second question, in my examples user1 is the username. If you'd like to update via userID then you would use this format: 

Set-JCUser -UserID 12345

 You can see more examples in the documentation I linked above. Hope this helps! 

MickeyCohenDiD
Novitiate I

Got you. I updated all my 80 users manually 😓

Consider a future feature.

We would love for you to submit one! https://jumpcloud.com/support/request-features