Getting Intune device config Powershell scripts via the Graph API

Since a few months, it’s possible to upload PowerShell scripts to Intune as a part of you device configuration policies. These scripts will then be pushed to the linked Windows devies and run either under the SYSTEM account, or as the logged on user.

While working on an Intune deployment, I wanted to check the PowerShell scripts that are currently in use, and found out you can’t do that through the portal. You can change the properties of the script and upload a new file, but can’t view the current script.

Looking for a way to make the script visible, I started playing around with the Graph API, to see if we can do it via this route. Spoiler: we can! 🙂

First of all, we need to authenticate to the graph API. There is some great example code on the Microsoft Graph GitHub pages that explains how to do this, so I won’t go into any detail here. The scriptblock I use to authenticate result in a $authHeader hashtable, that we can include in our REST-calls to the Graph.

First, I set a few variables in my script that I can re-use in my calls:

We need to use the beta version of the API, because the resources we need (deviceManagement/deviceManagementScrips) are not exposed in the currently stable version.

So, lets make our first call to the API to see what results we get back.

We set the URI we want to call, including the API version and resources specified earlier. Next, we call this URI using the invoke-restmethod cmdlet, including our authentication header we retrieved in the beginning. We use the ‘GET’ method, because we want to retrieve data. Because we set the resource to be deviceManagementScripts, the response will include the deviceManagementScripts currently in use.

The response is a PSObject with several properties. Of course, we have the most interest in the ‘value’ property, as this has the actual data we are looking for. So, rewrite our line of code to get just the ‘value’.

This returns the actual deviceManagementScripts that are currently in use.

In my case, this is only one script that apparently is used to redirect certain folders to OneDrive.

By referencing the id for this script in our API call, we can get more information on this particular object.

Again, we use the ‘GET method to retrieve the information from the Graph API. Because we are referencing this single object, now we don’t need to distinctly retrieve the ‘value’ property to get the actual data.

For ease of reading in this output I truncated the ‘scriptContent’ property shown here a bit, but as you can see we can retrieve all the information we have in the portal: the description, the runAsAccount (that can be either ‘user’ or ‘system’), if a signature check is enforced, and of course the filename of the script and the actual content.

The content of the script is stored (and displayed) in a Base64-encoded string. To make this human readable, we need to decode it.

First, we specify the $script64 variable to store the Base64-encoding string. Next, we decode the Base64-string to UTF8 and store it in the $decodedscript variable.

When we now display this $decodedscript variable, we see the contents of the script!

Again, I truncated to output for readability here. Since we have this variable, we can store the contents in a file and thus save the script to our local harddrive.

Pretty neat, right? But, if we can use this to download a script, why shouldn’t we be able to upload a script this way? Let’s check. The documentation by Microsoft is pretty clear: we call the same deviceManagementSripts resource, but with a POST-method in stead of a GET. In this POST we need to include a JSON in the body with the details of the script we would like to set, including the actual content of the script in the same Base64-encoding we saw earlier.

So, let’s put the building blocks together. I’ve created a mind blowing Powershell-script that I stored as c:\temp\testscript.ps1

We get the content for this file and then encode it using Base64

So now we have the $UploadScriptEncoded variable, containing the Base64-encoded script. Next, we need to build the JSON file to include in the POST to the REST API. I do this by creating a hastable with all the needed information and piping this to the ConvertTo-JSON cmdlet.

In the hashtable I specify the Displayname for the script and a short description, include the scriptContent we just encoded, specify it should run in the user-context and that we don’t want to check for a valid signature. Finaly, we give the filename for the script that will be displayed in the Intune portal where the script is referenced.

To finish up, we call the API with the given parameters to do the actual uploading.

We call the URI specified earlier, including our authentication header with the POST-method. We include the JSON-file we stored in $postbody as the body of the request, specifying that this is indeed a JSON-file.

The response indicates that file was uploaded and configured.

We can now check the Intune portal to double check if the script is there.

There you have it: using the Graph API you can do stuff you can’t do in the portal and automate many things you do Intune. For example, you can place the PowerShell scripts you deploy using Intune in a repository in (for example) VSTS and create a build sequence that uses the Graph API to update the script in Intune every time you push to master. If you haven’t played around with the Graph API, now is the time do so. The possibilities are endless.

Happy scripting!


The OneDrive admin-portal is in preview

Recently, Microsoft announced the OneDrive admin portal to be available in preview.

Through this portal, you can set permissions for your users on sharing files, syncing files and more.

A quick run through:

On the ‘sharing’ menu, you can control if users can share SharePoint- and OneDrive content with external users. You can also set the default type for sharing links.

For anonymous access links, you can set if and when these links will expire. Furthermore, you can block or allow external sharing with certain domains and set if external users can share your content with other.s

The next tab is the sync tab, where you can allow or disallow the synchronization of certain files and set if users can download the sync client.

If you want to limit the amount of storage available for users, you can use the settings on the storage tab.

As you can see, you can also control how long files are retained in OneDrive after a user account is deleted.

On the devices tab, you can set device access rules. I expect some integration with Intune to appear here in the future, so you can manage these rules from one simple interface.

The final tab is the compliance tab, which simply provides links to the respective settings in the Office 365 admin portal.

Want to try out the OneDrive admin portal yourself? It’s currently being rolled out in preview. Visit and log on with your tenant administrator’s credentials to check it out!

Fingerprint readers and corporate email

Fingerprint readers are hot. Many phones now sport this nifty future helping you to unlock your phone even faster. Despite the fact that this Is pretty cool, it does bring some challenges to the admin. Ever since Exchange 2003, and all the way up to Office 365, Exchange has had the option to define a policy to secure the devices connecting through ActiveSync. You can disable camera’s or Bluetooth to protect data leakage from your organization, or define a policy that requires the user to set up a PIN on his device when connecting to his (or her) corporate email account.

There are a few options that can be set here. Simply requiring a PIN, defining the minimum length of the PIN, the time-out before requiring a PIN, and more. What you can’t define, is to allow or disallow certain security options, like fingerprint readers.

If your fingerprint-enabled device plays nice with these settings, varies per device. It turns out that Apple implemented the fingerprint reader on their iPhones to be a substitution for the PIN. When you use the fingerprint reader, the chipset will in the background just use the PIN to unlock the device.

Android phones, or at least the Samsung Galaxy ones, work differently. The fingerprint reader here is just that: a fingerprint reader. The system sees this as a different authentication option and doesn’t use the PIN function as underlying technology. When connecting to Exchange, a PIN will be enforced despite the fact that the fingerprint technology is used, because the Exchange server enforcing this policy isn’t aware of the fact that this reader is there.

Currently, the only way to work around this issue is to not require a password in the mobile device policy, which is a security risk, or to use 3rd party applications to sync your mail. Which is of course an even bigger security risk.