microsoftgraph / powershell-intune-samples

This repository of PowerShell sample scripts show how to access Intune service resources. They demonstrate this by making HTTPS RESTful API requests to the Microsoft Graph API from PowerShell.
MIT License
1.36k stars 656 forks source link

ManagedDevicesHardware-Get stops after 1000 devices #87

Closed thommck closed 3 years ago

thommck commented 5 years ago

I appreciate there are similar issues that reference this issue on other scripts (fix being to use the paging sample https://github.com/microsoftgraph/powershell-intune-samples/tree/master/Paging). However, I can't figure out how to integrate that into the ManagedDevices_Hardware_Get.ps1 script. It would be great to include this limit in the README as I don't think most people coming from PowerShell will understand it. Can you include this by default or add it as a parameter please?

davefalkus commented 5 years ago

The Paging sample shows how to page against managed Devices, you can use the same function in the hardware get script to complete paging. https://github.com/microsoftgraph/powershell-intune-samples/blob/master/Paging/ManagedDevices_Get_Paging.ps1

thommck commented 5 years ago

OK, I have got this working now, it just wasn't clear what bit I should be copying where. Is there a reason you just don't make this the default in the script? Why would you want it to stop at 1000 (or at least flash a big warning saying about the limit).

For others looking at this thread, I replaced the following line (around line 215): (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).Value

with the paging code:

<#  Original code
       (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).Value
#>
<# New code below #>
$DevicesResponse = (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get)

        $Devices = $DevicesResponse.value

        $DevicesNextLink = $DevicesResponse."@odata.nextLink"

            while ($DevicesNextLink -ne $null){

                $DevicesResponse = (Invoke-RestMethod -Uri $DevicesNextLink -Headers $authToken -Method Get)
                $DevicesNextLink = $DevicesResponse."@odata.nextLink"
                $Devices += $DevicesResponse.value
Air-Git commented 5 years ago

There are two things that should be standard in these samples: 1) If you get a collection you need to page through the response 2) If it is not a collection, then the response is the data. If it is a collection, then the response is the metadata, and the collection is response.value.

NFCES commented 4 years ago

I added this code in the ManagedDevices_Hardware_Get.ps1 at line 215 where I do see the old code referenced. However, I am getting an error.

ManagedDevices_Hardware_Get.ps1:246 char:2

alynnik commented 4 years ago

There you are “paging modified” PS script.

<#

.COPYRIGHT Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information.

>

####################################################

function Get-AuthToken {

<# .SYNOPSIS This function is used to authenticate with the Graph API REST interface .DESCRIPTION The function authenticate with the Graph API Interface with the tenant name .EXAMPLE Get-AuthToken Authenticates you with the Graph API interface .NOTES NAME: Get-AuthToken

>

[cmdletbinding()]

param ( [Parameter(Mandatory=$true)] $User )

$userUpn = New-Object "System.Net.Mail.MailAddress" -ArgumentList $User

$tenant = $userUpn.Host

Write-Host "Checking for AzureAD module..."

$AadModule = Get-Module -Name "AzureAD" -ListAvailable

if ($AadModule -eq $null) {

    Write-Host "AzureAD PowerShell module not found, looking for AzureADPreview"
    $AadModule = Get-Module -Name "AzureADPreview" -ListAvailable

}

if ($AadModule -eq $null) {
    write-host
    write-host "AzureAD Powershell module not installed..." -f Red
    write-host "Install by running 'Install-Module AzureAD' or 'Install-Module AzureADPreview' from an elevated PowerShell prompt" -f Yellow
    write-host "Script can't continue..." -f Red
    write-host
    exit
}

Getting path to ActiveDirectory Assemblies

If the module count is greater than 1 find the latest version

if($AadModule.count -gt 1){

    $Latest_Version = ($AadModule | select version | Sort-Object)[-1]

    $aadModule = $AadModule | ? { $_.version -eq $Latest_Version.version }

        # Checking if there are multiple versions of the same module found

        if($AadModule.count -gt 1){

        $aadModule = $AadModule | select -Unique

        }

    $adal = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
    $adalforms = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll"

}

else {

    $adal = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
    $adalforms = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll"

}

[System.Reflection.Assembly]::LoadFrom($adal) | Out-Null

[System.Reflection.Assembly]::LoadFrom($adalforms) | Out-Null

$clientId = "d1ddf0e4-d672-4dae-b554-9d5bdfd93547"

$redirectUri = "urn:ietf:wg:oauth:2.0:oob"

$resourceAppIdURI = "https://graph.microsoft.com"

$authority = "https://login.microsoftonline.com/$Tenant"

try {

$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority

# https://msdn.microsoft.com/en-us/library/azure/microsoft.identitymodel.clients.activedirectory.promptbehavior.aspx
# Change the prompt behaviour to force credentials each time: Auto, Always, Never, RefreshSession

$platformParameters = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" -ArgumentList "Auto"

$userId = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier" -ArgumentList ($User, "OptionalDisplayableId")

$authResult = $authContext.AcquireTokenAsync($resourceAppIdURI,$clientId,$redirectUri,$platformParameters,$userId).Result

    # If the accesstoken is valid then create the authentication header

    if($authResult.AccessToken){

    # Creating header for Authorization token

    $authHeader = @{
        'Content-Type'='application/json'
        'Authorization'="Bearer " + $authResult.AccessToken
        'ExpiresOn'=$authResult.ExpiresOn
        }

    return $authHeader

    }

    else {

    Write-Host
    Write-Host "Authorization Access Token is null, please re-run authentication..." -ForegroundColor Red
    Write-Host
    break

    }

}

catch {

write-host $_.Exception.Message -f Red
write-host $_.Exception.ItemName -f Red
write-host
break

}

}

####################################################

Function Get-ManagedDevices(){

<# .SYNOPSIS This function is used to get Intune Managed Devices from the Graph API REST interface .DESCRIPTION The function connects to the Graph API Interface and gets any Intune Managed Device .EXAMPLE Get-ManagedDevices Returns all managed devices but excludes EAS devices registered within the Intune Service .EXAMPLE Get-ManagedDevices -IncludeEAS Returns all managed devices including EAS devices registered within the Intune Service .NOTES NAME: Get-ManagedDevices

>

[cmdletbinding()]

param ( [switch]$IncludeEAS, [switch]$ExcludeMDM )

Defining Variables

$graphApiVersion = "beta" $Resource = "deviceManagement/managedDevices"

try {

$Count_Params = 0

if($IncludeEAS.IsPresent){ $Count_Params++ }
if($ExcludeMDM.IsPresent){ $Count_Params++ }

    if($Count_Params -gt 1){

    write-warning "Multiple parameters set, specify a single parameter -IncludeEAS, -ExcludeMDM or no parameter against the function"
    Write-Host
    break

    }

    elseif($IncludeEAS){

    $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource"

    }

    elseif($ExcludeMDM){

    $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource`?`$filter=managementAgent eq 'eas'"

    }

    else {

    $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource`?`$filter=managementAgent eq 'mdm' and managementAgent eq 'easmdm' and managementAgent eq 'googleCloudDevicePolicyController'"
    Write-Warning "EAS Devices are excluded by default, please use -IncludeEAS if you want to include those devices"
    Write-Host

    }

    <#  Original code
   (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).Value

>

<# New code below #> $DevicesResponse = (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get)

    $Devices = $DevicesResponse.value

    $DevicesNextLink = $DevicesResponse."@odata.nextLink"

        while ($DevicesNextLink -ne $null){

            $DevicesResponse = (Invoke-RestMethod -Uri $DevicesNextLink -Headers $authToken -Method Get)
            $DevicesNextLink = $DevicesResponse."@odata.nextLink"
            $Devices += $DevicesResponse.value
}
$Devices
}
catch {

$ex = $_.Exception
$errorResponse = $ex.Response.GetResponseStream()
$reader = New-Object System.IO.StreamReader($errorResponse)
$reader.BaseStream.Position = 0
$reader.DiscardBufferedData()
$responseBody = $reader.ReadToEnd();
Write-Host "Response content:`n$responseBody" -f Red
Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
write-host
break

}

}

####################################################

region Authentication

write-host

Checking if authToken exists before running authentication

if($global:authToken){

# Setting DateTime to Universal time to work in all timezones
$DateTime = (Get-Date).ToUniversalTime()

# If the authToken exists checking when it expires
$TokenExpires = ($authToken.ExpiresOn.datetime - $DateTime).Minutes

    if($TokenExpires -le 0){

    write-host "Authentication Token expired" $TokenExpires "minutes ago" -ForegroundColor Yellow
    write-host

        # Defining User Principal Name if not present

        if($User -eq $null -or $User -eq ""){

        $User = Read-Host -Prompt "Please specify your user principal name for Azure Authentication"
        Write-Host

        }

    $global:authToken = Get-AuthToken -User $User

    }

}

Authentication doesn't exist, calling Get-AuthToken function

else {

if($User -eq $null -or $User -eq ""){

$User = Read-Host -Prompt "Please specify your user principal name for Azure Authentication"
Write-Host

}

Getting the authorization token

$global:authToken = Get-AuthToken -User $User

}

endregion

####################################################

$ExportPath = Read-Host -Prompt "Please specify a path to export Managed Devices hardware data to e.g. C:\IntuneOutput"

# If the directory path doesn't exist prompt user to create the directory

if(!(Test-Path "$ExportPath")){

Write-Host
Write-Host "Path '$ExportPath' doesn't exist, do you want to create this directory? Y or N?" -ForegroundColor Yellow

$Confirm = read-host

    if($Confirm -eq "y" -or $Confirm -eq "Y"){

    new-item -ItemType Directory -Path "$ExportPath" | Out-Null
    Write-Host

    }

    else {

    Write-Host "Creation of directory path was cancelled..." -ForegroundColor Red
    Write-Host
    break

    }

}

Write-Host

####################################################

$Devices = Get-ManagedDevices

if($Devices){

$Results = @()

foreach($Device in $Devices){

$DeviceID = $Device.id

Write-Host "Device found:" $Device.deviceName -ForegroundColor Yellow
Write-Host

$uri = "https://graph.microsoft.com/beta/deviceManagement/manageddevices('$DeviceID')?`$select=hardwareinformation,iccid,udid,ethernetMacAddress"

$DeviceInfo = (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get)

$DeviceNoHardware = $Device | select * -ExcludeProperty hardwareInformation,deviceActionResults,userId,imei,manufacturer,model,isSupervised,isEncrypted,serialNumber,meid,subscriberCarrier,iccid,udid,ethernetMacAddress
$HardwareExcludes = $DeviceInfo.hardwareInformation | select * -ExcludeProperty sharedDeviceCachedUsers,phoneNumber
$OtherDeviceInfo = $DeviceInfo | select iccid,udid,ethernetMacAddress

    $Object = New-Object System.Object

        foreach($Property in $DeviceNoHardware.psobject.Properties){

            $Object | Add-Member -MemberType NoteProperty -Name $Property.Name -Value $Property.Value

        }

        foreach($Property in $HardwareExcludes.psobject.Properties){

            $Object | Add-Member -MemberType NoteProperty -Name $Property.Name -Value $Property.Value

        }

        foreach($Property in $OtherDeviceInfo.psobject.Properties){

            $Object | Add-Member -MemberType NoteProperty -Name $Property.Name -Value $Property.Value

        }

    $Results += $Object

    $Object

}

$Date = get-date

$Output = "ManagedDeviceHardwareInfo_" + $Date.Day + "-" + $Date.Month + "-" + $Date.Year + "_" + $Date.Hour + "-" + $Date.Minute

# Exporting Data to CSV file in provided directory
$Results | Export-Csv "$ExportPath\$Output.csv" -NoTypeInformation
write-host "CSV created in $ExportPath\$Output.csv..." -f cyan

}

else {

write-host "No Intune Managed Devices found..." -f green Write-Host

}