cancel
Showing results for 
Search instead for 
Did you mean: 

Starting with JumpCloud with Organizational Best Practices

Idan
JumpCloud Employee
JumpCloud Employee

For a long time, I wanted to post in the JumpCloud Community. I had many ideas but looked for the one that would make the most impact. Reflecting on my conversations with JumpCloud prospects and current customers, I felt that having a tidy JumpCloud account is easy to talk about but hard to achieve as you need to create groups. But groups are not as easy to think about because you need to think about names, reason, organization, devices, users, policies, etc. Then you start creating/importing users, say to yourself that you will make the groups later, and end up with a flat JumpCloud account. Or you go back and forth and create groups in an ad-hoc fashion making your rollout more difficult.

After thinking about how I can help the JumpCloud Community, I decided to create a script that would do just this, so when rolling out, it's just a matter of checking a checkbox assigning users and devices to their groups.

The script and config files do the following:

  • Backs up your JumpCloud account before making changes (optional)
  • Creates the following empty groups (optional):
    • User Groups
    • Device Groups
    • Policy Groups
  • Backs up your JumpCloud account after making changes (optional)
  • Prints a Summary (optional)
  • The scripts are at the end of this post in code blocks

Just follow the recipe to success outlined below, and you are in the perfect position to start your rollout:

  1. Install PowerShell Core on your device (Windows, macOS, or Linux). I use LTS, which at the time of writing this, is 7.2.4.
  2. Install the JumpCloud PowerShell Module on your device.
  3. Create a list of all your organization units, business units, and sub-teams. These groups should reflect your organization and don't have to relate to any other groups you have in tools/platforms. You can get this information from your knowledge of the company, HR, or any other way. These units can be "Marketing", "Finance", "Sales", etc. For more complex organizations, you can break that down a bit more, for example: "Eng-Product", "Eng-BackEnd", "Eng-FrontEnd", "Eng-DevOps", and so forth.
    (You can see that I added the "All-Company" team. I prefer using it for a naming convention, so I remove the default "All Employees", "All Users", "All Devices", etc. This is optional)
  4. In the "config.json" file:
    1. Modify the value of the "jc_api_key" key with your JumpCloud API Key, which you can find in your JumpCloud Admin Console by clicking on your initials (top right) and clicking on "My API Key".
    2. Modify the "teams" array with the list you created in step #3 above. I like to use dashes and underscores instead of spaces as it makes my life easier when scripting, but you can use spaces if you prefer.
    3. Modify the value of the "backup_path" key with where you want to store the backups.
  5. In the "JC-BestPractice.ps1" file (aka "The Script") look at the top block, which is self-explanatory as I documented each line. For clarity, I'll explain the feature flags and variables:
    1. $config_file is the path to the config file we used in step #4. Note that the config file is in JSON format.
    2. $backup_before indicates the script to backup before making any changes. (Backup output is not suppressible)
    3. $create_groups indicates the script to create all groups (Users, Devices, and Policy groups).
    4. $backup_after indicates the script to backup after making the changes. (Backup output is not suppressible)
    5. $print_summary indicates the script to print a summary. 
  6.  Run the script and check your groups in the console.

Now you have a structure your organizational structure reflected in JumpCloud !

Your Next Steps:

  1. Connect your Google Workspace, M365, or AD to start importing users. I prefer having Staged users enabled.
  2. Onboard Users and put them in the correct User Group. (Optionally add each user to the "All-Company_Users" User group. If you plan not to do that, delete the "All-Company_Devices" group)
    Some pro tips for this step:
    1. When your employees activate their JumpCloud account, tell them to use the current password they have in Google Workspace, M365, or AD. You will change passwords later after you finish your rollout. The reasoning behind this is that we are onboarding them, so we don't want to confuse them with a new password.
    2. When onboarding, tell your employees to download the JumpCloud Protect app regardless of whether you purchased an MFA license. It is to save a potential step when you want to use MFA.
  3. Enroll Devices and put them in the correct Device Group.
    (Optionally add each device to the "All-Company_Devices" Device Group. If you plan not to do that, delete the "All-Company_Devices" group)
  4. Getting Started with Policies by creating Policies and bundling them to the Policy Groups we made. For example, an encryption policy for each device type and then adding these to the "All-Company_Policies". Another example will be team-specific device policies.
  5. Attach the Policy Groups to Device Groups. For example "All-Company_Policies" to every Device Group, which base-lines your compliance, and a unique customized team-specific Policy Group, which adds flexibility and ease of management and audits.
  6. Adding SSO Apps and Bookmarks to User Groups based on what App each User Group needs (this is why we did the organizational breakdown in the first place!) 
  7. Configure your Password and MFA Configurations.
  8. Achieve ZeroTrust by implementing Conditional Access.

At this point, you finished your JumpCloud Rollout !

Group Tips:

  1. Make sure you are synced with HR.
  2. Reviewing your organizational chart so that your groups are reflected correctly in JumpCloud. This can be on a Quarter, Bi-Yearly, Annually.
  3. How many groups should you have? I personally like to use groups even if there is a single user, under the assumption that at some point, someone else will be added or replaced.

Please let me know if this is useful for you, and shout out if you have any questions 🙂

Cheers,

Idan.

 

Screenshots:

Idan_0-1653757672333.png

 

Idan_2-1653757131758.png

Idan_3-1653757149155.png

Idan_4-1653757168009.png

 

"config.json" file:

 

 

 

 

{
    "jc_api_key" : "YOUR_JUMPCLOUD_API_KEY",
    "teams" : [
        "All-Company",
        "Marketing",
        "Operations",
        "Finance",
        "Sales",
        "Eng-Product",
        "Eng-BackEnd",
        "Eng-FrontEnd",
        "Eng-DevOps"
    ],
    "user_group_suffix" : "_Users",
    "system_group_suffix" : "_Devices",
    "policy_group_suffix" : "_Policies",
    "backup_format" : "json",
    "backup_path" : "~/Dev/JC-BestPractice/Backups"
}

 

 

 

 

 

"JC-BestPractice.ps1" file:

 

 

 

 

##BEGIN CONFIG VARIABLES
$config_file    = "config.json"     # Path to $config_file, note that the config file is in the JSON format
$backup_before  = $false             # Mark this as $true if you want to backup before changes, note that backup output is always enabled
$create_groups  = $true             # Mark this as $true if you want the script to create groups based on $config_file
$backup_after   = $false             # Mark this as $true if you want to backup after changes, note that backup output is always enabled
$print_summary  = $true             # Mark this as $true if you want to print the summary
##END CONFIG VARIABLES

## DO NOT CHANGE ANYTHING BELOW THIS LINE

# Test if $config_file exists
if ( -not ( Test-Path $config_file ) )
{
    Write-Host ""
    Write-Host "ERROR: Can't find the file '$config_file'"
    Write-Host ""
    Exit
}

# Variables
$ProgressPreference = "silentlyContinue"
$start_time = $(get-date)
$config = Get-Content $config_file | ConvertFrom-Json
$headers = @{}
$headers.Add("x-api-key", $config.jc_api_key)
$headers.Add("content-type", "application/json")
$results = New-Object System.Collections.ArrayList
$backup_before_changes = New-Object System.Object
$backup_after_changes = New-Object System.Object

# Connect to JumpCloud
Connect-JCOnline $config.jc_api_key -force
$jc_org = Get-JCOrganization
$backup_path = Join-Path -Path $config.backup_path -ChildPath $jc_org.OrgID

# Get existing User Grocups
$current_user_groups = Get-JCGroup -Type User
$current_user_groups = $current_user_groups | Group-Object -AsHashtable -Property name

# Get existing Device (aka System) Groups
$current_system_groups = Get-JCGroup -Type System
$current_system_groups = $current_system_groups | Group-Object -AsHashtable -Property name

# Get existing Policy Groups
$current_policy_groups = Invoke-RestMethod -Uri 'https://console.jumpcloud.com/api/v2/policygroups' -Method GET -Headers $headers -SkipHeaderValidation
$current_policy_groups = $current_policy_groups | Group-Object -AsHashtable -Property name

# Make sure Backup Path exists, otherwise create it
if ( $backup_before -or $backup_after )
{
    if ( -not (Test-Path $backup_path) )
    {
        New-Item $backup_path -ItemType Directory | Out-Null
    }
}

# Backup before making changes to JC organization
if ( $backup_before )
{
    Write-Host ""
    $backup_before_changes = Backup-JCOrganization -Path:($backup_path) -All -Format:($config.backup_format) -PassThru
    Write-Host ""
}

## Create new User, Device (aka System), and Policy Groups
if ( $create_groups )
{
    foreach ( $team in $config.teams )
    {
        # Create User Group
        $user_group_name = ($team + $config.user_group_suffix)
        if ( ( $null -eq $user_group_name) -or ( -not $current_user_groups.ContainsKey($user_group_name) ) )
        {
            $res = New-JCUserGroup -GroupName $user_group_name
            $res | Add-Member -MemberType NoteProperty -Name "GroupType" -Value "User"
            $res
            $results.Add($res) | Out-Null
        }
        else 
        {
            $res = New-Object System.Object
            $res | Add-Member -MemberType NoteProperty -Name "Name" -Value $user_group_name
            $res | Add-Member -MemberType NoteProperty -Name "id" -Value $current_user_groups[$user_group_name].id
            $res | Add-Member -MemberType NoteProperty -Name "Result" -Value "Exists-Skipped"
            $res | Add-Member -MemberType NoteProperty -Name "GroupType" -Value "User"
            $res
            $results.Add($res) | Out-Null
        }
        
        # Create Device (aka System) Group
        $system_group_name = ($team + $config.system_group_suffix)
        if ( ( $null -eq $current_system_groups) -or ( -not $current_system_groups.ContainsKey($system_group_name) ) )
        {
            $res = New-JCSystemGroup -GroupName $system_group_name
            $res | Add-Member -MemberType NoteProperty -Name "GroupType" -Value "Device"
            $res
            $results.Add($res) | Out-Null
        }
        else
        {
            $res = New-Object System.Object
            $res | Add-Member -MemberType NoteProperty -Name "Name" -Value $system_group_name
            $res | Add-Member -MemberType NoteProperty -Name "id" -Value $current_system_groups[$system_group_name].id
            $res | Add-Member -MemberType NoteProperty -Name "Result" -Value "Exists-Skipped"
            $res | Add-Member -MemberType NoteProperty -Name "GroupType" -Value "Device"
            $res
            $results.Add($res) | Out-Null
        }

        # Create Policy Group
        $policy_group_name = ($team + $config.policy_group_suffix)
        if ( ( $null -eq $current_policy_groups) -or ( -not $current_policy_groups.ContainsKey($policy_group_name) ) )
        {
            $body=@{}
            $body.Add("name", $policy_group_name)
            $body = $body | ConvertTo-Json
            $response = Invoke-RestMethod -Uri 'https://console.jumpcloud.com/api/v2/policygroups' -Method POST -Headers $headers -ContentType 'application/json' -Body $body
            $res = New-Object System.Object
            $res | Add-Member -MemberType NoteProperty -Name "Name" -Value $response.name
            $res | Add-Member -MemberType NoteProperty -Name "id" -Value $response.id
            $res | Add-Member -MemberType NoteProperty -Name "Result" -Value "Created"
            $res | Add-Member -MemberType NoteProperty -Name "GroupType" -Value "Policy"
            $res
            $results.Add($res) | Out-Null
        }
        else
        {
            $res = New-Object System.Object
            $res | Add-Member -MemberType NoteProperty -Name "Name" -Value $policy_group_name
            $res | Add-Member -MemberType NoteProperty -Name "id" -Value $current_policy_groups[$policy_group_name].id
            $res | Add-Member -MemberType NoteProperty -Name "Result" -Value "Exists-Skipped"
            $res | Add-Member -MemberType NoteProperty -Name "GroupType" -Value "Policy"
            $res
            $results.Add($res) | Out-Null
        }
    }
}


# Backup after making changes to JC organization
if ( $backup_after )
{
    Write-Host ""
    $backup_after_changes = Backup-JCOrganization -Path:($backup_path) -All -Format:($config.backup_format) -PassThru
    Write-Host ""
}

# Print Summary
if ( $print_summary )
{
    $elapsed_time = $(get-date) - $start_time
    $total_time = "{0:HH:mm:ss}" -f ([datetime]$elapsed_time.Ticks)
    $number_of_groups_created = $results.get_Count()
    Write-Host ""
    Write-Host ("Execution Summary for JumpCloud OrgID: " + $jc_org.OrgID)
    Write-Host "* Start Time: $start_time"
    Write-Host "* Total runtime: $total_time"
    Write-Host "* Number of Groups Created: $number_of_groups_created"
    if ( $backup_before ) { Write-Host ("* Backup before changes file name: " + $backup_before_changes.BackupLocation.FullName + ".zip") }
    if ( $backup_after )   { Write-Host ("* Backup after changes file name:  " + $backup_after_changes.BackupLocation.FullName + ".zip") }
    Write-Host ""
}

 

 

 

 

 

2 REPLIES 2

RyanBailey
Novitiate III

This is an awesome resource @Idan! Would've saved us a bunch of time when onboarding.

Any improvements in mind if you had the time?

Idan
JumpCloud Employee
JumpCloud Employee

Hi @RyanBailey !

Apologies for the late reply. I'm happy that you find this as a good resource for you 🙂

I'm not sure I have time for modifying the script, but am interested on hearing suggestions you may have !