In my work-life, I see often app secrets for unattended scripts are stored in the script itself or in environment variables. And this is even true for apps with high privilege permissions to Azure AD like for example User.ReadWrite.All. This is in particular dangerous for scripts running on on-premises machines in local networks without a Zero-Trust architecture.

I recommend instead using certificated based authentication (or when running in Azure -> Managed Identities)

Create a self-signed X.509 certificate

Install the Microsoft.Graph module and create a self-signed certificate:

# Create certificate
$privateCert = New-SelfSignedCertificate -Subject "MyGraphApp" -CertStoreLocation "cert:\LocalMachine" -NotAfter (Get-Date)
.AddYears(1) -KeySpec KeyExchange

# Export certificate to .cer file
$privateCert | Export-Certificate -FilePath C:\tmp\public.cer

Register and configure an Azure AD App

Next, register a new App in the Azure AD App Registration. In the example, I've called the app "MyGraphApp".

Upload the public.cert under "Certificates & secrets" and note the certificate thumbprint for later.

Assign the app the Graph permissions that you need, for example, User.ReadWrite.All.

Don't forget to grant admin consent afterward.

For your script, you need the Application (client) ID, Tenant ID, and the certificate thumbprint from the app registration.

Connect to the Microsoft Graph with the certificate

Now when can write our script:

# Install the Microsoft.Graph module
Install-Module Microsoft.Graph -Scope AllUsers

# Get certificate from the machine store. Use the thumprint from above
$cert = Get-ChildItem Cert:\LocalMachine\My\078DDE862A516845FEB7A24E431A6EAF4C188131

# Connect to the MS Graph using the client id, tenant id and the certificate
Connect-MgGraph -ClientID 936acea1-56a7-4a5a-8190-7f4dbe0d9963 -TenantId 2cc6d4d2-40f8-43d5-8a11-8986ds84b7se -Certificate $cert

# Execute Graph Cmdlets as needed

Welcome To Microsoft Graph!