cancel
Showing results for 
Search instead for 
Did you mean: 
Disclaimer
JUMPCLOUD EXPRESSLY DISCLAIMS ALL REPRESENTATIONS, WARRANTIES, CONDITIONS, AND LIABILITIES OF ANY KIND ARISING FROM OR RELATED TO THIRD-PARTY SOFTWARE, SCRIPTS, REPOSITORIES, AND APIS. JUMPCLOUD IS NOT REQUIRED TO SUPPORT ANY SUCH THIRD-PARTY MATERIALS AND ALL RISKS RELATED TO THIRD-PARTY MATERIALS ARE YOUR RESPONSIBILITY. PLEASE ALSO REVIEW THE JUMPCLOUD TOS.

Handy Powershell Script & Functions for Automation Using the JumpCloud API

JacobLawson
Novitiate III

Awhile back I was trying to answer the question "How do I automate binding a user to a windows device in JumpCloud?" The answer to that question took me to the JumpCloud API documentation and started me down a rabbit hole I don't think I'll ever be able to climb out of. 

Below you'll find some Powershell scripts and functions I created that have been incredibly helpful in automating our zero touch deployments for Windows.

 

 Creating the Header: 

In order to authenticate your connection to the JC API you'll need your API key and Org ID which can be found in the JumpCloud admin console.  You'll then use those to create a variable that will be the header of your API request. It will look something like this: 

 

 

$APIkey = 'InsertYourAPIKeyHere'
$OrgID = 'InsertYourOrgIDHere'
$Header = @{}
   $Header.Add("Content-Type","application/json")
   $Header.Add("X-API-KEY","$APIkey")
   $Header.Add("X-ORG-ID","$OrgID")

 

 

Querying Directory Insights:

JumpCloud Directory Insight has a wealth of information and it is here that I looked for newly enrolled devices. The specific event we are wanting to search for is system_create which has a few fields that we need to include like initiated_by.id, resource.id, and OS.  This queries the JC Directory Insights API for the event and info we need: 

 

 

$APIkey = 'InsertYourAPIKeyHere'
$OrgID = 'InsertYourOrgIDHere'
$Header = @{}
   $Header.Add("Content-Type","application/json")
   $Header.Add("X-API-KEY","$APIkey")
   $Header.Add("X-ORG-ID","$OrgID")

$BaseURL = "https://api.jumpcloud.com/insights/directory/v1/events"
# convert date/time to format required by JC API
$StartTime = [Xml.XmlConvert]::ToString((Get-Date).AddMinutes(-10),[Xml.XmlDateTimeSerializationMode]::Utc)
# Body of API Request
$body = @"
{
      "service": [
         "all"
      ],
      "start_time": "$StartTime",
      "sort": "ASC",
      "search_term": {
            "and": [
               {
                  "event_type":"system_create"
               }
            ]
      },
      "fields": [
            "initiated_by.id",
            "resource.id",
            "changes"
      ]
   }
"@

# API Query
$JCQuery = (Invoke-RestMethod -Method POST -Uri $BaseURL -Header $Header -Body $body)
$JCQuery

 

 

From here, I take the output of the query and run it through a for-each loop in case multiple new devices were found. For each device, I check the OS and then take certain actions like binding the user that initiated the request and adding the device to a JumpCloud Device Group. 

 

 

# Group ID can be found in the JC Admin Console
$WindowsOnboardingGroupID = "GroupID"

#Separate devices by OS
$JCQuery | ForEach-Object {
    if ($_.changes.to.Contains("windows")) {
        #For Windows devices do the following:
        #Bind user to device as a standard user
        $body = @"
        {
            "op":"add",
            "attributes":{
            "sudo":{
                "enabled":false,
                "withoutPassword":false
            }
            },"type":"user",
            "id":"$($_.initiated_by.id)"
        }
"@
        Invoke-RestMethod -Uri "https://console.jumpcloud.com/api/v2/systems/$($_.resource.id)/associations" -Method POST -Headers $Header -ContentType 'application/json' -Body $body
        
        # Body of API request to add device to a group
        $body = @"
        {
            "id":"$($_.resource.id)",
            "op":"add",
            "type":"system"
        }
"@
        # Add device to Group
        Invoke-RestMethod -Uri "https://console.jumpcloud.com/api/v2/systemgroups/$($WindowsOnboardingGroupID)/members" -Method POST -Headers $Header -ContentType 'application/json' -Body $body
    }
    else {
        # You can add more here for other OS types
    }
}

 

 

This is a simplified example of what is possible. Additional actions and logic can be added to this script depending on what you need. 

At this point the user that initiated the JumpCloud agent install is bound to the device and the device has been added to a specified group. What I like to do from here is scope another command in the JumpCloud Admin Console to that specified onboarding group that initiates various onboarding tasks on the device; like installing software, adding printers, and updating the devices name. My preferred method of triggering that command is with a webhook:  

 

 

# Trigger onboarding commands for Windows if a new device was found
if ($null -ne $JCQuery) {
    $triggerName = 'OnboardWindowsDevices'
    $TriggerURL = "https://console.jumpcloud.com/api/command/trigger/$triggerName"
 
    Invoke-RestMethod -Method POST -Uri $TriggerURL -Header $Header
 }

 

 

I would suggest including an API call in your onboarding script that removes the device from the onboarding group so that the onboarding script doesn't trigger multiple times. 

 

Hosting the Script: 

The script created above is set to query JumpCloud for new devices added with the last 10 minutes.  In order to make the script useful, we'll need to host it somewhere that is able to trigger and run it every 10 minutes.  There are a few solutions that come to mind.  What I personally use is AWS Lambda with an AWS Event Bridge trigger.  Another option is to use windows Task Scheduler on a server or device that is always on.  A third option could be to setup a repeating command in JumpCloud to run against a single device that is always on. 

There are many hosting options but the important part, especially if you include the webhook trigger, is to only run the command once every 10 minutes. 

 

Other Helpful Snippets & Functions:

Get Device's System ID:

In order to do anything to a device with the JC API, you'll need the system ID of the device you are trying to edit. This is how I like to get the system ID:

 

 

# Get system ID by querying for device in JumpCloud by serial number
# Get device serial number
$SerialNumber = (Get-WmiObject -Class win32_bios).SerialNumber
$DeviceURI = ('https://console.jumpcloud.com/api/systems?fields=&filter=serialNumber:$eq:' + "$SerialNumber")

# Make API Call
$DeviceSearch = Invoke-RestMethod -Uri $DeviceURI -Method GET -Headers $Header

# Save system id as variable
$SystemID = $DeviceSearch.results.id

 

 

Manage Association of a Device to a Command:

 

 

function Set-JCCommandAssociation {
    [cmdletbinding()]
    param (
        [Parameter(Mandatory=$true)]
        $Header,
        [Parameter(Mandatory=$true)] 
        $body_op, 
        [Parameter(Mandatory=$true)] 
        $SystemID,
        [Parameter(Mandatory=$true)] 
        $CommandID 
    )
    $body = @"
    {
        "id":"$SystemID",
        "op":"$body_op",
        "type":"system"
    }
"@
    Invoke-RestMethod -Uri "https://console.jumpcloud.com/api/v2/commands/$($CommandID)/associations" -Method POST -Headers $Header -Body $body
}

 

 

Calling the function:

 

 

Set-JCCommandAssociation -Header $Header -body_op "add/remove" -SystemID $SystemID -CommandID $CommandID

 

 

Send JumpCloud a Webhook:

 

 

function Send-JCWebhook {
    [cmdletbinding()]
    param (
        [Parameter(Mandatory=$true)]
        $Header,
        [Parameter(Mandatory=$true)]
        $TriggerName
    )
    $TriggerURL = "https://console.jumpcloud.com/api/command/trigger/$TriggerName"
    Invoke-RestMethod -Method POST -Uri $TriggerURL -Header $Header
}

 

 

Calling the function: 

 

 

Send-JCWebhook -Header $Header -TriggerName $TriggerName

 

 

 Manage Device Group Associations

 

 

function Set-JCDeviceGroupAssociation {
    [cmdletbinding()]
    param (
        [Parameter(Mandatory=$true)]
        $Header,
        [Parameter(Mandatory=$true)] 
        $body_op, 
        [Parameter(Mandatory=$true)]
        $SystemID,
        [Parameter(Mandatory=$true)]
        $GroupID
    )
    # API Body
    $body = @"
    {
    "id":"$($SystemID)",
    "op":"$($body_op)",
    "type":"system"
    }
"@
    # API Call
    Invoke-RestMethod -Uri "https://console.jumpcloud.com/api/v2/systemgroups/$($GroupID)/members" -Method POST -Headers $Header -ContentType 'application/json' -Body $body
}

 

 

Calling the function:

 

 

Set-JCDeviceGroupAssociation -Header $Header -body_op "add/remove" -SystemID $SystemID -GroupID $GroupID

 

 

 

Closing Remarks: 

The JumpCloud API is incredibly helpful and has launched me down a path of automation! They also recently released the ability to run a command after agent install which will change what is necessary in the above script and improve the zero-touch workflow.  Hopefully this helps or serves as a template for others to automate their JumpCloud environment!

PS. I am not a Powershell expert. Please share any improvements or suggestions, they are appreciated! 

2 REPLIES 2

Idan
JumpCloud Alumni
JumpCloud Alumni

Hi @JacobLawson !

Happy to see you are on your JumpCloud API journey 🙂

I'm not sure if you are familiar with our PowerShell Module and our PowerShell SDK - both of these can be very very useful. An example from your script is the ability to query Directory Insights with just one line using this function:
Get-JcSdkDirectoryInsight be sure to check if there are any logs using Get-JcSdkEventCount.

Cheers,

Idan.

Hi Idan! 

The JumpCloud powershell module is pretty great, I use it on my personal machine a decent amount! I stuck with the longer approach for these ones so that I had a script that could run natively without any external modules needing to be installed. That makes it easier to use AWS Lambda to run the scripts.  

What I should probably do is learn some python but I think the work you guys have done on adding an "after agent install" trigger will make a lot of this obsolete, which is ideal.