azureblog.co.uk
← cd ~/posts

Unattended Graph PowerShell scripts with certificate authentication

Scheduled scripts can't answer an MFA prompt. App-only authentication with a certificate is the right way to run Microsoft Graph PowerShell unattended.

2 min read✓ checked 16 Sept 2026PowerShell · Entra ID · Security
On this page
  1. 1. Create a certificate
  2. 2. Register the app
  3. 3. Connect in the script
  4. Keep it safe
  5. Renewing the certificate
  6. Troubleshooting

Interactive Connect-MgGraph is fine at your desk. A scheduled task or server-side script needs its own identity. If the script runs in Azure, use a . Otherwise, use an with a certificate, never a client secret pasted into the script.

Delegated (interactive)

  • Someone signs in
  • Acts as that user
  • Limited to what the user can access
  • MFA prompts apply

App-only

  • No user involved
  • Acts as the app
  • Limited to the app permissions granted
  • Needs a certificate or managed identity
Interactive versus app-only sign-in.

1. Create a certificate

For a script on a Windows server, a self-signed certificate in the machine store works well:

powershell
$cert = New-SelfSignedCertificate -Subject "CN=Graph-UserReport" `
  -CertStoreLocation "Cert:\LocalMachine\My" `
  -KeyExportPolicy NonExportable -KeySpec Signature `
  -NotAfter (Get-Date).AddYears(1)

Export-Certificate -Cert $cert -FilePath C:\Temp\Graph-UserReport.cer

Only the public key (.cer) leaves the server. The private key stays put and can't be exported.

2. Register the app

  1. Create an app registration, single tenant.
  2. Under Certificates & secrets, upload the .cer file.
  3. Under API permissions, add the Application permissions the script needs, such as User.Read.All, and grant admin consent. Keep it to the minimum.
  4. Add at least two owners.

3. Connect in the script

powershell
Connect-MgGraph -ClientId "<app-id>" -TenantId "<tenant-id>" `
  -CertificateThumbprint "<thumbprint>" -NoWelcome

Get-MgUser -All -Property DisplayName,UserPrincipalName,AccountEnabled |
  Export-Csv C:\Reports\users.csv -NoTypeInformation
ScriptLocal cert storeEntra IDMicrosoft GraphLooks up certificate bythumbprint1Signs a client assertion withthe private keyRequests token for the app3Checks signature against theuploaded public keyAccess token with application permissions5Calls Graph6
What happens when the script connects.

Keep it safe

  • Run the scheduled task as an account that can read the certificate's private key, and no more.
  • Track the certificate's expiry date and renew it before it lapses.
  • Watch the app's sign-ins in the sign-in logs.
  • Consider limiting the app with a policy for if you have the licensing.

Renewing the certificate

  1. About a month before expiry, create a new certificate on the server, as before.
  2. Upload the new .cer to the app registration. Both certificates now work.
  3. Update the thumbprint in the script, or the scheduled task's parameter, and test a run.
  4. Remove the old certificate from the app registration and the server.

Troubleshooting

  • AADSTS700027 (client assertion failed signature validation): the certificate on the server doesn't match one uploaded to the app, or it has expired.
  • Certificate not found: the account running the task can't see it. Check the store (LocalMachine or CurrentUser) and the private key permissions.
  • 403 Forbidden from Graph: the app is missing an application permission, or admin consent wasn't granted.
Next in Automating Entra and Azure safely · part 3 of 5Deploy from GitHub Actions to Azure without storing a single secret →