03-24-2023 10:35 AM - edited 03-30-2023 05:47 PM
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.
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.
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:
This post assumes that:
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:
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) |
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:
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.
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.
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.
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:
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.
02-23-2024 09:36 AM
Can you provide a simple script to set the Local User Account and the Display Name for a username?
02-28-2024 08:29 AM
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.
02-28-2024 09:14 AM
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 ?
02-29-2024 06:03 AM
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.
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!
02-29-2024 07:00 AM
Got you. I updated all my 80 users manually 😓
Consider a future feature.
02-29-2024 08:13 AM
We would love for you to submit one! https://jumpcloud.com/support/request-features
New to the site? Take a look at these additional resources:
Ready to join us? You can register here.