I am an SCCM guy. We'll get that out first. :)
Our environment has multiple sites across the US with local IT staff in each. We wanted to grant them access to the JSS but restricted them based on their site. The problem is that machines are not automatically assigned to a site, even though we can attach subnets to a building and a building to a site (sort of). So even though JSS knows a machine is on a subnet in a building that is in a site, it will not update the site for that machine. That means the smart computer groups that are limited by site will not show any machines until that machine is also updated to that site...
So I put together a powershell script that uses the API to pull all computers, see if the building and site match, if not, it compares the building to the site list and if it exists, it sets it. This makes the flow much more manageable for larger environments (and really, this should be built in...).
The only problem is that the smart groups do not re-evaluate until their normal schedule. If there is an API call to kick JSS into re-evaluating then please let me know so I can add it. Otherwise, the smart groups will update when the machines check in or a manual recon command is ran.
All you need to do for this script is add a new standard account for JSS API calls and modify the URL, user, and pass. Then run and done. All your computers that have a building that is also a site will be updated.
Script:
#========================================================================
# Created with: SAPIEN Technologies, Inc., PowerShell Studio 2012 v3.1.35
# Created on: 12/14/2015 2:16 PM
# Created by: Corey Thomas
# Organization: Removed for privacy
# Version : 1.0
#========================================================================
### Pre-reqs: Create a standard user account for JSS (full access, custom privileges) and grant it read/update to the JSS Objects. Specify this account below
$JSSAPIURL = "https://yourJSSURL:8443/JSSResource"
$JSSAPIUser = "JSSApi"
$JSSAPIPass = "JSSAPIpassword"
$VerbosePreference = "SilentlyContinue" #Optional for extra logging - Change to "Continue"
#First we need to setup the shell to ignore self-signed certs for non-PKI Casper installs:
add-type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
#Next we setup the creds we will be using:
$user = $JSSAPIUser
$pass = ConvertTo-SecureString -String $JSSAPIPass -AsPlainText -Force
$Creds = New-Object –TypeName System.Management.Automation.PSCredential –ArgumentList $user, $pass
#First, let's get a list of the sites from JSS
$url = "$JSSAPIURL/sites"
$sites = Invoke-RestMethod -Uri $url -Credential $Creds
#The data is stored in $sites.sites.site
#Get a list of all computers:
$url = "$JSSAPIURL/computers"
$computers = Invoke-RestMethod -Uri $url -Credential $Creds
#Setting up a template for XML update into the JSS API
[xml]$Template = "<?xml version='1.0' encoding='UTF-8'?><computer><general><site><id></id><name></name></site></general></computer>"
#Loop through the data and start pulling data:
foreach($machine in $computers.computers.computer){
#Now we setup the URL to pull data from
$computerID = $machine.ID
$computerName = $machine.Name
$url = "$JSSAPIURL/computers/id/$computerID"
Write-Output "Getting data for $computerName"
Write-Verbose "ID: $computerID"
Write-Verbose "API: $url"
#Clear the data variable to ensure we don't accidentally overwrite the wrong data
$data = $null
#Now we call the JSS REST API to get the data
$data = Invoke-RestMethod -Uri $url -Credential $Creds
#Pull the current Site data
$currentSite = $data.computer.general.site.name
$building = $data.computer.location.building
Write-Verbose "Current site: $currentSite"
Write-Verbose "Current building: $building"
#See if that building exists in the site list and update the site to match:
if($sites.sites.site.name -contains $building){
Write-Verbose "Building $building exists in the JSS Sites"
if($currentSite -notmatch $building){
Write-Verbose "Site and building do not match in JSS"
#Get the ID of the site (get the index of the name and then use the index to pull the ID)
$SiteID = $sites.sites.site.ID[[array]::indexof($sites.sites.site.name,$currentSite)]
Write-Verbose "Site ID: $SiteID"
#To change data, we simply update XML and then send it back to JSS REST API.
Write-Host "Updating computer to match site"
### OLD Method, this had the potential to be more destructive since it sent the entire computer data back to the JSS API
### so I switched to using a template to ensure that only the site data is sent back.
### $data.computer.general.site.id = $SiteID
### $data.computer.general.site.name = $building
$newData = $template
$newData.computer.general.site.ID = $SiteID
$newData.computer.general.site.name = $building
$return = Invoke-RestMethod -Uri $url -Credential $Creds -Method put -Body $newData
}else{Write-Output "Site and building match, no changes made"}
}else{Write-Output "Building does not exist as a site, no changes made"}
}