ConfigMgr 1906 Technical Preview – Testing CMPivot over CMG using AdminService

ConfigMgr 1906 Technical Preview was released last week and I immediately upgraded one of my tech preview labs I have running in Hyper-V on my laptop. As you may know, I’m a huge fan of the new AdminService functionality which will allow you to interact with the SMS Provider over HTTPS. With each release, it has had more features added. With Tech preview 1906, I noticed that you can run CMPivot using the AdminService. In 1905 you could use RunScript, though the functionality for that has been disabled in the frontend for 1906. With both of these tools, you can extend your reach to all of your devices, no matter where they are, or how they are connected (within reason I suppose).

As I was experimenting with CMPivot over AdminService, I wrote a quick PowerShell to test it and ended up Tweeting about it. David James responded with a challenge that I couldn’t pass up:

Since I was working in my disposable Technical Preview lab, I’ve never set up any cloud services for it. I have to say, the process (and documentation) to configure Azure Active Directory, Intune, AutoPilot, Co-Management, Cloud Management Gateway, etc has gotten much better. I think that I spent more time waiting for NameCheap to replicate my DNS entries than I did for anything else.

The Setup

Each of these bullets represents quite a few steps that are required to configure the features required to access and manage devices over the internet. For the sake of this blog, I’m just listing them, otherwise I doubt I’d ever get this post out the door!

  • Azure Active Directory Pay-As-You-Go subscription (apparently you can’t just keep signing up for free trials!)
  • Intune Trial with AutoPilot with Azure AD Join Profile Deployed
  • Microsoft 365 E5 30-day Trial
  • Azure AD Connect
    • Hybrid Azure AD Join (not used for the test device)
    • Password Hash Sync with Seamless Single Sign-On
  • ConfigMgr 1906 TP on Hyper-V and Widows Server 2016
    • Co-Management Cloud Service Configured
    • Cloud Management Gateway with wildcard cert from SSLforFree
    • External facing Domain name added to local AD as an alias and assigned to user accounts (my lab uses a non-routable .lab extension)
  • Windows 10 1809 Hyper-V VM
    • Autopiloted using Azure AD user account (non AD sync’d account)
    • ConfigMgr client deployed from Intune as a Line of Business App
    • Connected to External Ethernet adapter for direct internet access

With this setup I was able to build a new Windows 10 device and deploy it as an Azure AD Joined device with AutoPilot, provision it with an Azure AD User account over the internet and auto install the ConfigMgr client configured to use AAD for authentication.

Testing CMPivot over HTTPS

The first step was to confirm that everything was communicating properly by checking CcmNotificationAgent.log on my internet client. See the logs section below for more info on what to look for. In the console, I verified that BGB was working by looking for the green check on the device’s icon on the console. So, I knew right away that the client was communicating with the console just fine.

CMPivot in the Console

My first test was just opening the ConfigMgr console and launching CMPivot from my All Systems collection (I only have 3 devices in my lab, you may want to use a different collection). Inside CMPivot, I submitted a few queries and got results back from the client.

Postman Chrome Extension

Next I used the Google Chrome Postman extension to test using the AdminService.

A quick note about Postman – the standalone version of Postman doesn’t handle Kerberos properly, but the Chrome extension does. In 1906 TP, NTLM is no longer supported and you can’t pass credentials to the AdminService, so I had to switch to the Chrome version.

In order to run CMPivot, you need a CollectionId and a QueryString. Also, AdminService is CaSe SeNsEtIvE, so be careful! Run these commands in Postman to query. Don’t forget to change the request type to POST since you have to submit a request body.


After Postman, I tested with Powershell and had similar results. Each time I ran CMPivot using AdminService, I would see the the CMPivot script payload on the client then I could view the results in the database OR with AdminService using the SMS_CMPivotStatus method. (You can obviously run these in Postman as well).

If you want to see the status of a specific job, you can filter the results. The easiest option is to grab the OperationId from the json returned when you submit the CMPivot job. It should look like this. The OperationId is just below the script blob 16777270.

Once you have the OperationId, you can just pass it to the status query as a filter.

And the results will looks like this:

As you can see, the clients each return an a block of json with an XML blob in the ScriptOutput node. If you expand this out, it contains the results row that you are used to seeing when you run CMPivot from the app in the console.

Additionally, if you use the wmi controller vs the v1.0 controller in the request URI to view the script status, you will get a few extra WMI fields, but overall the status is the same. Also each client block contains overall information about the script including the total devices (completed, failed, etc – populated once the job has timed out attempting to contact clients) as well as a the script results for each device. (You can also see these results in the database if you want yet another option).

I attempted to do some cool stuff with it in PowerShell but I was just reinventing the wheel since the CMPivot console already does what we need. If you want to do some cool automation though, learning how to execute and view results with AdminService will be worthwhile.

Anyway, here’s the Powershell that I wrote. It will output the results to gridview. When you close it, it will prompt you to reload the grid for new results since we have to wait for machines to report back. It’s a proof of concept, not production code!

Logs And Such

As is the case with ConfigMgr, logs are the first stop for most troubleshooting. Here are a few that had useful bits.

AdminService.log (server-side)

Each time an AdminService command is run, you will see it logged in AdminService.log in your logs folder on the site server where your SMS Provider is running from. This result is from running SMS_CMPivotStatus.

CcmNotificationAgent.Log (Client-Side)

On your clients, you can watch CcmNotificationAgent.log for the client to communicate over the client fast channel (BGB). This action is what gives you the green checkmark on clients in the console indicating they are currently online. Since CMPivot uses the fast channel to drop the query script on the device when you run CMPivot, this log will show the encrypted script payload as it gets dropped on the client. It will look something like this. You can see that the first line contains the encrypted script blob from the CMPivot job.

Run Scripts over CMG

I didn’t cover using Scripts in this post, but I did test the over the Cloud Management Gateway as well and they worked the same way as CMPivot, in fact, if you look at the Scripts table in the database, you will see the CMPivot master script listed along with any of your other scripts. While the AdminService can’t run scripts in 1906 (as far as I can tell), it can in 1905 and I believe the guts are still in the AdminService just not exposed this time. So in a real world situation, you would be able to run CMPivot then use Scripts to take immediate action – all through the Cloud Managemen Gateway on internet connected clients.

This is a terrible screenshot. Just go query your DB

A note about CMG

I feel like CMG may need it’s own post. Just a quick note about it. When an internet-only client checks in, it uses an internet facing proxy address that points to the CMG server in your Azure tenant. Here’s a snippet from StatusAgent.log showing the internet-facing CMG address ASDCMTP1906CMG.ASDCMTP1906.SITE. This is the same way that Email Approvals work when you click the approval link while connected to the internet.

Here’s where that address is configured in the console on your Cloud Management Gateway. Additionally, you have to set up a CName record to point the url to the address listed below it.


So, thanks for the challenge David! The scenario you described is not only a common scenario to encounter, but can easily be accomplished using ConfigMgr integrated with Intune using Co-Management and a Cloud Management Gateway.

Quick Scenario – A user with an internet-only, Azure AD joined, co-managed device calls in because they need to get on VPN for something but the VPN client isn’t working. Use CMPivot to query the offending registry setting. Run a script to remediate – all from the ConfigMgr console, all through the Cloud Management Gateway, just like you would do for any other device you manage. Truly #ITWithoutBorders.

Anyone who says they don’t need Intune today must not have any remote users (like that one dude from MMSMOA!). I can’t wait to see what 1906 CB has in store for us!


  • Reply
    yohan NEY
    February 3, 2021 at 4:22 am


    When I try to run the Powershell script, I have the following error message : “No HTTP resource was found that matches the request URI”

    I have tried different uri, one for getting the site and one for getting the devices and it works perfectly. Only the one for getting the cmpivot does not work: “https://AdminService/v1.0/Collections(‘SMS00001’)/AdminService.RunCmpivot.
    Is there something I am missing ?


  • Comment

    This site uses Akismet to reduce spam. Learn how your comment data is processed.


    Fatal error: Uncaught GuzzleHttp\Exception\ClientException: Client error: `POST` resulted in a `400 Invalid instrumentation key` response: {"itemsReceived":1,"itemsAccepted":0,"errors":[{"index":0,"statusCode":400,"message":"Invalid instrumentation key"}]} in /opt/bitnami/apps/wordpress/htdocs/wp-content/plugins/application-insights/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php:113 Stack trace: #0 /opt/bitnami/apps/wordpress/htdocs/wp-content/plugins/application-insights/vendor/guzzlehttp/guzzle/src/Middleware.php(66): GuzzleHttp\Exception\RequestException::create(Object(GuzzleHttp\Psr7\Request), Object(GuzzleHttp\Psr7\Response)) #1 /opt/bitnami/apps/wordpress/htdocs/wp-content/plugins/application-insights/vendor/guzzlehttp/promises/src/Promise.php(203): GuzzleHttp\Middleware::GuzzleHttp\{closure}(Object(GuzzleHttp\Psr7\Response)) #2 /opt/bitnami/apps/wordpress/htdocs/wp-content/plugins/application-insights/vendor/guzzlehttp/promises/src/Promise.php(156): Guzzle in /opt/bitnami/apps/wordpress/htdocs/wp-content/plugins/application-insights/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php on line 113