How to get an report with all new enrolled devices

How to Get a Report of All New Enrolled Devices

Keeping track of newly enrolled devices in your organization can be a challenging task when relying solely on the Intune console. Wouldn’t it be awesome to receive a complete report with all new enrolled devices automatically via email? As you know, I love automating things. In this blog post, we’ll explore a simple and efficient way to generate a weekly report with all new enrolled devices using PowerShell, Azure Automation Runbooks, and Microsoft Graph API. This automated solution will save you time and effort, allowing you to focus on more important tasks in managing your organization’s devices. So, let’s dive in and learn how to create this valuable report with all new enrolled devices!

How to get an report with all new enrolled devices

How to get the script for the report with all new enrolled devices

You can find the script that builds the report with all new enrolled devices in my GitHub repository, before running the script you have to adapt the from and to email address. If you enjoy automations like this, check out more of my Intune and automation guides:

How to get an report with all new enrolled devices

What is the purpose of the script?

This PowerShell script builds the report with all new enrolled devices by retrieving every device enrolled in the last 7 days, creating a CSV from them, and attaching the CSV to an email.

How can I schedule the Report?

To schedule the report with all new enrolled devices, you can create an Azure Automation Runbook and authenticate via an App Registration. The only thing that you have to do is to paste the content of the script into the runbook.

How to create the Automation?

Create an App Registration

  • Search for Microsoft Entra ID
How to get an report with all new enrolled devices
  • Select App registration
How to get an report with all new enrolled devices
  • Select +New registration
How to get an report with all new enrolled devices
  • Enter a Name and click Register
How to get an report with all new enrolled devices
  • Click API permissions and +Add a permission
How to get an report with all new enrolled devices
  • Select Microsoft Graph
How to get an report with all new enrolled devices
  • Select Application permissions
How to get an report with all new enrolled devices
  • Search for DeviceManagementApps.Read.All & Mail.Send
How to get an report with all new enrolled devices
  • Click Grant admin consent for *** and approve with Yes
How to get an report with all new enrolled devices
  • Select Certificates & secrets and click +New client secret
How to get an report with all new enrolled devices
  • Enter a Description and select a Expires time
  • Click Add
How to get an report with all new enrolled devices
  • Copy and save the Value and the Secret ID
How to get an report with all new enrolled devices

Create Automation Account

  • Search for Automation Accounts
How to get an report with all new enrolled devices
  • Click + Create
How to get an report with all new enrolled devices
  • Select a Subscription and a Resource group
  • Enter an account name and select a Region
  • Click Next
How to get an report with all new enrolled devices
  • Click Next
How to get an report with all new enrolled devices
  • Click Next -> Next -> Create
How to get an report with all new enrolled devices

Create the Runbook

  • Open the Automation Account
  • Navigate to Variables and click + Add a variable
  • Add the Secret ValueTenantId, and the App ID as Variable
  • How to get an report with all new enrolled devices
  • How to get an report with all new enrolled devices
  • How to get an report with all new enrolled devices
  • How to get an report with all new enrolled devices
  • How to get an report with all new enrolled devices
  • How to get an report with all new enrolled devices
  • Select Runbooks
  • Click + Create a runbook
How to get an report with all new enrolled devices
  • Enter a Name
  • Select PowerShell as Runbook type
  • Select 5.1 as Runtime version
  • Click Create
How to get an report with all new enrolled devices
  • Insert the Script from my Github repository
  • Add the sender and receiver email, in the script
How to get an report with all new enrolled devices
  • Save and test the script
How to get an report with all new enrolled devices
How to get an report with all new enrolled devices
  • Click Publish
How to get an report with all new enrolled devices
  • Navigate to Schedules and click + Add a schedule
How to get an report with all new enrolled devices
How to get an report with all new enrolled devices
  • Click Link to schedule and add the created schedule
How to get an report with all new enrolled devices
How to get an report with all new enrolled devices

Script

<#
Version: 1.0
Author: Jannik Reinhard (jannikreinhard.com)
Script: Get-NewEnrolledDevicesReport
Description:
Get Email with new enrolled devices
Release notes:
Version 1.0: Init
#> 


function Get-AuthHeader{
    param (
        [parameter(Mandatory=$true)]$tenantId,
        [parameter(Mandatory=$true)]$clientId,
        [parameter(Mandatory=$true)]$clientSecret
       )
    
    $authBody=@{
        client_id=$clientId
        client_secret=$clientSecret
        scope="https://graph.microsoft.com/.default"
        grant_type="client_credentials"
    }

    $uri="https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
    $accessToken=Invoke-WebRequest -Uri $uri -ContentType "application/x-www-form-urlencoded" -Body $authBody -Method Post -ErrorAction Stop -UseBasicParsing
    $accessToken=$accessToken.content | ConvertFrom-Json

    $authHeader = @{
        'Content-Type'='application/json'
        'Authorization'="Bearer " + $accessToken.access_token
        'ExpiresOn'=$accessToken.expires_in
    }
    
    return $authHeader
}

#################################################################################################
########################################### Start ###############################################
#################################################################################################
# Variables
$MailSender = "mail@abc.onmicrosoft.com"
$MailTo = "mail@abc.onmicrosoft.com"

# Automation Secrets
$tenantId = Get-AutomationVariable -Name 'TenantId'
$clientId = Get-AutomationVariable -Name 'AppId'
$clientSecret = Get-AutomationVariable -Name 'AppSecret'


$global:authToken = Get-AuthHeader -tenantId $tenantId -clientId $clientId -clientSecret $clientSecret

# Define the time range
$endDate = Get-Date
$startDate = $endDate.AddDays(-7)
$filter = "enrolledDateTime gt $($startDate.ToString("yyyy-MM-ddTHH:mm:ssZ"))&enrolledDateTime le $($endDate.ToString("yyyy-MM-ddTHH:mm:ssZ"))"

# Query Intune devices enrolled in the past week
$graphApiUrl = "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices"
$graphApiQuery = "?`$filter=$filter&`$select=id,deviceName,operatingSystem,enrolledDateTime,userPrincipalName,model"
$uri = $graphApiUrl + $graphApiQuery
$response = Invoke-RestMethod -Method Get -Uri $uri -Headers $global:authToken

# Generate CSV report
$reportPath = "NewEnrolledDevicesReport.csv"
$response.value | Select-Object id,deviceName,operatingSystem,enrolledDateTime,userPrincipalName,model | Export-Csv -Path $reportPath -NoTypeInformation
$csv = [Convert]::ToBase64String([IO.File]::ReadAllBytes(".\$reportPath"))

#Send Mail    
$URLsend = "https://graph.microsoft.com/v1.0/users/$MailSender/sendMail"
$BodyJsonsend = @"
{
    "message": {
      "subject": "New enrolled devices",
      "body": {
        "contentType": "Text",
        "content": "Dear Admin, this Mail contains the enrolled devices from the previous 7 days"
      },
      "toRecipients": [
        {
          "emailAddress": {
            "address": "$MailTo"
          }
        }
      ],
      "attachments": [
        {
          "@odata.type": "#microsoft.graph.fileAttachment",
          "name": "newEnrolledDevicesReport.csv",
          "contentType": "text/plain",
          "contentBytes": "$csv"
        }
      ]
    }
  }
"@


Invoke-RestMethod -Method POST -Uri $URLsend -Headers $global:authToken -Body $BodyJsonsend

Common pitfalls and tips

When you set up the report with all new enrolled devices for the first time, a few small details tend to cause the most trouble. The first one is the API permission. The script reads managed devices, so the app registration needs DeviceManagementManagedDevices.Read.All in addition to Mail.Send, and you have to click Grant admin consent afterwards. If you skip the consent step, the token is issued but every Graph call returns a 403, which can be confusing because authentication itself looks successful.

The second pitfall is the client secret. Secrets expire, so pick a realistic lifetime and add a reminder to rotate it before the expiry date. If your weekly report suddenly stops arriving, an expired secret is the most likely reason. A good practice is to store the secret as an encrypted Automation variable rather than hard-coding it into the runbook, exactly as shown above, so you can update it in one place without editing the script.

Finally, test the runbook manually once before you link it to a schedule. Use the Test pane in the Automation Account to confirm the email is delivered and the CSV attachment opens correctly. Once that works, the weekly schedule is fire-and-forget. If you manage several tenants, simply clone the runbook per Automation Account and adjust the three variables, and you have a clean, repeatable report with all new enrolled devices across your whole estate.

Conclusion

In conclusion, automating the report with all new enrolled devices can save time and effort, ensuring that you stay up-to-date with device management. By following the steps outlined in this post, you can easily set up an automated report with all new enrolled devices that keeps you informed about device enrollment in your organization. Give it a try and experience the benefits of automation firsthand.

Feel free to modify the conclusion as you see fit or let me know if you would like further assistance.

5 thoughts on “How to Get a Report of All New Enrolled Devices

  1. Hi Jannik, thank you for this!
    I am experiencing this error after testing the Runbook,.
    The csv file does exist but it is empty.

    Completed

    The remote server returned an error: (401) Unauthorized.
    Cannot bind argument to parameter ‘InputObject’ because it is null.
    Object reference not set to an instance of an object.
    System.Management.Automation.MethodInvocationException: Exception calling “ReadAllBytes” with “1” argument(s): “Could not find file ‘C:\Windows\System32\NewEnrolledDevicesReport.csv’.” —> System.IO.FileNotFoundException: Could not find file ‘C:\Windows\System32\NewEnrolledDevicesReport.csv’.
    at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
    at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
    at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
    at System.IO.File.InternalReadAllBytes(String path, Boolean checkHost)
    at CallSite.Target(Closure , CallSite , Type , String )
    — End of inner exception stack trace —
    at System.Management.Automation.ExceptionHandlingOps.ConvertToMethodInvocationException(Exception exception, Type typeToThrow, String methodName, Int32 numArgs, MemberInfo memberInfo)
    at CallSite.Target(Closure , CallSite , Type , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    Object reference not set to an instance of an object.

    • Same here: The remote server returned an error: (403) Forbidden.
      System.Management.Automation.MethodInvocationException: Exception calling “ReadAllBytes” with “1” argument(s): “Could not find file ‘C:\Windows\System32\NewEnrolledDevicesReport.csv’.” —> System.IO.FileNotFoundException: Could not find file ‘C:\Windows\System32\NewEnrolledDevicesReport.csv’.
      at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
      at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
      at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
      at System.IO.File.InternalReadAllBytes(String path, Boolean checkHost)
      at CallSite.Target(Closure , CallSite , Type , String )
      — End of inner exception stack trace —
      at System.Management.Automation.ExceptionHandlingOps.ConvertToMethodInvocationException(Exception exception, Type typeToThrow, String methodName, Int32 numArgs, MemberInfo memberInfo)
      at CallSite.Target(Closure , CallSite , Type , String )
      at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
      at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
      at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
      The remote server returned an error: (400) Bad Request.

Comments are closed.