MSEndpointMgr

Getting started with Microsoft Intune and Azure Automation

Microsoft has recently announced the general availability of the Microsoft Graph Intune API’s. This means that we now have two set of API’s that we can use to access resources in Microsoft Intune, the beta and v1.0 API’s. The main differences between these two are that v1.0 is meant for production and beta is meant for testing and verifying new functionality being made available in Microsoft Intune. Once a new feature is made general available in the monthly update cycle of Microsoft Intune, and its Graph API resource is made available in the v1.0 API, you should update the resource reference for your script. Microsoft is keeping a change log for what’s new and has changed for both API’s, available here:

https://developer.microsoft.com/en-us/graph/docs/concepts/changelog

However, this post is not about the general availability of this new API. In this post we’re meant to cover the subject of setting up unattended automation workflows with the capability of running and/or scheduling PowerShell scripts built for Microsoft Intune using Azure Automation and how to handle storing of credentials for authentication.

Some time back, I wrote a couple of blog posts on how to get started with Microsoft Intune and PowerShell. This post will not cover the requirements of having the prerequisites in terms of an Azure AD app registration or using the default Microsoft Intune PowerShell enterprise app, but I recommend reading the following post to get a better understand of what’s required:

Create an Azure AD App Registration for accessing Microsoft Intune Graph API

Depending on if you’ve read the post mentioned above, or are already familiar with the requirements of accessing Graph API with an app registration or enterprise app, it’s important to know the ClientID (also referred to as the ApplicationID) of your app. If you will be using the Microsoft Intune PowerShell app, the ClientID that you should be familiar with is this:

d1ddf0e4-d672-4dae-b554-9d5bdfd93547

Accessing resources and none the less automating tasks in Microsoft Intune is not a walk in the park. It’s different to what you may be experienced with already in terms of automating a ConfigMgr hierarchy where you’d simply load a module and fire up a bunch of cmdlets to get going. When building scripts for Microsoft Intune, you require to be authenticated and correctly licensed in your tenant for it to work. There’s also the requirement of using the enterprise app or app registration that we’ve previously discussed. Image the scenario when you’ve been given a task to automate something for Microsoft Intune. With the knowledge of you requiring to authenticate for your script to run, you’d want to store your credentials in a safely manner. The Microsoft Intune PowerShell samples provide an example for how to store credentials used for authentication in a local file, but isn’t there an easier and more secure way of dealing with stored credentials?

Let’s talk about Azure Automation for a bit. Just like Microsoft Intune, Azure Automation is also a cloud service provided by Microsoft. Doesn’t it make sense to automate a cloud service using another cloud service, and at the same time use a secure method of storing the credentials required for authentication? In my opinion it does, for sure. I’m not trying to debate about cloud vs. on-premise here, I’m simply expressing my opinion. Obviously there could be circumstances where this would not be suitable.

Authentication

Now that we’ve discussed storing of credentials, we need to cover the authentication part. If you’re a frequent reader of this blog, you may remember a post I wrote some time back regarding how to handle the authentication when attempting to automate Microsoft Intune with PowerShell. The post in mention describes a PowerShell script that I wrote back then to assist with retrieving the authentication token (access token or bearer token). This script uses a couple of assemblies from the AzureAD module in order to prompt for the credentials, however this method is not a suitable candidate for what we’re talking about here. We need a method that supports an unattended workflow that’s using stored credentials for authentication.

For this purpose, I decided to re-write the PowerShell script mentioned in the post above into a module called PSIntuneAuth. This module contains a function named Get-MSIntuneAuthToken that has a parameter called Credential. This parameter supports passing of regular PSCredential objects, retrieved either using Get-Credential or Get-AutomationPSCredential cmdlets.

If you want to download and play around with this module, I’ve published it to PowerShell Gallery for your convenience. Run the following command to install it:

Install-Module -Name PSIntuneAuth

However, this blog post is about how to get started with Microsoft Intune and Azure Automation, so lets get back on track.

Prepare Azure Automation

Hopefully by now, you’ve prepared your tenant with an Enterprise App from Microsoft or created your own Azure AD app registration. What we need to do now is to prepare Azure Automation with some customization in order to be able to use the PSIntuneAuth module from PowerShell Gallery and setup a few variables. We’ll not cover the basics of getting started with Azure Automation it self, and from this point on we’re assuming that you’ve created an Automation Account in your tenant. An Automation Account looks like this in the portal:

Add PSIntuneAuth module

Adding the PSIntuneAuth module so that any runbook you create in the future can leverage it and import it’s functions is a very simple task. Microsoft has made it extremely convenient for us to add module from the PowerShell Gallery.

  • From within your Automation Account, scroll down to Shared Resources and select Modules Gallery.
  • In the search field, write PSIntuneAuth.

  • Click on the search result for PSIntuneAuth.
  • Click on Import.

  • Click OK in the import blade that appears.
  • Go back to Shared Resources and select Modules. You should now be able to see the PSIntuneAuth module at the bottom.

Don’t panic if you can’t see the module right away, it takes a little while for it to appear or become available. Once it has been imported successfully, the status should change to Available. Make a note of the name of the variable, since we’ll reference it later when we’re going to take a look at a sample code for retrieving an authentication token and running a PowerShell script against Microsoft Intune.

NOTE: For the PSIntuneAuth module to work, it requires that the AzureAD module is present as well. Make sure its status reads Available in the Modules blade.

Add required variables

If you’re not using a custom app registration, this part is not really necessary since the authentication function from the PSIntuneAuth module uses the ClientID from the default Microsoft Intune PowerShell enterprise app. However, when using a custom app registration, it needs to be passed into the authentication function from the PSIntuneAuth module. We can either put it straight into every script, or we can store it as an automation variable in Azure Automation.

  • In the Automation Accounts blade, scroll down to Shared Resources and select Variables.
  • Click Add a variable.
  • Give it a name, e.g. AppClientID. Set Type as String and enter the ClientID (also referred to as ApplicationID of your custom app registration) in the Value field. Do not set it as encrypted.

  • Click Create.
  • In the Variables blade you should now see the newly created variable.

Whenever we want to retrieve this variable object, we can simply use the Get-AutomationVariable cmdlet and specify the name of it.

Add automation credential

Just like for the variable we just created, we can populate the credentials that we want to use when attempting to retrieve the authentication token. Azure Automation provides the capability of securely storing credentials being used in runbooks, and this makes perfect sense for our scenario.

  • In the Automation Accounts blade, scroll down to Shared Resources and select Credentials.
  • Click on Add a credential.
  • Give the credential a name and make a note of it, just like we did for the variable object. Enter user name and password.

  • Click Create.

Whenever we want to retrieve this credential object, we can simply use the Get-AutomationPSCredential cmdlet and specify the name of it.

Create a runbook for accessing Microsoft Intune resources

All the prerequisites are now in place and are ready to be utilized for basically any type of automated workflow. Let’s picture a scenario where we’d want an automated workflow that validates if any of the uploaded Apple VPP tokens in the tenant have expired, or are about to expire soon. If you’re following along and want to successfully test this scenario, I’d recommended that you have at least one Apple VPP token in your tenant, otherwise you’ll not get the similar results when we’re about to execute the runbook.

Before we dig into the actual code that we’re going to use, and how to setup a runbook in Azure Automation, let’s explore the Intune Graph API resources available to us. The documentation for both API references can be found here:

When browsing through the documentation, it’s easy to get lost in the beginning. It’s also worth mentioning that the categorization between the two API’s may differ on some occasions, so don’t panic when you can’t find what you’re looking for. For instance, I’ve noticed that some resources may get moved under another resource when made general available.

For our scenario, there’s unfortunately at the time of writing this post, no resource available for Apple VPP tokens in the v1.0 API, so we’re going to leverage a resource named deviceAppManagement/vppTokens. You’ll find it’s reference in the beta documentation below:

https://developer.microsoft.com/en-us/graph/docs/api-reference/beta/resources/intune_onboarding_vpptoken

Create a runbook

Now, let’s create the runbook that will be used to run the actual PowerShell script to detect the expired Apple VPP tokens.

  • In the Azure portal under Automation Accounts, select your account and scroll down to Runbook.
  • Click Add a runbook.
  • Select Create a new runbook.
  • Give the runbook a unique name and select PowerShell as the runbook type.

  • Click Create.

You’ve now been directed into the editing mode of the newly created runbook. It’s now time to add the actual PowerShell code to the runbook.

Edit a runbook

Instead of simply showing you how to copy and paste the code to get our scenario working, let’s step through the code that we’ll be using to get a better understanding of how we utilize the prerequisites that we’ve setup previously in this post. We begin with simply importing the required modules.

# Import required modules
try {
    Import-Module -Name AzureAD -ErrorAction Stop
    Import-Module -Name PSIntuneAuth -ErrorAction Stop
}
catch {
    Write-Warning -Message "Failed to import modules"
}

We do that by running Import-Module and specify AzureAD and PSIntuneAuth, like shown above. Next we acquire the stored credentials and the ClientID (or ApplicationID) from a variable.

# Read credentials and variables
$Credential = Get-AutomationPSCredential -Name "IntuneAutomation"
$AppClientID = Get-AutomationVariable -Name "AppClientID"

By using the Get-AtomationPSCredential cmdlet, we get our credentials that will be used to authenticate and acquire our authentication token. The Get-AutomationVariable cmdlet is used to retrieve the data from a variable named AppClientID, that we created earlier in this post. With this information at hand, we can now attempt to acquire the authentication token.

# Acquire authentication token
try {
    Write-Output -InputObject "Attempting to retrieve authentication token"
    $AuthToken = Get-MSIntuneAuthToken -TenantName emsmgmt.onmicrosoft.com -ClientID $AppClientID -Credential $Credential
    if ($AuthToken -ne $null) {
        Write-Output -InputObject "Successfully retrieved authentication token"
    }
}
catch [System.Exception] {
    Write-Warning -Message "Failed to retrieve authentication token"
}

At this point we should now have a valid authentication token in the $AuthToken variable. Next up is to make a call to the Graph API.

# Get Apple VPP tokens
Write-Output -InputObject "Attempting to retrieve Apple VPP tokens"
$AppleVPPResource = "https://graph.microsoft.com/beta/deviceAppManagement/vppTokens"
$AppleVPPTokens = (Invoke-RestMethod -Uri $AppleVPPResource -Method Get -Headers $AuthToken).Value

As shown in the code above, we define the Apple VPP resource URI and we then pass that together with the authentication token. If the call is successful, the property named Value will contain the data retrieved from Graph API. At this point we’re now able to perform any type of data validation.

# Validate tokens
if ($AppleVPPTokens -ne $null) {
    foreach ($AppleVPPToken in $AppleVPPTokens) {
        $AppleVPPExpirationDate = [System.DateTime]::Parse($AppleVPPToken.expirationDateTime)
        if ($AppleVPPExpirationDate -lt (Get-Date)) {
            Write-Output -InputObject "Apple VPP token has already expired"
        }
        else {
            $AppleVPPTokenDaysLeft = ($AppleVPPExpirationDate-(Get-Date))
            Write-Output -InputObject "Apple VPP token expires in days: $($AppleVPPTokenDaysLeft.Days)"
        }
    }
}
else {
    Write-Output -InputObject "Query for Apple VPP tokens returned empty"
}

While making sure that we actually have retrieved any data, it’s simple enough to loop through all the returned objects and run the validation, in this scenario to see if the expirationDateTime for each token is less than today’s date. If not, we output the remaining days.

NOTE – Data retrieved from Graph API is formatted as JSON. In order to convert a JSON date string into a PowerShell object, we can use the [System.DateTime]::Parse() static method.

If we put everything together, we end up with the following PowerShell code for our scenario:

# Import required modules
try {
    Import-Module -Name AzureAD -ErrorAction Stop
    Import-Module -Name PSIntuneAuth -ErrorAction Stop
}
catch {
    Write-Warning -Message "Failed to import modules"
}

# Read credentials and variables
$Credential = Get-AutomationPSCredential -Name "IntuneAutomation"
$AppClientID = Get-AutomationVariable -Name "AppClientID"

# Acquire authentication token
try {
    Write-Output -InputObject "Attempting to retrieve authentication token"
    $AuthToken = Get-MSIntuneAuthToken -TenantName emsmgmt.onmicrosoft.com -ClientID $AppClientID -Credential $Credential
    if ($AuthToken -ne $null) {
        Write-Output -InputObject "Successfully retrieved authentication token"
    }
}
catch [System.Exception] {
    Write-Warning -Message "Failed to retrieve authentication token"
}

# Get Apple VPP tokens
Write-Output -InputObject "Attempting to retrieve Apple VPP tokens"
$AppleVPPResource = "https://graph.microsoft.com/beta/deviceAppManagement/vppTokens"
$AppleVPPTokens = (Invoke-RestMethod -Uri $AppleVPPResource -Method Get -Headers $AuthToken).Value

# Validate tokens
if ($AppleVPPTokens -ne $null) {
    foreach ($AppleVPPToken in $AppleVPPTokens) {
        $AppleVPPExpirationDate = [System.DateTime]::Parse($AppleVPPToken.expirationDateTime)
        if ($AppleVPPExpirationDate -lt (Get-Date)) {
            Write-Output -InputObject "Apple VPP token has already expired"
        }
        else {
            $AppleVPPTokenDaysLeft = ($AppleVPPExpirationDate-(Get-Date))
            Write-Output -InputObject "Apple VPP token expires in days: $($AppleVPPTokenDaysLeft.Days)"
        }
    }
}
else {
    Write-Output -InputObject "Query for Apple VPP tokens returned empty"
}

The code from above, is what we’re going to paste into the runbook that has been created.

  • While still in the editing mode of the runbook, paste the code in and click Save.

  • Click Publish and then Yes in the window that appears.

And that’s it, the runbook has been created and is ready to be executed to see if it’s able to get an authentication token, retrieve data from Graph API and determine if any Apple VPP tokens have expired.

Executing a runbook

The runbook that we’ve created is a quite simple one without any parameter input. As for executing the runbook, there are different ways of accomplishing that task, but for the sake of demonstration we’ll simply just start it through the portal. We could of course create a schedule for the runbook, which I’d recommend if you’re looking implementing something similar to this in your tenant, but for now let’s settle with the manual approach.

  • In the Azure portal under Automation Accounts, select your account and go to Runbooks.
  • Select the newly created runbook.
  • Click Start.

  • Click Yes in the window that appears. You’ll now be transferred to the Job that was manually executed.
  • When the Job status has changed to Completed, click on Output to see what the runbook returned.

Notice the output from the blade on the right side, it should read something like:

Summary

Using Azure Automation for unattended workflow automation using PowerShell for Microsoft Intune sure has many benefits instead of using traditional scheduled tasks and storing of credentials, some of them being encrypted credentials and not requiring any on-premise infrastructure. I hope this post has triggered you to get started with automating Microsoft Intune using Graph API.

Nickolaj Andersen

Chief Technical Architect and Enterprise Mobility MVP since 2016. Nickolaj has been in the IT industry for the past 10 years specializing in Enterprise Mobility and Security, Windows devices and deployments including automation. Awarded as PowerShell Hero in 2015 by the community for his script and tools contributions. Creator of ConfigMgr Prerequisites Tool, ConfigMgr OSD FrontEnd, ConfigMgr WebService to name a few. Frequent speaker at conferences such as Microsoft Ignite, NIC Conference and IT/Dev Connections including nordic user groups.

6 comments

  • Hi Nickolaj,
    I have tried using the code you have provided, I am getting an error when testing the Runbook: Failure to acquire access token. Response with access token was null. So far I am only running: # Import required modules
    try {
    Import-Module -Name AzureAD -ErrorAction Stop
    Import-Module -Name PSIntuneAuth -ErrorAction Stop
    }
    catch {
    Write-Warning -Message “Failed to import modules”
    }

    # Read credentials and variables
    $Credential = Get-AutomationPSCredential -Name “intunebackacc”
    $AppClientID = Get-AutomationVariable -Name “IntuneClientId”

    # Acquire authentication token
    try {
    Write-Output -InputObject “Attempting to retrieve authentication token”
    $AuthToken = Get-MSIntuneAuthToken -TenantName emsmgmt.onmicrosoft.com -ClientID $AppClientID -Credential $Credential
    if ($AuthToken -ne $null) {
    Write-Output -InputObject “Successfully retrieved authentication token”
    }
    }
    catch [System.Exception] {
    Write-Warning -Message “Failed to retrieve authentication token”
    }

    Any ideas why I maybe getting this?

    Any help would be appreciated.

    Thanks
    Sarah

    • Hi Nickolaj, I forgot to mention, I have changed the tenant name, but I am still getting the same error, also the name for variables is intunebackacc and IntuneClientId.
      Thanks Sarah

    • Hi Sarah,

      Yeah, you’ve not changed the tenant name on this row:

      $AuthToken = Get-MSIntuneAuthToken -TenantName emsmgmt.onmicrosoft.com -ClientID $AppClientID -Credential $Credential

      That’s my lab tenant 🙂

      Regards,
      Nickolaj

  • Hi Nickolaj,

    Looks like the following line needs a quick update.. We created ‘IntuneAutomation’; your referencing ‘MSIntuneAutomation’ in the code.
    $Credential = Get-AutomationPSCredential -Name “MSIntuneAutomation”

    Also, I’m not sure if everyone has a Apple VPP token. Suggest on future ones maybe go with the lowest common denominator (Windows).

    Overall, a great intro to automation.. If only MS docs could be as simplified (grr!).

    Cheers,
    Josh

    • Hi Josh,

      You’re absolutely correct, it has now been updated. Thanks for pointing it out. Regarding the scenario, I was thinking about this for some while when writing the post and came to the conclusion that it’s only meant as an idea of what can be performed and that it was not something that (hopefully) no-one else had already shown. But I do get your input and appreciate it and I’ll remember it for future posts.

      Regards,
      Nickolaj

Sponsors