Azure AD / Entra ID : lister tous les groupes d’un utilisateur (PowerShell, CLI, Graph)

Sur un poste Azure AD Join ou Hybrid Join sans accès à l’AD local, whoami /groups n’affiche pas les groupes Azure AD. Voici des méthodes éprouvées, avec scripts réutilisables, pour récupérer rapidement et précisément toutes les appartenances (directes et imbriquées) d’un utilisateur.

Sommaire

Obtenir la liste des groupes d’un utilisateur Azure AD

Problématique

Dans un environnement Active Directory « on‑prem », la commande whoami /groups renvoie la liste complète des groupes (SID résolus) associés au jeton de l’utilisateur connecté. En revanche, lorsque le poste est uniquement attaché au cloud Azure AD (Azure AD Join, ou Hybrid Join sans ligne de vue vers le contrôleur de domaine), whoami /groups ne remonte pas les groupes Azure AD : l’OS n’interroge pas Microsoft Graph pour compléter ce jeton. D’où la question : quel équivalent utiliser pour récupérer ces appartenances ?

Pourquoi whoami /groups ne reflète pas Azure AD

  • Jeton local Windows : il contient des SIDs issus de la machine et, le cas échéant, de l’AD DS. Les groupes Azure AD n’y sont pas nativement injectés.
  • Group claims dans les tokens Azure : côté cloud, les groupes se propagent dans les access tokens/ID tokens (claims groups/roles). Cette information n’est pas exposée par whoami.
  • Azure AD vs Azure AD DS : si vous utilisez Azure AD Domain Services (AAD DS), les groupes répliqués peuvent apparaître via des SIDs AD DS. Mais sur un simple Azure AD Join, ce n’est pas le cas.

Ce que proposait le fil d’origine

Dans le fil mentionné, aucune solution technique n’a été fournie ; l’utilisateur a été redirigé vers Microsoft Q&A. Ci‑dessous, vous trouverez des méthodes concrètes et immédiatement utilisables.

Méthodes recommandées pour récupérer les groupes Azure AD

MéthodeOutil / ModuleExemple de commandePoints clés
Portail AzureAzure PortalAzure Active Directory → Utilisateurs[Nom d’utilisateur]GroupesInterface graphique. Rapide pour un cas ponctuel.
PowerShell Microsoft Graph (recommandé)Microsoft.GraphConnect-MgGraph -Scopes "Directory.Read.All" Get-MgUser -UserId alice@contoso.com | Get-MgUserTransitiveMemberOf -All | Where-Object {$_.OdataType -eq "#microsoft.graph.group"} | Select-Object DisplayName, IdInclut l’appartenance transitive (groupes imbriqués).
PowerShell AzureAD (hérité)AzureADConnect-AzureAD Get-AzureADUser -ObjectId alice@contoso.com | Get-AzureADUserMembershipModule déprécié mais encore utilisable.
Azure CLIazaz ad user get-member-groups \ --id alice@contoso.com \ --security-enabled-only \ --output tableIdéal en environnement multi‑plateformes & CI/CD.
Microsoft Graph API (REST)HTTPSGET /v1.0/users/{user-id}/transitiveMemberOf/microsoft.graph.group?$select=displayName,id,securityEnabled,groupTypesIntégration directe dans vos applis et outils.

Prérequis & autorisations

  • Rôle : au minimum Directory Reader (ou équivalent en privilèges).
  • Scopes (délégués) : souvent Directory.Read.All est le plus simple. Selon vos besoins, une combinaison User.Read.All + Group.Read.All peut suffire.
  • App‑only (application) : Application.Read.All / Group.Read.All en permissions applicatives, avec consentement administrateur.

Étape par étape : PowerShell Microsoft Graph (option privilégiée)

Installation & connexion

# 1) Installer le SDK Microsoft Graph (une fois)
Install-Module Microsoft.Graph -Scope CurrentUser -Repository PSGallery

# 2) Se connecter avec les scopes nécessaires

Connect-MgGraph -Scopes "Directory.Read.All"
Select-MgProfile -Name "v1.0"   # Profil stable

Vérifiez votre contexte courant :

Get-MgContext | Format-List

Lister les groupes d’un utilisateur donné

$upn = "alice@contoso.com"

# Récupère l'utilisateur puis ses appartenances transitives

$user   = Get-MgUser -UserId $upn -Property Id,UserPrincipalName,DisplayName
$groups = Get-MgUserTransitiveMemberOf -UserId $user.Id -All |
Where-Object {$*.OdataType -eq "#microsoft.graph.group"} |
Select-Object @{
Name="UserUPN";       Expression={$user.UserPrincipalName}
}, @{
Name="GroupName";     Expression={$*.AdditionalProperties.displayName}
}, @{
Name="GroupId";       Expression={$*.Id}
}, @{
Name="Security";      Expression={$*.AdditionalProperties.securityEnabled}
}, @{
Name="GroupTypes";    Expression={$_.AdditionalProperties.groupTypes -join ","}
}

$groups | Sort-Object GroupName | Format-Table -AutoSize

Astuce : pour filtrer par type :

  • Groupes Microsoft 365 (Unified) : Where-Object { $_.GroupTypes -match "Unified" }
  • Groupes de sécurité : Where-Object { $_.Security -eq $true }

Version « boîte à outils » (fonction réutilisable)

function Get-AadUserGroups {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$UserPrincipalName,
        [switch]$Transitive = $true
    )

```
$u = Get-MgUser -UserId $UserPrincipalName -Property Id,UserPrincipalName,DisplayName -ErrorAction Stop

$cmd = if ($Transitive) { 'Get-MgUserTransitiveMemberOf' } else { 'Get-MgUserMemberOf' }

& $cmd -UserId $u.Id -All -ErrorAction Stop |
  Where-Object { $_.OdataType -eq "#microsoft.graph.group" } |
  Select-Object @{
      N='UserUPN'; E={$u.UserPrincipalName}
    }, @{
      N='DisplayName'; E={$_.AdditionalProperties.displayName}
    }, @{
      N='Id'; E={$_.Id}
    }, @{
      N='SecurityEnabled'; E={$_.AdditionalProperties.securityEnabled}
    }, @{
      N='GroupTypes'; E={$_.AdditionalProperties.groupTypes -join ","}
    }
```

}

Utilisation :

Get-AadUserGroups -UserPrincipalName "alice@contoso.com" | Out-GridView

Exporter pour un audit massif

$output = "GroupsByUser.csv"
Remove-Item $output -ErrorAction SilentlyContinue

$allUsers = Get-MgUser -All -Property Id,UserPrincipalName

foreach ($u in $allUsers) {
Write-Host "Traitement $($u.UserPrincipalName)..." -ForegroundColor Cyan

Get-MgUserTransitiveMemberOf -UserId $u.Id -All |
Where-Object { $*.OdataType -eq "#microsoft.graph.group" } |
Select-Object @{N='UserUPN';E={$u.UserPrincipalName}},
@{N='GroupName';E={$*.AdditionalProperties.displayName}},
@{N='GroupId';E={$_.Id}} |
Export-Csv -Path $output -Append -NoTypeInformation -Encoding UTF8
}

Write-Host "Export terminé → $output" -ForegroundColor Green

Bonnes pratiques Graph PowerShell

  • Utilisez -All pour paginer automatiquement.
  • Gérez les erreurs et éventuels throttlings (Try/Catch, backoff exponentiel).
  • Limiter les champs avec -Property/$select pour améliorer les performances.

Alternative (héritée) : module AzureAD

Bien que déprécié, il reste présent dans de nombreux environnements.

Install-Module AzureAD -Scope CurrentUser
Connect-AzureAD

Get-AzureADUser -ObjectId "[alice@contoso.com](mailto:alice@contoso.com)" |
Get-AzureADUserMembership |
Where-Object { $_.ObjectType -eq "Group" } |
Select-Object DisplayName, ObjectId

Limites : surface API plus restreinte, évolution stoppée, et compatibilité future non garantie. Privilégiez le SDK Microsoft Graph.

Azure CLI : listing scriptable multi‑plateformes

# Connexion (interactif, device code possible)
az login

# Lister les groupes (GUID)

az ad user get-member-groups 
--id [alice@contoso.com](mailto:alice@contoso.com) 
--security-enabled-only 
--output tsv 

La commande ci‑dessus renvoie des IDs de groupes. Pour obtenir les noms :

az ad user get-member-groups --id alice@contoso.com --security-enabled-only -o tsv \
| while read gid; do
    name=$(az ad group show --id "$gid" --query displayName -o tsv)
    echo -e "$name\t$gid"
  done

Astuce : supprimez --security-enabled-only pour inclure aussi les groupes Microsoft 365 (Unified).

Microsoft Graph (REST) : pour l’intégration applicative

Endpoint recommandé

GET https://graph.microsoft.com/v1.0/users/{user-id}/transitiveMemberOf/microsoft.graph.group?$select=id,displayName,securityEnabled,groupTypes

Le cast /microsoft.graph.group filtre nativement les objets d’annuaire non pertinents (ex. roles, unités d’administration).

Exemple de réponse (tronquée)

{
  "value": [
    {
      "id": "1f3508c8-1c3a-4f3a-9b9d-8c1a0a2f0a11",
      "displayName": "Sec-App-ERP-Readers",
      "securityEnabled": true,
      "groupTypes": []
    },
    {
      "id": "8a8b7b42-95e2-4c21-87fa-50a6307f5f6b",
      "displayName": "Marketing (M365)",
      "securityEnabled": false,
      "groupTypes": ["Unified"]
    }
  ]
}

Permissions minimales (rappels)

  • Déléguées : Directory.Read.All (ou combinaison équivalente couvrant utilisateurs et groupes).
  • Applicatives : Group.Read.All (app‑only) + consentement admin.

Cas du poste Azure AD Join : récupérer les groupes de l’utilisateur connecté

Pour l’utilisateur courant, vous pouvez éviter de fournir un UPN explicite :

# Après Connect-MgGraph
$me = (Get-MgContext).Account
Get-MgUser -UserId $me |
  Get-MgUserTransitiveMemberOf -All |
  Where-Object {$_.OdataType -eq "#microsoft.graph.group"} |
  Select-Object @{N='UserUPN';E={$me}},
                @{N='GroupName';E={$_.AdditionalProperties.displayName}},
                Id

Diagnostic d’appartenance du poste :

dsregcmd /status

Vous verrez notamment AzureAdJoined : YES si la machine est bien jointe à Azure AD.

Séparer les groupes de sécurité des groupes Microsoft 365

Dans Microsoft Graph, deux champs aident à classifier :

  • securityEnabled : true pour un groupe de sécurité.
  • groupTypes : contient Unified pour un groupe Microsoft 365.

Exemple :

$groups = Get-AadUserGroups -UserPrincipalName "alice@contoso.com"

$securityOnly = $groups | Where-Object { $*.SecurityEnabled -eq $true }
$m365Only     = $groups | Where-Object { $*.GroupTypes -match "Unified" }

"Groupes de sécurité : $($securityOnly.Count)"
"Groupes Microsoft 365 : $($m365Only.Count)"

Automatiser sans interaction (identité applicative)

  1. Créer une application (inscription) et lui attribuer un certificat (recommandé) ou un secret.
  2. Accorder les permissions applicatives (ex. Group.Read.All) puis réaliser le consentement administrateur.
  3. Script :
$tenantId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$appId    = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"
$thumb    = "AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 00"

Connect-MgGraph -TenantId $tenantId -ClientId $appId -CertificateThumbprint $thumb

Get-MgUserTransitiveMemberOf -UserId "[alice@contoso.com](mailto:alice@contoso.com)" -All |
Where-Object {$*.OdataType -eq "#microsoft.graph.group"} |
Select-Object Id, @{N='DisplayName';E={$*.AdditionalProperties.displayName}}

Important : en app‑only, l’API n’agit pas « au nom d’un utilisateur », mais en tant qu’application. Veillez à limiter la portée (principe du moindre privilège) et à contrôler l’accès aux exports générés.

Erreurs fréquentes & résolutions

SymptômeCause probableCorrection
Insufficient privileges to complete the operationScopes ou rôles insuffisantsAjouter Directory.Read.All (délégué) ou Group.Read.All (app‑only) et redemander le consentement
Résultats incompletsPagination non géréeAjouter -All (SDK) ou itérer @odata.nextLink (REST)
Groupes en doubleCombinaison de membre direct + imbriquéDédupliquer sur Id (Select-Object -Unique Id)
Nom de groupe manquant en CLIaz ad user get-member-groups renvoie des IDsEnchaîner avec az ad group show --id <guid> pour résoudre le nom
Timeouts / throttlingQuota Graph franchiImplémenter des backoffs, réduire le $select, parallélisme raisonnable

Comparatif rapide des approches

ApprocheTransitiveMulti‑OSAutomatisationSimplicitéCas d’usage type
Portail AzureOuiFaibleTrès simpleVérification ponctuelle
PowerShell Microsoft GraphOuiWindows/Linux/macOS (PowerShell 7)ÉlevéeMoyenneAudit, remédiation, reporting
Azure CLIOuiOuiÉlevéeMoyenneDevOps, CI/CD multi‑plateformes
Microsoft Graph (REST)OuiOuiTrès élevéeTechniqueIntégration applicative
Module AzureAD (hérité)PartielWindowsMoyenneSimpleEnvironnements existants

Aller plus loin : cas particuliers & conseils

  • Groupes imbriqués : préférez toujours la vue « transitive ». Get-MgUserTransitiveMemberOf agrège les appartenances indirectes (groupe A membre de B, etc.).
  • Rôles Azure AD : les rôles (ex. Global Reader) sont des directory roles, pas des groupes. D’où l’intérêt du cast /microsoft.graph.group.
  • Confidentialité : limitez les exports à ce qui est nécessaire (principe du moindre privilège) et sécurisez les fichiers générés.
  • Performance : quand vous bouclez sur des milliers d’utilisateurs, récupérez d’abord tous les Id/UserPrincipalName, puis parallélisez avec un throttle maîtrisé.
  • Terminologie : Azure AD est désormais appelé Microsoft Entra ID. Les commandes et APIs restent identiques côté Graph.

Exemples pratiques prêts à l’emploi

Exporter les groupes (sécurité vs Microsoft 365) pour tous les utilisateurs

Connect-MgGraph -Scopes "Directory.Read.All"
Select-MgProfile v1.0

$output = "GroupsByUser-typed.csv"
Remove-Item $output -ErrorAction SilentlyContinue

$users = Get-MgUser -All -Property Id,UserPrincipalName

foreach ($u in $users) {
$groups = Get-MgUserTransitiveMemberOf -UserId $u.Id -All |
Where-Object { $_.OdataType -eq "#microsoft.graph.group" }

foreach ($g in $groups) {
$name   = $g.AdditionalProperties.displayName
$id     = $g.Id
$isSec  = [bool]$g.AdditionalProperties.securityEnabled
$types  = ($g.AdditionalProperties.groupTypes) -join ","
$kind   = if ($types -match "Unified") { "M365" } elseif ($isSec) { "Security" } else { "Other" }

```
[pscustomobject]@{
  UserUPN    = $u.UserPrincipalName
  GroupName  = $name
  GroupId    = $id
  Category   = $kind
} | Export-Csv $output -Append -NoTypeInformation -Encoding UTF8
```

}
}

Write-Host "Export → $output" -ForegroundColor Green

Afficher uniquement les groupes liés à une appli (pattern)

$pattern = "ERP"
Get-AadUserGroups -UserPrincipalName "alice@contoso.com" |
  Where-Object { $_.DisplayName -like "*$pattern*" } |
  Sort-Object DisplayName |
  Format-Table DisplayName, GroupId -AutoSize

FAQ courte

Q : Puis‑je obtenir l’équivalent exact de whoami /groups sur un Azure AD Join ?
R : Non. whoami interroge le jeton local Windows. Utilisez Microsoft Graph (PowerShell/CLI/REST) pour une vision fiable côté Azure.

Q : La vue « transitive » est‑elle toujours préférable ?
R : Oui pour l’audit/contrôles d’accès, car elle inclut les appartenances via des groupes imbriqués. Pour des scénarios strictement « directs », utilisez Get-MgUserMemberOf.

Q : Quels droits minimums accorder à un analyste ?
R : Le rôle Directory Reader et le scope Directory.Read.All (ou équivalents ciblés) suffisent dans la majorité des cas de lecture.

Conclusion

Même si whoami /groups ne reflète rien d’utile dans un contexte Azure AD pur, vous avez à votre disposition plusieurs méthodes entièrement supportées pour lister les groupes d’un utilisateur : Portail Azure (cas ponctuels), PowerShell Microsoft Graph (préféré pour l’IT), Azure CLI (DevOps multi‑plateformes) et Graph REST (intégration applicative). En combinant l’appartenance transitive, un filtrage par type et un export CSV, vous disposerez d’un inventaire exploitable pour les audits et la remédiation.


Cheat‑sheet (récapitulatif)

# PowerShell (SDK Graph) – utilisateur unique (transitif)
Connect-MgGraph -Scopes "Directory.Read.All"
Get-MgUser -UserId alice@contoso.com |
  Get-MgUserTransitiveMemberOf -All |
  Where-Object {$_.OdataType -eq "#microsoft.graph.group"} |
  Select-Object DisplayName, Id

# Azure CLI – IDs des groupes de sécurité

az ad user get-member-groups --id [alice@contoso.com](mailto:alice@contoso.com) --security-enabled-only -o table

# REST – uniquement des objets de type groupe

GET /v1.0/users/{user-id}/transitiveMemberOf/microsoft.graph.group?$select=id,displayName,securityEnabled,groupTypes

Choisissez la méthode la mieux adaptée à votre flux de travail (GUI, script IT, pipeline DevOps, ou code).

Sommaire