Use app-only authentication with the Microsoft Graph PowerShell SDK

The PowerShell SDK supports two types of authentication: delegated access, and app-only access. This guide will focus on the configuration needed to enable app-only access.

Important

App-only access grants permissions directly to an application, and requires an administrator to consent to the required permission scopes. For more information on app-only access, see Microsoft identity platform and the OAuth 2.0 client credentials flow.

Let's configure app-only access for a simple script to list users and groups in your Microsoft 365 tenant.

Prerequisites

Before you can use app-only access with the Microsoft Graph PowerShell SDK, make sure you have the required prerequisites:

  • Microsoft Graph PowerShell SDK is installed. Follow the Install the Microsoft Graph PowerShell SDK guide to install the SDK.
  • A certificate to use as a credential for the application. This certificate can be a self-signed certificate or a certificate from an authority. Refer to the See also section for guidance on how to create a self-signed certificate.
    1. Have an X.509 certificate installed in your user's trusted store on the machine where you'll run the script.
    2. Export the certificate's public key in .cer, .pem, or .crt format.
    3. Get the value of the certificate subject or its thumbprint.
  • Register an application in Microsoft Entra ID, configure it with the permission scopes your scenario requires, and share the public key for your certificate.

Step 1: Register an application

You can register an application using the PowerShell SDK with delegated access by signing in as an administrator, and creating the app registration.

  1. Use a text editor to create a new file named RegisterAppOnly.ps1. Paste the following code into the file.

    param(
      [Parameter(Mandatory=$true,
      HelpMessage="The friendly name of the app registration")]
      [String]
      $AppName,
    
      [Parameter(Mandatory=$true,
      HelpMessage="The file path to your public key file")]
      [String]
      $CertPath,
    
      [Parameter(Mandatory=$false,
      HelpMessage="Your Azure Active Directory tenant ID")]
      [String]
      $TenantId,
    
      [Parameter(Mandatory=$false)]
      [Switch]
      $StayConnected = $false
    )
    
    # Graph permissions constants
    $graphResourceId = "00000003-0000-0000-c000-000000000000"
    $UserReadAll = @{
      Id="df021288-bdef-4463-88db-98f22de89214"
      Type="Role"
    }
    $GroupReadAll = @{
      Id="5b567255-7703-4780-807c-7be8301ae99b"
      Type="Role"
    }
    
    # Requires an admin
    if ($TenantId)
    {
      Connect-MgGraph -Scopes "Application.ReadWrite.All User.Read" -TenantId $TenantId
    }
    else
    {
      Connect-MgGraph -Scopes "Application.ReadWrite.All User.Read"
    }
    
    # Get context for access to tenant ID
    $context = Get-MgContext
    
    # Load cert
    $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($CertPath)
    Write-Host -ForegroundColor Cyan "Certificate loaded"
    
    # Create app registration
    $appRegistration = New-MgApplication -DisplayName $AppName -SignInAudience "AzureADMyOrg" `
     -Web @{ RedirectUris="http://localhost"; } `
     -RequiredResourceAccess @{ ResourceAppId=$graphResourceId; ResourceAccess=$UserReadAll, $GroupReadAll } `
     -AdditionalProperties @{} -KeyCredentials @(@{ Type="AsymmetricX509Cert"; Usage="Verify"; Key=$cert.RawData })
    Write-Host -ForegroundColor Cyan "App registration created with app ID" $appRegistration.AppId
    
    # Create corresponding service principal
    New-MgServicePrincipal -AppId $appRegistration.AppId -AdditionalProperties @{} | Out-Null
    Write-Host -ForegroundColor Cyan "Service principal created"
    Write-Host
    Write-Host -ForegroundColor Green "Success"
    Write-Host
    
    # Generate admin consent URL
    $adminConsentUrl = "https://login.microsoftonline.com/" + $context.TenantId + "/adminconsent?client_id=" `
     + $appRegistration.AppId
    Write-Host -ForeGroundColor Yellow "Please go to the following URL in your browser to provide admin consent"
    Write-Host $adminConsentUrl
    Write-Host
    
    # Generate Connect-MgGraph command
    $connectGraph = "Connect-MgGraph -ClientId """ + $appRegistration.AppId + """ -TenantId """`
     + $context.TenantId + """ -CertificateName """ + $cert.SubjectName.Name + """"
    Write-Host -ForeGroundColor Cyan "After providing admin consent, you can use the following values with Connect-MgGraph for app-only:"
    Write-Host $connectGraph
    
    if ($StayConnected -eq $false)
    {
      Disconnect-MgGraph
      Write-Host "Disconnected from Microsoft Graph"
    }
    else
    {
      Write-Host
      Write-Host -ForegroundColor Yellow "The connection to Microsoft Graph is still active. To disconnect, use Disconnect-MgGraph"
    }
    
  2. Save the file. Open PowerShell in the directory that contains RegisterAppOnly.ps1 and run the following command. If you don't already have a certificate, you can create a self-signed certificate using the See also section.

    .\RegisterAppOnly.ps1 -AppName "Graph PowerShell Script" -CertPath "PATH_TO_PUBLIC_KEY_FILE"
    
  3. Open your browser as prompted. Sign in with an administrator account and accept the permissions.

  4. Review the output for the prompt Please go to the following URL in your browser to provide admin consent. Copy the URL provided and paste it in your browser. Sign in with an administrator account to grant admin consent to your newly registered application.

    Note

    After granting admin consent, the browser will redirect back to http://localhost and display a Not Found error. This error can be ignored as long as the URL contains admin_consent=True.

  5. Review the rest of the PowerShell output for Connect-MgGraph command pre-filled with the values for your app registration.

    Tip

    If the script returns an error stating New-MgServicePrincipal : Unable to find target address, re-run the script with the additional -TenantId parameter. For details, see How to find your Microsoft Entra tenant ID.


Step 2: Authenticate the application

Using that app registration in step 1, you can use the Microsoft Graph PowerShell SDK with app-only access, allowing for unattended scripts.

You should have three pieces of information after completing the configuration steps above.

  • Certificate subject or thumbprint of the certificate uploaded to your Microsoft Entra app registration.
  • Application ID for your app registration.
  • Your tenant ID.

We'll use this information to test authentication. Open PowerShell and run the following command, replacing the placeholders with your information.

Connect-MgGraph -ClientID YOUR_APP_ID -TenantId YOUR_TENANT_ID -CertificateName YOUR_CERT_SUBJECT ## Or -CertificateThumbprint instead of -CertificateName

If the command succeeds, you'll see Welcome To Microsoft Graph!. Run Get-MgContext to verify that you've authenticated with app-only. The output should look like the following.

ClientId              : YOUR_APP_ID
TenantId              : YOUR_TENANT_ID
CertificateThumbprint :
Scopes                : {Group.Read.All, User.Read.All}
AuthType              : AppOnly
CertificateName       : YOUR_CERT_SUBJECT
Account               :
AppName               : Graph PowerShell Script
ContextScope          : Process

Steps 3: Create and run a script

Create a new file named GraphAppOnly.ps1 and add the following code.

# Authenticate
Connect-MgGraph -ClientID YOUR_APP_ID -TenantId YOUR_TENANT_ID -CertificateName YOUR_CERT_SUBJECT

Write-Host "USERS:"
Write-Host "======================================================"
# List first 50 users
Get-MgUser -Property "id,displayName" -PageSize 50 | Format-Table DisplayName, Id

Write-Host "GROUPS:"
Write-Host "======================================================"
# List first 50 groups
Get-MgGroup -Property "id,displayName" -PageSize 50 | Format-Table DisplayName, Id

# Disconnect
Disconnect-MgGraph

Replace the placeholders in the Connect-MgGraph command with your information. Save the file, then open PowerShell in the directory where you created the file. Run the script with the following command.

.\GraphAppOnly.ps1

The script outputs a list of users and groups similar to the output below (truncated for brevity).

Welcome To Microsoft Graph!
USERS:
======================================================

DisplayName              Id
-----------              --
Conf Room Adams          88d1ba68-8ff5-4de2-90ed-768c00abcfae
Adele Vance              3103c7b9-cfe6-4cd3-a696-f88909b9a609
MOD Administrator        da3a885e-2d97-41de-9347-5271ef321b58
...

GROUPS:
======================================================

DisplayName                         Id
-----------                         --
App Development                     06dce3e5-d310-4add-ab2c-be728fb9076e
All Employees                       1a1cd42d-9801-4e9d-9b77-5215886174ef
Mark 8 Project Team                 2bf1b0d0-81f6-4e80-b971-d1db69f8d651
...

We've used app-only access to authenticate with Microsoft Graph and list users and groups in our tenant. You can use this same pattern to create scripts that perform any operation supported by the Microsoft Graph PowerShell SDK for non-interactive scenarios.

See also