programmer holding a paper cutout with an api quote

Trovare PST e molto altro con Intune

Dopo il mio articolo precedente sulla ricerca dei file PST presenti nei computer Windows della nostra rete o del nostro dominio, ho deciso di estendere il lavoro fatto, rendendolo compatibile anche con le soluzioni di management moderno degli endpoint, nel mio caso Intune.

Lo script precedente infatti poteva essere eseguito solo su computer connessi nella stessa rete: ora possiamo ottenere lo stesso risultato via Intune ed avere un pratico report a disposizione che includa dati dei nostri dispositivi ovunque siano, purché connessi ad internet e gestiti da Intune.

Un esempio del report ottenuto, in questo caso in formato OGV (Out-GridView)

Non possiamo trovare solo PST, e questa credo sia la cosa più importante, ma – usando il sistema qui descritto – possiamo ottenere quasi qualunque tipo di output di script in PowerShell eseguito via Intune sui nostri client (l’esempio più semplice che mi viene in mente è un inventario custom). Modificando infatti lo script che recupera l’output, possiamo estrarre informazioni molto preziose dai nostri endpoint sfruttando le API di Graph di Microsoft.

Avvertenza: un risultato simile (anche se non proprio lo stesso) può essere ottenuto facilmente anche con le Remediations in Intune, ma le licenze richieste non rendono molto popolare questa utilissima feature, almeno nel mondo SMB italiano.

L’idea di sfruttare Graph per recuperare l’output di uno script eseguito con Intune mi è venuta grazie ad un bellissimo articolo di Michael Griswold, che mi ha dato lo spunto giusto. In sostanza, quello che andremo a fare è il seguente:

  • Fare il deploy via Intune dello script per la ricerca dei PST nel computer, opportunamente modificato.
  • Annotare l’id dello script lanciato via Intune.
  • Sulla nostra workstation, eseguiremo lo script che si collega alla API di Graph via PowerShell, dopo aver inserito l’id dello script del punto precedente.
  • Lo script recupera con una HTTP GET una response in formato JSON contente l’output dello script, contenuto nel campo resultMessage.
  • Lo script si occupa quindi di manipolare e filtrare la response JSON, convertendola poi in oggetti PowerShell, che usiamo poi per costruire il nostro report.

Il formato JSON dei dati forniti da Graph può presentare qualche difficoltà, come degli array nested oppure la presenza del carattere di escape “\” nei path dei file Windows, che in qualche modo deve essere gestita. Il sistema però funziona bene e, soprattutto, si presta ad essere esteso facilmente per altri usi, che non siano solo la ricerca di PST.

Se dobbiamo ottenere una qualche info dai nostri client, basta solo creare uno script su Intune che cerchi le info richieste e che – cosa fondamentale – produca un qualche tipo di output. Fatto questo, si può modificare lo script che recupera da Graph le info, chiedendogli di manipolare diversamente la response JSON, a seconda dell’output contenuto.

Requisiti:

Script da distribuire via Intune:

Veniamo però agli script, iniziando da quello da distribuire con Intune per la ricerca dei PST (lo trovate anche su GitHub):

#region Credits
# Author: Federico Lillacci - Coesione Srl - www.coesione.net
# GitHub: https://github.com/tsmagnum
# Version 1.1 - Path string splitted
# Date: 04/03/2024
# 
# The script searches for PST files in the computer
#
#endregion

#region TODO

#endregion

#creating an empty array for the results
$results = @()

#performing the search
$pstFiles = Get-Wmiobject -namespace "root\CIMV2" -Query "Select * from CIM_DataFile Where Extension = 'pst'"

#storing the results in PS Object
Foreach ($file in $PstFiles)
{
    $result = [PSCustomObject]@{
        Computer = $file.CSName
        Name = $file.Filename
        Path = $file.Description.Split("\")
        Size = ($file.FileSize)/1GB
        LastAccess = ($file.LastAccessed.Split("."))[0]
    }  

    $results += $result

}

#outputting the results, converted to JSON format
$results | ConvertTo-Json 

Configurate in questo modo i parametri per l’esecuzione dello script (no credenziali utente loggato, no signature check, esecuzione a 64 bit):

Dopo aver creato lo script in Intune, annotiamoci l’id dello script, ci servirà dopo per eseguire lo script che recupera i dati da Graph (basta copiare una parte della URL della pagina dello script in Intune):

Script per ottenere i dati dalla API di Graph:

Una volta che lo script avrà girato sui client, dovremo prendere la nostra workstation, installare il modulo di PowerShell per collegarsi a Intune con Graph (vedi sopra) ed eseguire lo script seguente (anche questo recuperabile anche su GitHub), dopo aver valorizzato la variabile $scriptId con l’id annotato prima:

#region Credits
# Author: Federico Lillacci - Coesione Srl - www.coesione.net
# GitHub: https://github.com/tsmagnum
# Version: 1.1
# Date: 06/03/2024
#
#
# Thanks to Micheal Griswold
# https://techcommunity.microsoft.com/t5/device-management-in-microsoft/how-to-collect-custom-inventory-from-azure-ad-joined-devices/ba-p/2280850
#endregion

#region TODO

#endregion

#scriptId variable - this needs to be set before running the script!
#$scriptId = "ddf14782-b75e-4479-afae-779e0b248e6f" WARNING: This is just an example
$scriptId = "" 

#checking if $scriptId is present; exiting if it's null
if (!$scriptId) 
    {
        Write-Host -ForegroundColor Red 'Error: $scriptId variable is null, cannot proceed'
        Write-Host -ForegroundColor Yellow "Please set this variable before running the script again"
        exit
    }

#connecting to MS Graph
Update-MSGraphEnvironment -SchemaVersion 'beta'
Connect-MSGraph

#creating two empty arryas to store the results
$jsonArray = @()
$resultsArray = @()

#setting the Graph url
$graphUrl = "deviceManagement/deviceManagementScripts/$($scriptId)"+
    '/deviceRunStates?$expand=managedDevice($select=deviceName)&$select=lastStateUpdateDateTime,errorCode,resultMessage'

#making the Graph request and converting to a PS Object the JSON results contained in the resultMessage
$graphResult = Invoke-MSGraphRequest -HttpMethod GET -Url $graphUrl | Get-MSGraphAllPages

foreach ($result in $graphResult)
{
    $deserializedObj = $result.resultMessage | ConvertFrom-Json

    $jsonArray += $deserializedObj
}

#nested looping through the array of results converted from JSON:
#inside the main array, we have a nested array when a computer contains more than a PST file
for ($i = 0; $i -lt $jsonArray.Length; $i++)
{
    #creating the PstFilePath property from the filePath array
    #this step is needed to avoid the problem with the "\" character in the JSON response
    foreach ($item in $jsonArray[$i])
        {
                $filePath = $item.Path -join "\" 
                $item | Add-Member -Name PstFilePath -MemberType NoteProperty -Value $filePath -ErrorAction SilentlyContinue
                $item.PSObject.Properties.Remove("Path")

                #storing the final results in the results array
                $resultsArray += $item
        }
}

#final results output
#default format, console output
$resultsArray | Format-Table

#uncomment get the results in ogv
#$resultsArray | Out-GridView

#uncomment to export to CSV; modify the path accordingly
#$resultsArray | export-csv -NoTypeInformation -Path pstFiles.csv

#how many computer were processed
Write-Host -ForegroundColor Cyan "Script eseguito su $($graphResult.Count) computer"

A questo punto otterremo un report nel formato desiderato, a seconda di quanto scelto nelle linee 67-75: il default è a console, ma si può facilmente passare in OGV o esportare tutto in CSV.

Esempio di report a console

Prossimamente probabilmente tornerò su questo script: Graph è un tool molto promettente e insieme ad Intune può davvero semplificare la vita di noi admin.