Nathan Ziehnert

9 minute read

I have to say, the ConfigMgr team has done a truly phenomenal job of implementing a nice REST API to read data. If you've talked with me about the AdminService (or Adam Gross) you'll know that there is SO much value here. For me, the possibility of creating Power Apps on the Power Platform with this data (and the associated methods) is going to be a game changer.

So Much Room For Activities

So I know you're itching to get to work - let's start with the basics.

 

OData Basics

OData (Open Data Protocol) is an ISO/IEC approved, OASIS standard that defines a set of best practices for building and consuming RESTful APIs.

- https://www.odata.org/

Essentially this means that you shouldn't have to guess how to get or set data if the implementer (the ConfigMgr team in this case) has followed the standard. For the most part, the important parts of the protocol have been implemented by the ConfigMgr team with some minor exceptions. I expect as the development continues into the AdminService we will see more added (especially if requested via UserVoice).

For our purposes in this post we're going to focus on entities and entity sets. We'll briefly talk about relationships, but as of the time of writing this post relationships are still a bit buggy. Entities are just an instance of an entity set - imagine an entity set as an array of objects (e.g. SMS_R_Users) and the entity would be an instance of the object (e.g. User).

Each entity can have properties (e.g. a user's email address). Entities are also assigned a key based on a subset of these properties (e.g. a user's ResourceId).

So when we read data from the AdminService we're going to be given a entity set in JSON (JavaScript Object Notation) format. It will look something like this:

{
    UserId: 001,
    UserName: "nziehnert",
    Email: "nathan@z-nerd.com",
    Phone: "555-555-5555"
},
{
    UserId: 002,
    UserName: "pmitchell",
    Email: "petem@topgun.org",
    Phone: "555-867-5309"
}

In this example of an entity set we have two entities, both with the properties of UserId, UserName, Email, and Phone.

 

What Entity Sets are Available Through the AdminService?

It's an exhaustive list. You have two different endpoints where you can gather data: the AdminService, and what you're probably most familiar with: the WMI classes.

To see which entity sets are available, just go to the following URL replacing the SERVERFQDN with the FQDN of the Site Server with the SMS Provider role (the trailing slash is important):

  • https://SERVERFQDN/AdminService/v1.0/
  • https://SERVERFQDN/AdminService/wmi/

You'll get output similar to this:

Entity Sets

Each entity set is defined with a Name, a Kind (which is almost always EntitySet) and the URL for that entity set.

 

Reading an Entity Set

To read an entity set, we just append the URL of the entity set (which can be retrieved from the URLs in the previous section) to the endpoint you are reading from. The URL generally also matches the name of the entity set. In the case of WMI it also matches the class name. These are CASE SENSITIVE so make sure you get it right. Here's an example for SMS_R_User:

URL Example

So, let's read from the SMS_R_User WMI class! To do this we just append “SMS_R_User” to the WMI endpoint, like so:

https://SERVERFQDN/AdminService/wmi/SMS_R_User

Go ahead, run it - I'll wait…

Boom Baby!

BOOM BABY - You just executed your first query against the AdminService!

 

Filtering Our Data

Now what if you wanted to just get a single entity, or a subset of those entities? This is especially important when it comes to Power Apps because the default max data set that you can work with is 500 objects… yikes!

Getting a Single Entity by Key

This is one area, as far as I can tell, that is not fully implemented yet. For example SMS_Collection's key is “CollectionID”, but if you try to get a single entity by CollectionID (which I'll show here) you end up with a error similar to the following:

Failed to find complex type with name: SCCMGraph.SMS_CollectionRuleQuery

I assume that these particular issues will be fixed in future versions (as we submit feature requests to the ConfigMgr team). Anyways the basic way this is done is by appending (KEY) to the end of our URL. It will look like the following:

https://SERVERFQDN/AdminService/wmi/SMS_R_User(2063597568)

Where 2063597568 is the ResourceId (which is the key) of the user.

So how do you determine the key? This can be a little challenging. We need to review the $metadata of the OData endpoint and then look for the keys on the entity type.

Steve Brule - Huh?

The $metadata is a representation of how the entity sets and entities will be presented when you retrieve them (in addition to other things that we'll cover in a future post). Okay… so earlier we looked at the available entity sets by visiting a URL. We can get the $metadata the same way:

  • https://SERVERFQDN/AdminService/v1.0/$metadata
  • https://SERVERFQDN/AdminService/wmi/$metadata

Be forewarned, this is going to look scary. I'm going to help you break it down. Let's search for the entityset “SMS_R_User” in the WMI metadata:

SMS_R_User - Entity Set

Looking at this, we do see the properties that we are used to: FullUserName, Mail, Name, etc., but there doesn't appear to be anything that looks like a “key”. You're right. Normally you would see a child element called “<Key>”. So where is it defined? The “key” (heh.) is in the “BaseType” attribute on the EntitySet element. It equals “SCCMGraph.SMS_Resource”. So now we need to search for the SMS_Resource entity type - and lucky for us, it's actually right above the SMS_R_User element.

SMS_Resource - Entity Set

There are a number of different entity sets that share the same set of base properties, so instead of writing these base properties out for every entity set, OData allows the developer to define a BaseType. This means that every entity set that uses a base type (in our case SCCMGraph.SMS_Resource) ALSO contain the properties and/or keys defined in the BaseType. Lo and behold we see our key is defined as the “ResourceId”. So for SMS_R_User, you use the ResourceId of the entity to get a single entity from the entity set, as was the case in my previous example:

https://SERVERFQDN/AdminService/wmi/SMS_R_User(2063597568)

It's not always useful to get a single result though. Many times we're looking for a subset of data - like all users that have “Domain Admin” in their name. This is where the $filter syntax comes in.

Getting a Subset of Entities Using the $filter Syntax

The filter syntax is very similar to how you might filter something in PowerShell. Here's a basic example of a filter query on the SMS_R_User class:

https://SERVERFQDN/AdminService/wmi/SMS_R_User?$filter=ResourceId eq 2063597568

In this example we're telling the AdminService to return all users with a ResourceId that equals 2063597568.

Cool. So what filter operators are available to us?

Operator/Function Example
Equals ?$filter=ResourceId eq 1234
Not Equals ?$filter=ResourceId ne 1234
Greater Than ?$filter=ResourceId gt 1234
Less Than ?$filter=ResourceId lt 1234
Greater Than or Equal ?$filter=ResourceId ge 1234
Less Than or Equal ?$filter=ResourceId le 1234
And ?$filter=ResourceId eq 1234 and FullDomainName eq 'ZNERD.DEV'
Or ?$filter=ResourceId eq 1234 or FullDomainName eq 'ZNERD.DEV'
Contains ?$filter=contains(Name,'Ziehnert')
Starts With ?$filter=startswith(Name,'Ziehnert')
Ends With ?$filter=endswith(Name,'Ziehnert')

We can combine these operators with parenthesis as well. So we could build a string like:

?$filter=(contains(Name,'Ziehnert') or contains(Name,'Mitchell')) and FullDomainName eq 'znerd.dev'

Currently, the following filters - although defined in the OData protocol - are not available to the AdminService as far as I can tell:

  • Not
  • Has
  • matchesPattern

Additionally there is an option to filter on a DateTimeOffset object - so you could, say, grab a list of all devices which have not checked in for 10 days. I will describe that use in a future post because it gets even more confusing.

Now what if we want to order our data on one of the available properties?

Using the $orderby Syntax

This one is much simpler. We just need to pass the properties that we want to sort on to $orderby and tell it if we want to sort ascending or descending:

https://SERVERFQDN/AdminService/wmi/SMS_R_User?$orderby=FullDomainName asc, FullUserName desc

In this example we are first sorting all users by their FullDomainName ascending and then by their FullUserName descending.

Now what if you wanted to grab a set of Users, but paginate them?

Using the $top and $skip Syntax

Okay, so when I said paginate the data, OData doesn't do this for you. However, if you're building an application where you have pagination, you can retrieve a specific number of objects ($top) and start that retrieval from a specific index in the entity set ($skip).

https://SERVERFQDN/AdminService/wmi/SMS_R_User?$skip=10&$top=5

In this example we are skipping the first 10 entities and then returning the next 5 results. So you've effectively grabbed results 11-15.

Also notice how we combined the $top and $skip syntax by adding and ampersand. You can do this with the other syntaxes as well - you could combine $filter with $top for example.

Now every time we have run these queries we've retrieved ALL of the properties for those entities. This could be expensive and make your query run longer than you need it to. What if you only wanted the e-mail address and username?

Using the $select Syntax

The $select syntax works similarly to how SELECT works in T-SQL. We tell the $select syntax which properties we would like to retrieve and only those properties will be returned!!

https://SERVERFQDN/AdminService/wmi/SMS_R_User?$select=FullUserName,Mail

Notice that to select multiple properties you just need to separate them with a comma.

Using the $count Syntax

This isn't really a “filter” per se, but you can easily grab a total count of the entities in a entity set by appending a forward slash and then $count

https://SERVERFQDN/AdminService/wmi/SMS_R_User/$count

This will return an integer representing the number of objects.

 

Miscellaneous Unsupported Syntaxes

So, as I said earlier, not all of the OData protocol has been implemented. Here are a few of the current (as of the time of writing this post) unsupported syntaxes.

Retrieving a Single Property of an Entity

As defined in OData you can append a forward slash and the property name to a single instance of an entity and have just that property returned. This is not currently supported, but could easily be mimicked with the $select syntax. By extension you cannot retrieve the raw value (which is done by appending an additional forward slash and then $value).

When you have relationships between entities - for example SMS_R_System has a related entity type of SMS_G_System_OPERATING_SYSTEM - you should be able to get the related entity by appending a forward slash and the name of the related entity set. I believe this was possibly supported in a previous technical preview, so the dev team might be working out some issues here.

 

For the Over Achievers In the Audience

If you would like to look further into the OData conventions to explore things that may not have been covered in this post, you can review the following links:

https://www.odata.org/getting-started/basic-tutorial/

http://docs.oasis-open.org/odata/odata/v4.0/errata03/os/complete/part2-url-conventions/odata-v4.0-errata03-os-part2-url-conventions-complete.html

There is also a Postman collection available to help you learn the conventions. That is available here:

https://www.odata.org/getting-started/learning-odata-on-postman/

 

Final Thoughts

If you stuck with me for this entire post then this gif is for you!

Boom Baby!

Thanks for sticking around and I hope you get a chance to poke around the AdminService and do some fun things with the data you get! Until next time, Happy Admining!

comments powered by Disqus

Table of Contents