The AAD Graph PowerShell SDK allowed you to use a client secret for the Application only ( Service Principal ) login flow – also known as the client_credentials grant flow. The documentation for the new Microsoft Graph PowerShell SDK does not tell you how to use a client secret but instead, uses the more secure certificate method for the flow: Use app-only authentication with the Microsoft Graph PowerShell SDK | Microsoft Docs This post will show you how you can still use a client secret if you want by obtaining an access token and then using the -AccessToken parameter on the Connect-MgGraph command.

Assuming that you already have an app registration configured for this and it has the proper application permissions consented to for the request you want to make, we will use the PowerShell command “Invoke-RestMethod” to obtain an access token using the client_credentials grant flow.

$tenantId = "{your_tenant_id}"
$clientId = "{your_app_id}"
$clientSecret = "{your_client_secret}"

$body = @{

$response = Invoke-RestMethod -Method Post -Uri$tenantId/oauth2/v2.0/token -Body $body
$accessToken = $response.access_token


This first part of the script will obtain an access token with any consented Microsoft Graph application permissions. Once the access token is obtained, we can then set the -AccessToken parameter on the Connect-MgGraph request and make our graph requests accordingly.

Connect-MgGraph -AccessToken $accessToken
$user = Get-MgUser -Filter "userPrincipalName eq ''"



Don’t forget to disconnect once you’re done. If you’re performing a long running task ( such as paging through records ) you may need to renew your access token with the same method as in the first part.

Note: You can also use the ROPC flow to get the access token the same way as the client credentials flow. We don’t recommend the ROPC flow and by default, it is disabled for federated users unless you have allow this with a home realm discovery policy.

$body = @{"client_id"="{your_client_id}"
          "username"="{user upn}"
          "password"="{user password}"

$response = Invoke-RestMethod '{tenant_id}/oauth2/v2.0/token' -Method 'POST' -Body $body

2 Thoughts to “Microsoft Graph PowerShell SDK – Use Client Secret instead of Certificate for Service Principal login”

  1. Zak


    You mentioned token renewal for long-running scripts/actions

    Can u make a blog post on how to deal with that in powershell. Let’s say we run the script, obtain the token, then after some time token expire. So how to get an new refresh token without script iterruption or the need to re-run the script?

    Some loop command?


    1. Ray Held [MSFT]

      Hi Zak, sorry this took a bit to reply but here is an example of how you can do this. The command “CheckToken” is all that needs to be called to renew the token. Alternatively, you can just use MSAL because MSAL will manage the cache for you.

      # this needs to be global so it can persist when the variable falls out of scope of the CheckToken method
      $global:expiresDateTime | out-null

      function GetAuthResponse {
        $tenantId = “{your_tenant_id}”
        $clientId = “{your_client_id}”
        $clientSecret = “{your_client_secret}”

        $body = @{

        try {
          $response = Invoke-RestMethod -Method Post -Uri$tenantId/oauth2/v2.0/token -Body $body
          return $response
        } catch {
          $response = $null
          $global:expiresDateTime = $null
          return $null

      function ConnectMG {

        $response = GetAuthResponse
        Connect-MgGraph -AccessToken $response.access_token
        return $response

      function CheckToken {
        # This method will check to see if the current access token is expired based on the global variable expiresDateTime,
        # which gets set when a new token is acquired.
        Write-Output (‘Expires Date/Time: ‘ + $global:expiresDateTime)
        Write-Output (‘Current Date/Time: ‘ + (Get-Date -Format “MM/dd/yyyy HH:mm:ss”))

        # the -5 minutes in this if statement is to build in a 5 minute buffer
        if($global:expiresDateTime -lt (Get-Date).AddMinutes(-5)){
          # token needs renewed
          Write-Output(‘Getting new access token…’)
          $rsp = ConnectMG
          $expiresIn = [double]($rsp).expires_in

          Write-Output (‘Expires in: ‘ + $expiresIn + ‘ seconds’)

          $global:expiresDateTime = (Get-Date).AddSeconds($expiresIn)
          Write-Output (‘New Exp Date/Time: ‘ + $global:expiresDateTime)
          Write-Output (‘Access token: ‘ + $rsp.access_token)


Leave a Comment