Get-MgUser: How to Get and Export Azure AD users with PowerShell

Starting from Dec 2022 we will need to use the Get MgUser cmdlet to get and export our Azure AD users. Get-MgUser is part of the Microsoft Graph SDK for PowerShell. It allows us to interact with all Microsoft Services through a single endpoint.

All the user accounts in your Microsoft 365 tenant are stored in the Azure Active Directory. We can manage them through the admin center or Azure Portal, but sometimes is PowerShell more convenient.

In this article, I am going to explain how we can use the Get MgUser cmdlet to find and retrieve user information from the Azure AD. I have also created a complete script that allows you to export all your users to CSV, which you will find at the end of the article.

Getting started with Get-MgUser

Before we can start, make sure that you have installed the Microsoft Graph Module in PowerShell. In this article, you can find a complete guide on how to install the module.

The Get MgUser cmdlet allows you to find and extract user information from the Azure Active Directory. There are a couple of parameters that we can use to find or filter the users:

  • UserId – Return specific user based on UPN or ObjectID
  • Filter – Retrieve multiple objects based on a oDate v3 query
  • Search – Get all users that match the searchString
  • Top – Return n number of results
  • All – Return all results (by default the first 100 items are returned)

Good to know is the cmdlet returns only the first 100 results by default. So make sure that you use the -all parameter to get all results when needed.

So the first step is to connect the Microsoft Graph with the correct scope. We are only going to retrieve user data, so we can use the User.Read.All scope.

Connect-MgGraph -Scopes 'User.Read.All'

To test if the cmdlet is working you can simply get all users from your Azure Active Directory with the following cmdlet:

Get-MgUser -All

To get a single user we can use the UserId of the user. This can either be the UserPrincipalName of the user or the actual user id:

# Get the user by the UserPrincipalName
Get-MgUser -UserId adelev@lazydev.onmicrosoft.com

# Get the user by the actual id:
Get-MgUser -UserId 7a3b301d-0462-41b6-8468-19a3837b8ad1

Using Filters with Get-MgUser

Just like with the Get-AzureAduser cmdlet we can filter the users. The filter is based on the oDate v3 query, but not all operators are supported. We can only use the following operators to filter to users:

OperatorDescriptionExample
eqEquals tojobtitle eq ‘Marketing Assistant’
andAndjobtitle eq ‘Recruiter’ and jobtitle eq ‘hr’
orOrjobtitle eq ‘Recruiter’ or jobtitle eq ‘hr’
startswithString starts withstartswith(jobtitle,’recr’)
Get-MgUser Filters

Important is that you wrap the filter query in double-quotes and the string that you want to filter on in single-quotes. Only when you filter on a boolean you don’t need to put quotes around the true or false statement.

So let’s take a look at a couple of examples using the -filter parameter. To find a user by the display name we can specify the complete name of the user or use the startsWith operator. Keep in mind that we can’t use wildcard or the -like operator here.

# Find the user based on the full name
Get-MgUser -Filter "DisplayName eq 'Adele Vance'"

# Find the user by the first part of the name
Get-MgUser -Filter "startsWith(DisplayName, 'A')"
Get-MgUser filter
Find user by name

The same method can also be used to get all users with the job title “Marketing Assistant” for example:

Get-MgUser -Filter "jobtitle eq 'Marketing Assistant'"

To get for example only the enabled user accounts with the Get-MgUser cmdlet we can use the following command:

Get-MgUser -Filter 'accountEnabled eq true' -All

Note that I have added the -all parameter. By default, the Get MgUser cmdlet only returns the first 100 results. By adding the -all parameter we get all the results returned.

We can also filter on multiple conditions, by using and or or in the filter query:

Get-MgUser -Filter "department eq 'Marketing' and jobtitle eq 'Manager'"

Another example that I would like to share is the option to get all users that are created last year. For this, we can filter the CreatedDateTime property:

Get-MgUser -Filter "CreatedDateTime ge $((Get-Date).AddYears(-1).ToString("s"))Z"

Besides the filter parameter, we can also use the -Search parameter to find users. The parameter requires a property that you want to search on and a value. You will also need to set the -consistencylevel to eventual.

The advantage of the -search parameter is that it allows us to search on any part of the value. So for example, if want to search on a part of the name we can use :

Get-MgUser -Search 'DisplayName:van' -ConsistencyLevel eventual
Get-MgUser Search Parameter

Also, note that you will need to wrap the search query in single quotes. You can use the search parameter on pretty much all the fields that are returned by the Get-MgUser cmdlet.

Combining Search and Filter in Get MgUser

We can also combine the -search and -filter parameters in one command. This allows us to search on a name or job title among enabled accounts only for example:

Get-MgUser -Filter 'accountEnabled eq true' -Search 'DisplayName:van' -ConsistencyLevel eventual

Selecting Properties to Return

When using the Get-MgUser cmdlet you have probably noticed that it doesn’t return a lot of properties by default. Most fields are empty or contain the value Microsoft.Graph.PowerShell.Models. followed by an entity name. So how do we retrieve this data?

There are two parameters that we can use to get the information that we need, -property and -expandproperty.

The -property parameter is just like the select that we can pipe behind the cmdlet. The only difference is that with select, all data is returned by Microsoft Graph and filtered out in PowerShell.

Get-MgUser -UserId adelev@lazydev.onmicrosoft.com | Select DisplayName,BusinessPhones,Mail,JobTitle

DisplayName BusinessPhone     Mail                           JobTitle
----------- -------------     ----                           --------
Adele Vance {+1 425 555 0109} AdeleV@lazydev.onmicrosoft.com Retail Manager

When you use the property parameter, only the data that you need is returned by Microsoft Graph. Especially when working with a lot of records this will be faster than using select.

Get-MgUser -UserId adelev@lazydev.onmicrosoft.com -Property DisplayName,BusinessPhones,Mail,JobTitle | fl

If you output the results to a list, you will see that only the fields that you have selected with the property parameter contain data.

Expanding Microsoft.Graph.PowerShell.Models

When viewing the properties of a user you may have noticed that some of them contain the value Microsoft.Graph.PowerShell.Models followed by a resource name. These models (or resources) are relationships of the resource type that you are viewing.

These relationships should allow us to easily view the related data of the resource. If we take the user, we may want to know the manager of it.

To do this we will need to expand the properties and select the correct parameter of the manager object:

Get-MgUser -UserId adelev@lazydev.onmicrosoft.com -ExpandProperty manager | Select @{Name = 'Manager'; Expression = {$_.Manager.AdditionalProperties.displayName}}

# Result:
Manager
-------
Miriam Graham

The challenge with those relationships is to find the correct field and permissions that you need. We are now connected to Microsoft Graph with only the User.Read.All scope. If we for example want to view the working hours of a user in the mailbox settings, then we first need to find and add the correct scope to our session.

To find the correct permission I like to use the Rest API documentation. If you expand a resource it will show the relationships of the resource. You can then click on the type which will take you to the related resource where you can find the required permissions.

So for the mailbox settings, we will need to add the MailboxSettings.Read permissions:

Connect-MgGraph -Scopes "User.Read.All","MailboxSettings.Read"

And then we can select the property and nested values:

Get-MgUser -UserId meganb@lazyadmin.nl -Property MailboxSettings | Select @{Name = 'days'; Expression = {$_.MailboxSettings.WorkingHours.DaysofWeek}}

Script to get all Azure AD Users and Export to CSV

I have created a complete script that allows you to get and export all Azure Active Directory users to a CSV file. There are a couple of options when you run the script:

  • Get the manager of the user – Default True
  • Get enabled, disabled or both user accounts – Default True
  • Set the export path for the CSV file – By default the location of the script

You can download the latest version of the script here from my GitHub repository. To run the script, simply navigate to the script location and run:

.\Get-MgUsers.ps1 -path c:\temp\users.csv

If you want to know more on how to run a script, or how to add your script location to your PowerShell profile, then make sure that you read this article.

<#
.SYNOPSIS
  Get all Azure AD Users using Microsoft Graph with properties and export to CSV

.DESCRIPTION
  This script collects all Azure Active Directory users with the most important properties. By default it will only
  get the enabled users, manager of the user and searches the whole domain.

.OUTPUTS
  CSV with Azure Active Directory Users

.NOTES
  Version:        1.0
  Author:         R. Mens - LazyAdmin.nl
  Creation Date:  15 feb 2022
  Purpose/Change: Initial script development

.EXAMPLE
  Get all AzureAD users from the whole Domain

   .\Get-MgUsers.ps1 -path c:\temp\users.csv

.EXAMPLE
  Get enabled and disabled users

   .\Get-MgUsers.ps1 -enabled both -path c:\temp\users.csv

   Other options are : true or false

.EXAMPLE
  Don't lookup the managers display name
  .\Get-MgUsers -getManager:$false -path c:\temp\users.csv
#>


param(
  [Parameter(
    Mandatory = $false,
    HelpMessage = "Get the users manager"
  )]
  [switch]$getManager = $true,

  [Parameter(
    Mandatory = $false,
    HelpMessage = "Get accounts that are enabled, disabled or both"
  )]
    [ValidateSet("true", "false", "both")]
  [string]$enabled = "true",

  [Parameter(
    Mandatory = $false,
    HelpMessage = "Enter path to save the CSV file"
  )]
  [string]$path = ".\ADUsers-$((Get-Date -format "MMM-dd-yyyy").ToString()).csv"
)


Function Get-Users {
    <#
    .SYNOPSIS
      Get users from the requested DN
    #>
    process{
      # Set the properties to retrieve
      $properties = @(
        'id',
        'DisplayName',
        'userprincipalname',
        'mail',
        'jobtitle',
        'department',
        'OfficeLocation',
        'MobilePhone',
        'BusinessPhones',
        'streetAddress',
        'city',
        'postalcode',
        'state',
        'country',
        'AccountEnabled',
        'CreatedDateTime'
      )

      If (($getManager.IsPresent)) {
        # Adding additional properties for the manager
        $select = $properties += @{Name = 'Manager'; Expression = {$_.Manager.AdditionalProperties.displayName}}
        $select += @{Name ="Phone"; Expression = {$_.BusinessPhones}} 
      }else{
        $select = $properties
      }

      # Get enabled, disabled or both users
      switch ($enabled)
      {
        "true" {$filter = 'AccountEnabled eq true'}
        "false" {$filter = 'AccountEnabled eq false'}
        "both" {$filter = ''}
      }

      # Get the users
      Get-MgUser -Filter $filter -Property $properties -ExpandProperty Manager | select $select
    }
}


Function Get-AllMgUsers {
  <#
    .SYNOPSIS
      Get all AD users
  #>
  process {
    Write-Host "Collecting users" -ForegroundColor Cyan

    # Collect and loop through all users
    Get-Users | ForEach {

      [pscustomobject]@{
        "Name" = $_.DisplayName
        "UserPrincipalName" = $_.UserPrincipalName
        "Emailaddress" = $_.mail
        "Job title" = $_.JobTitle
        "Manager" = $_.Manager
        "Department" = $_.Department
        "Office" = $_.OfficeLocation
        "Phone" = $_.Phone
        "Mobile" = $_.MobilePhone
        "Enabled" = if ($_.AccountEnabled) {"enabled"} else {"disabled"}
        "Street" = $_.StreetAddress
        "City" = $_.City
        "Postal code" = $_.PostalCode
        "State" = $_.State
        "Country" = $_.Country
        "Account Created on" = $_.CreatedDateTime
      }
    }
  }
}

# Check if MS Graph module is installed
if (Get-InstalledModule Microsoft.Graph) {
  # Connect to MS Graph
  Connect-MgGraph -Scopes "User.Read.All"
}else{
  Write-Host "Microsoft Graph module not found - please install it" -ForegroundColor Black -BackgroundColor Yellow
  exit
}

Get-AllMgUsers | Sort-Object Name | Export-CSV -Path $path -NoTypeInformation

if ((Get-Item $path).Length -gt 0) {
  Write-Host "Report finished and saved in $path" -ForegroundColor Green

  # Open the CSV file
  Invoke-Item $path

}else{
  Write-Host "Failed to create report" -ForegroundColor Red
}

Wrapping Up

The Microsoft Graph SDK for PowerShell is still pretty new and not well documented at the moment of writing. So it can be a bit of a challenge to find the correct syntax or cmdlet to get the information that you need.

I hope this article helped you to get started with the Get MgUser cmdlet. If you have any questions just let me know in the comments below.

31 thoughts on “Get-MgUser: How to Get and Export Azure AD users with PowerShell”

  1. Hello Rudy,

    I want to pull this set of values into csv.file from azure user directory,
    manager,
    companyName,
    createdDateTime,
    department,
    displayName,
    employeeId,
    givenName,
    jobTitle,
    lastPasswordChangeDateTime,
    mail,
    mobilePhone,
    officeLocation,
    onPremisesDistinguishedName,
    onPremisesExtensionAttributes,
    onPremisesSamAccountName,
    proxyAddresses,
    surname,
    userPrincipalName,
    userType,
    country,
    state,
    accountExpires,
    whenChanged,
    userAccountControl,

    Please help me with powershell script.

      • I have used your script but i am not able to pull some values, [whenChanged,
        accountExpires,userAccountControl] these attributes are not present in mgattributes but previouly those we are able to pull using “aduser” form activedirectory now it is not supported. Please guide me to extrace those attirbutes. Thanks for the script.

  2. Hi,
    Excellent article! Is there a way to return the user Id along with one of the extensionAttributes?
    For example, I would like to store in a variable the Id of each user with their extensionAttribute10 value for comparison reasons.
    Thank you!

  3. is there a way to have stored credentials for Azure as a secured xml file or something so when this is run it doesn’t prompt for credentials?

    • You could look at the ConvertTo-SecureString cmdlet to store the password. But keep in mind that this isn’t 100% secure. If someone gains access to the file they could gain administrator access. So when taking this approach, I recommend to create an unique account for the script and limit the permissions as much a possible.

  4. I need to get email or UPN for over 2,000 user starting with a csv file containing their display names (from Dataverse, CoE). Here’s what doesn’t work (with error messages):

    foreach ($own in import-csv -path c:\Users\580616\DistinctOwners.csv) {(get-mguser -consistencylevel eventual -filter “(displayname eq $own.owner)”)}
    Get-MgUser_List1: Invalid filter clause

    foreach ($own in import-csv -path c:\Users\580616\DistinctOwners.csv) {(get-mguser -consistencylevel eventual -filter (displayname eq “$own.owner”))}
    displayname: The term ‘displayname’ is not recognized as a name of a cmdlet, function, script file, or executable program.

    But this reads and writes my csv file fine:
    foreach ($own in import-csv -path c:\Users\580616\DistinctOwners.csv) {(echo “$own.owner”)}

    And this works fine for one user (without the csv):
    get-mguser -consistencylevel eventual -filter “(displayname eq ‘Kushner, Rick [USA]’)”

  5. Great work, Rudy
    The move to Graph is a necessity, so your info is a valuable resource!
    Highly recommend adding the Graph x-ray dev tool to your browser.
    From the portal gui, gives you an equivalent Graph command in a side window that you can try.
    Graph explorer is also a goldmine of info for this.

    Thank you for your efforts helping the IDAM community!

    Chris

  6. How about filtering based on a list in a .csv?
    foreach ($own in import-csv -path c:\Users\580616\DistinctOwners.csv) {(get-mguser -consistencylevel eventual -filter “displayname eq ‘$own.owner'”)}
    … or …
    foreach ($own in import-csv -path c:\Users\580616\DistinctOwners.csv) {(get-mguser -consistencylevel eventual -filter (displayname eq “$own.owner”))}
    … or …
    foreach ($own in import-csv -path c:\Users\580616\DistinctOwners.csv) {(get-mguser -consistencylevel eventual -filter “(displayname eq $own.owner)”)}

    None of them work!!!

    • Here it is!
      foreach ($own in import-csv -path c:\Users\580616\DistinctOwners.csv) {(get-mguser -consistencylevel eventual -filter “(displayname eq ‘$($own.owner)’)”)}

  7. Additional to this, is there a way to pull in the email or UserPrincipalName for a Manager
    $select = $properties += @{Name = ‘Manager’; Expression = {$_.Manager.AdditionalProperties.displayName}}

    I tried replacing displayName but then there is no value written at all.

    • A couple of options:
      get-mguser -UserId ruud@lazyadmin.nl -ExpandProperty manager | Select @{Name = 'Manager'; Expression = {$_.Manager.AdditionalProperties.mail }}

      or

      $user = get-mguser -UserId rudymens@thunnissen.nl -ExpandProperty manager
      $user.Manager.AdditionalProperties.mail

  8. Great script! Thank you for providing it. It’s a good foundation that I can build-on, as requirements change. I suggest just one change though. Since the script is described to get all AAD users, add “-All” to line 101.

  9. Great script and really insightful.

    Do you know if it is possible to filter the user list so that only user accounts where the employeeId extension attribute is populated with a value are returned, and all other user accounts are ignored and not in the CSV file?
    I’m a Powershell newbie, and cant figure how to do that in PS using the Graph SDK with the limiting filtering options.

    • You will need to first select all users, and then run a where filter on them. The attributes are in the OnPremisesExtensionAttributes parameter which you will need to expand first:

      Get-MgUser -All | Select -ExpandProperty OnPremisesExtensionAttributes

  10. Hi, another question that I can’t seem to find how to get OtherEmails field. if I try to request it as a property, I get System.String[] and if I do “get-mgUser -ExpandProperty OtherEmails”
    I get:
    get-mgUser : Parsing OData Select and Expand failed: Could not find a property named ‘OtherEmails’ on type ‘microsoft.graph.user’.
    At line:1 char:1
    + get-mgUser -ExpandProperty OtherEmails
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: ({ ConsistencyLe…stem.String[] }:f__AnonymousType54`9) [Get-MgUser_List], RestException`1
    + FullyQualifiedErrorId : BadRequest,Microsoft.Graph.PowerShell.Cmdlets.GetMgUser_List

    Any ideas where to find it?

  11. Hi – great write up and fully agree the lack of documentation is a big hurdle in moving away from Azure AD Powershell.

    Question – the powershell standard output works fine, but writing to a CSV causes letters with accents to be converted to question marks. Any idea on how to write to CSV with correct encoding?

  12. Hi there
    Many thanks for your work here.
    I’m trying to export a similar list but including users who have $null strong/modern authentication methods but I don’t think you can expand the property authentication as it simply returns Microsoft.Graph.PowerShell.Models.MicrosoftGraphAuthentication on a basic query and This operation target is not yet supported if you attempt to expand.
    Have you any ideas?
    Regardless, many thanks for the insight.
    Regards
    Marty

Leave a Comment

0 Shares
Tweet
Pin
Share
Share