Exchange Online: eseguire operazioni avanzate con Microsoft Graph

In alcuni casi, come ad esempio per un’importazione errata con network upload, si rende necessario effettuare alcune attività di automazione massive (bulk) in Microsoft 365.

Eliminare una determinata cartella da tutte le mailbox presenti in un tenant è uno scenario più comune di quanto sembri e lo utilizzeremo come modello per capire come connetterci con Microsoft Graph tramite PowerShell ed il relativo SDK al fine produrre script di automazione che possano oltrepassare il limite del contesto utente.

Graph PowerShell SDK è solo un wrapper per l’API Graph, che a sua volta non è altro che un’API REST. Un’API REST, nota anche come API RESTful, è un’interfaccia di programmazione delle applicazioni (API o API web) conforme ai vincoli dello stile architetturale REST, che consente l’interazione con servizi web RESTful.

Il repository GitHub di riferimento e supporto per Graph PowerShell SDK è il seguente: microsoftgraph/msgraph-sdk-powershell · GitHub

Connettersi alle API di Microsoft Graph tramite PowerShell

Come primissima cosa è mandatorio installare l’SDK di Graph. L’SDK farà in modo che sia possibile attestarci alle Graph API semplicemente tramite Microsoft PowerShell.

Installiamo quindi il modulo Microsoft.Graph meglio conosciuto come Microsoft Graph Powershell SDK. Da notare che tutti i cmdlet di questo modulo iniziano con “Mg

Install-Module Microsoft.Graph -Scope AllUsers -Force

NB: lo scope AllUsers richiede diritti amministrativi, contrariamente non sono necessari per lo scope CurrentUser.

Un argomento molto importante, con cui è preferibile prendere confidenza quando ci si cimenta nell’utilizzo delle API di Microsoft Graph ,sono gli ambiti anche noti come scope. Se ad esempio un account dispone delle autorizzazioni per un determinato ruolo, non significa che gli venga automaticamente concesso tale ambito.

Verificare l’ambito di un cmdlet

Per questioni di sicurezza, e per evitare di disporre di privilegi eccessivi, è consigliabile utilizzare solo gli ambiti applicabili alla sessione corrente. Ma come si può sapere esattamente quali ambiti sono necessari per i cmdlet che intendiamo usare?

La risposta scontata è naturalmente la documentazione, ma esiste un altro modo probabilmente più efficace anche se dipende molto da come si è abituati: Graph Explorer! Da qui possiamo verificare tutte le autorizzazioni necessarie per ogni query.

Figura 1 – mailFolder permissions

Tutte le autorizzazioni richiedono il consenso per poter essere eseguite. Un amministratore può concedere il consenso per conto dell’intera organizzazione, se necessario, o concedere il consenso ad un’identità individuale.

Connesione a Microsoft Graph

Esistono diversi modi per connettersi a Graph, il più semplice è sicuramente intertactive logon unitamente alle delegated permissions. Interactive logon non è utilizzabile per l’automazione e non di meno non è stato progettato per quello scopo.

Registrazione dell’applicazione in Azure AD

L’utilizzo di un’applicazione registrata in Azure AD, e di un’entità di servizio, è sicuramente la strada da percorrere quando si desidera utilizzare l’SDK per l’automazione. Per impostazione predefinita, quando si crea un’applicazione, viene creata automaticamente un’entità di servizio anche meglio conosciuta come “service principal”.

Ecco i passi principali da seguire per registrare un’applicazione:

  • Connettersi ad Azure AD
  • Passare ad App registrations
  • Cliccare su + New registration
  • Scegliere il nome per l’app, nel nostro esempio “DeleteFoldersApp”
  • Sotto “Supported account types” selezionare la riga che termina con Sigle Tenant
  • Cliccare sul bottone “Register

Figura 2- App Registration

Una volta completato quanto sopra, ci troveremo nella pagina di Overview dell’app. Annotiamo Application (client) ID e Directory (tenant) ID, in quanto ne avremo bisogno in seguito per creare la stringa di connessione.

Figura 3 – Overview App

Autenticazione Certificate Based o Client Secret?

Esistono due metodi per l’autenticazione delle applicazioni. Tali metodi possono avvalersi di un certificato digitale o in alternativa di un segreto client che sostanzialmente potrebbe essere paragonato ad un’entità sulla falsa riga di una password.

Autenticazione su Certificato (Certificate Based Authentication)

Anche se naturalmente l’opzione più sicura è quella di creare un certificato PKIAzure ci consente l’utilizzo di un certificato auto firmato. Indovinate che strumento useremo per generare il certificato necessario?  

Prendiamo come riferimento il codice seguente:

# Certificate parameters definition
$CertParam = @{
    'KeyAlgorithm'      = 'RSA'
    'KeyLength'         = 2048
    'KeyExportPolicy'   = 'NonExportable'
    'DnsName'           = 'windowserver.it'
    'FriendlyName'      = 'DeleteFoldersApp_GraphAPI'
    'CertStoreLocation' = 'Cert:\CurrentUser\My\'
    'NotAfter'          = (Get-Date).AddYears(1)
}
 
# Generate self signed cert 
$AutoCert = New-SelfSignedCertificate @CertParam

# Export Certificate 
Export-Certificate -Cert $AutoCert -FilePath $Home\Desktop\DeleteFoldersApp_GraphAPI.cer 

Il certificato che è appena stato generato, e che troverete sul vostro Desktop, dovrà essere importato tramite upload nella sezione “Certificate e secrets”.

Figura 4- Certificate Import

Autenticazione Client Secret (Client Secret Based Authentication)

Nella medesima schermata, a fianco di Certificates, si trova Client secrets.  Da qui possiamo aggiungere un nuovo client secret.

NB: copiate immediatamente il valore perché agli accessi successivi non verrà più visualizzato.

Figura 5 – Client Secret

Configurare le Autorizzazioni API per l’applicazione

La nostra applicazione, che ricordiamo dovremo usare per scopi di automazione, ora ha bisogno che vengano concesse le autorizzazioni API necessarie. Questo set di autorizzazioni può essere di tipo Application o Delegate. Lo scenario per il quale l’applicazione verrà utilizzata determinerà quale set di autorizzazioni si renderà necessario. Nel nostro caso sarà Mail.ReadWrite

Proseguiamo sempre nella stessa pagina a sinistra, scendiamo e selezioniamo API permissions.  Da qui aggiungiamo un nuovo set di permission. Selezionare Microsoft Graph, poi Application permissions, cerchiamo “mail” e selezioniamo Mail.ReadWrite. Ora è necessario concedere “admin consent” con il pulsante apposito.

Figura 6 – API permission

Connessione a Graph Tramite Certificato Digitale

Nei passaggi precedenti abbiamo registrato l’app in Azure AD ed impostato le autorizzazioni per le Graph API. Ora non dobbiamo fare altro che connetterci a Microsoft Graph tramite PowerShell. Per questo scenario useremo il certificato auto firmato generato nei passi precedenti.

# Graph Connection by certificate 
$ApplicationClientID = "01fc2d4f-xxxx-49c1-8284-1d0527xxxxx"
$DirectoryTenantID = "c9acee48-xxxx-4a89-xxxx-a9f4e558xxxx"
$Certificate = Get-ChildItem Cert:\CurrentUser\My\3850D75690E5B036DF7EDA0142BD35802XXXXX

Connect-Graph -TenantId $DirectoryTenantID -AppId $ApplicationClientID -Certificate $Certificate

Connessione a Graph tramite Client Secret

Lo script seguente invece ci consentirà di attestarci a Graph usando la secret based authentication. Da notare che in questo caso si renderà necessaria l’installazione del modulo MSAL.PS. Il modulo MSAL.PS è scaricabile da Powershell da PSGallery.

# Install MSAL.PS // https://www.powershellgallery.com/packages/MSAL.PS
Import-Module MSAL.PS

# App data 
$ClientSecret= "xxxx~OhENIvmTstB8Y0a.VNFsmSC6YXr-xxxx"  
$ApplicationClientID = "01fc2d4f-xxxx-49c1-xxxx-1d05276cexxx"
$DirectoryTenantID = "c9acee48-xxxx-4a89-xxxx-a9f4e558xxxx"

# Get Msal Token with AppID and related secret  
$MsalToken = Get-MsalToken -TenantId $DirectoryTenantID -ClientId $ApplicationClientID -ClientSecret ($ClientSecret | ConvertTo-SecureString -AsPlainText -Force)

# Connect to Graph using access and client secret token
Connect-Graph -AccessToken $MsalToken.AccessToken

Eliminare una Determinata Cartella da Tutte le Mailbox

Dopo aver argomentato come connetterci a Graph è arrivato il momento di sporcarci le mani di codice PowerShell per trovare una soluzione che possa permetterci di eliminare una determinata cartella da tutte le mailbox presenti in un tenant. A scopo dimostrativo creerò una cartella di nome “Folder1” su alcuni account.

A testimonianza del fatto che quanto si scrive è sempre basato su condizioni reali, eccovi un ritratto di una delle cartelle direttamente da Outlook on the web

Figura 7 – indicazione cartella da eliminare

Dato che di fatto siamo già connessi a Graph con autorizzazioni in scrittura su tutte le mailbox perché non usare PowerShell anche per creare la cartella, che poi cancelleremo, su qualche altra mailbox?

Utilizziamo l’API New-MgUserMailFolder del modulo Microsoft.Graph.Mail per creare una nuova cartella di posta nella cartella radice della cassetta postale dell’utente. Se si desidera nascondere una nuova cartella, è necessario impostare la proprietà isHidden su true

# Create new folder with PS Graph SDK 
New-MgUserMailFolder -UserId gradya@xxx.onmicrosoft.com -DisplayName "Folder1"

Il prossimo passo è estrapolare tutte le mailbox in cui potrebbe essere presente la cartella incriminata. Creiamo un array di dati filtrando ciò che ci interessa: in questo caso solo le mailbox utente.

# Connect to EXO
Connect-ExchangeOnline

# Get all the user mailbox
$MailboxList = Get-Mailbox | Where-Object -FilterScript {$_.RecipientTypeDetails -EQ "UserMailbox"} 

# Filter array with smtp address
$MailboxList | ForEach-Object {Write-Host $_.PrimarySmtpAddress}

Abbiamo appena imparato come creare un array e come filtrarlo. Il prossimo passo potrebbe essere quello di crearci un loop che possa fare la stessa operazione su tutti gli oggetti in cui la variabile $FolderDisplayName indica il nome della cartella che andrà eliminata.

# Get UPN list and folder ID
$MailboxList | ForEach-Object {

    $RootFolderID = (Get-MgUserMailFolder -UserId $_.PrimarySmtpAddress `
                     | Where-Object -FilterScript {$_.DisplayName -EQ $FolderDisplayName} `
                     | Format-Table -hide id `
                     | out-string).trim(); `

Write-Host $_.PrimarySmtpAddress 'con ID' $RootFolderID -ForegroundColor Green
} 
Figura 8 – Folder ID & UPN

C’è però una condizione da rispettare: per poter eliminare correttamente una cartella dobbiamo conoscere il suo ID. La sintassi dell’API Remove-MgUserMailFolder infatti prevede che venga specificato il parametro -MailFolderId come stringa.

Con qualche riga di codice e la potenza di PowerShell e Graph abbiamo ottenuto i vari UPN e relativi ID per la cartella denominata “Folder1”.

Abbiamo il già il loop, non ci resta che aggiungere la parte di codice che eliminerà la cartella “Folder1”, tramite il corrispondente ID, da tutte le mailbox (UPN) contenute nell’array.

Remove-MgUserMailFolder -UserId $_.PrimarySmtpAddress -MailFolderId $RootFolderId; 
Figura 9 – Eliminazione Folder1 da ogni mailbox in cui è presente

Mettendo insieme quanto appreso finora siamo stati in grado di eliminare la cartella di posta “Folder1” da tutte le mailbox in cui era presente, unitamente al suo contenuto! That’s All!

Conclusioni

Nell’articolo abbiamo imparato, avvalendoci un piccolo scenario di esempio o se preferite obiettivo, come Graph unitamente all’SDK di PowerShell si sia rivelato uno strumento davvero potente per scopi di automazione ma non solo! Le API di Microsoft Graph sono davvero numerose, ciò significa che quanto appreso può essere facilmente esteso a tutta la suite di Microsoft 365.

Tags:
Language »