A junior-admin-friendly guide to secure Microsoft 365 automation, from IT Master Services. Part 1 of 3.
If your PowerShell scripts connect to Microsoft 365 with a client secret — a long password pasted into a script, a config file, or a scheduled task — you're carrying real risk. Secrets get copied, checked into repositories, left in spreadsheets, and forgotten until they leak. The modern, far safer approach is certificate-based authentication: the script proves its identity with a certificate whose private key never leaves the machine, and nothing password-like is stored in the script at all.
The core idea: instead of a shared secret, you upload the public half of a certificate to Microsoft Entra, and keep the private half protected on the computer that runs your automation. The script references the certificate by its thumbprint — which is just an ID, not a password.
This is Part 1 of a three-part series:
- Part 1 (this guide): create the certificate and configure the Microsoft Entra app registration.
- Part 2: convert scripts from client secrets to certificates and connect to Microsoft Graph, Exchange Online, and SharePoint PnP.
- Part 3: rotate and renew certificates, and troubleshoot authentication problems.
This guide is for PowerShell automation authentication only. Do not use it to create website TLS/SSL certificates — that's a different job.
Key Concepts (in Plain English)
| Term | What it means |
| CER file | The public half of the certificate. This is what you upload to Entra. On its own it cannot authenticate. |
| PFX file | A backup that contains the private key. Treat it like a password — protect it with a strong password and secure storage. |
| Thumbprint | The unique ID PowerShell uses to find the certificate in the Windows certificate store. |
| Certificate store | Where Windows keeps certificates, e.g. Cert:\CurrentUser\My or Cert:\LocalMachine\My. |
| Private key | The secret part of the certificate. The account that runs the script must be able to access it. |
Prerequisites
- You're signed in as the Windows account that will run the scripts — or you know which account the scheduled task will use.
- Local administrator rights (if you need to create folders or change permissions).
- A certificate naming convention. This guide uses the example
M365-Automation-2026.
- A secure place to store the PFX password (never in a script).
- Access to the Microsoft Entra admin center with permission to manage app registrations and grant admin consent (or someone who can).
CurrentUser vs LocalMachine: for most scheduled tasks that run as a specific account, creating the certificate under CurrentUser is simplest — as long as the task runs as that same account. Use LocalMachine when multiple accounts need it, and make sure the run account has permission to the private key.
Part A — Create the Certificate
Step 1: Create a working folder
Create a folder to hold your exported certificate files and notes.
New-Item -Path "C:\mycertificates" -ItemType Directory -Force
Test-Path "C:\mycertificates" # Expected: True
Step 2 (optional): Lock down the folder
Because this folder may hold PFX backups, restrict it to administrators. If you're unsure about NTFS permissions, review this with a senior admin first — don't remove access for the account that maintains the folder.
$path = "C:\mycertificates"
$acl = Get-Acl $path
$acl.SetAccessRuleProtection($true, $false)
$admins = New-Object System.Security.AccessControl.FileSystemAccessRule("Administrators","FullControl","ContainerInherit,ObjectInherit","None","Allow")
$system = New-Object System.Security.AccessControl.FileSystemAccessRule("SYSTEM","FullControl","ContainerInherit,ObjectInherit","None","Allow")
$acl.SetAccessRule($admins)
$acl.SetAccessRule($system)
Set-Acl -Path $path -AclObject $acl
Step 3: Create the self-signed certificate
Open PowerShell as the account that will run the automation. This creates a 24-month certificate in the current user's store.
$CertName = "M365-Automation-2026"
$Cert = New-SelfSignedCertificate `
-Subject "CN=$CertName" `
-CertStoreLocation "Cert:\CurrentUser\My" `
-KeySpec Signature `
-KeyExportPolicy Exportable `
-KeyLength 2048 `
-KeyAlgorithm RSA `
-HashAlgorithm SHA256 `
-NotAfter (Get-Date).AddMonths(24)
$Cert | Select-Object Subject, Thumbprint, NotBefore, NotAfter
Copy the Thumbprint. You'll use it later with Connect-MgGraph, Connect-ExchangeOnline, and other certificate-aware modules. (For LocalMachine, change the store path to Cert:\LocalMachine\My.)
Step 4: Export the public CER file
The CER holds only the public key and is safe to upload to Entra.
$CerPath = "C:\mycertificates\M365-Automation-2026.cer"
Export-Certificate -Cert $Cert -FilePath $CerPath
Step 5: Export the password-protected PFX backup
The PFX contains the private key. Protect it with a strong password and store it securely.
$PfxPath = "C:\mycertificates\M365-Automation-2026.pfx"
$PfxPassword = Read-Host "Enter a strong PFX password" -AsSecureString
Export-PfxCertificate -Cert $Cert -FilePath $PfxPath -Password $PfxPassword
Never hard-code the PFX password in a script. Use Read-Host, a password vault, or another approved secure process.
Step 6: Verify the private key exists
This is the check that saves you hours of confusion later.
Get-ChildItem Cert:\CurrentUser\My |
Where-Object Thumbprint -eq $Cert.Thumbprint |
Select-Object Subject, Thumbprint, NotAfter, HasPrivateKey
HasPrivateKey must show True. If it shows False, authentication will fail because the machine can't sign the request.
Part B — Configure the Microsoft Entra App Registration
Now you'll tell Microsoft Entra to trust this certificate, and grant the app exactly the permissions it needs.
Step 1: Open the app registration
In the Microsoft Entra admin center, go to Identity → Applications → App registrations and open your automation app registration (check All applications if it isn't listed under owned apps). From the Overview page, record the Application (client) ID and Directory (tenant) ID — these identify the app and tenant. They're not passwords, but treat them as configuration data.
Step 2: Upload the public certificate
Open Certificates & secrets → Certificates → Upload certificate, browse to C:\mycertificates, and select the .cer file from Part A. Add a description (e.g. M365-Automation-2026) and confirm it appears with the correct thumbprint and expiration date.
Upload the CER only. Never upload or share the PFX.
Step 3: Grant least-privilege API permissions
Add only the permissions the automation actually needs. For unattended scripts, use Application permissions (not Delegated — those require a signed-in user).
| Workload | Example permissions | Notes |
| Graph reporting | User.Read.All, Group.Read.All, Directory.Read.All | Prefer read-only where possible. |
| Graph administration | User.ReadWrite.All, DeviceManagementManagedDevices.Read.All | Confirm least privilege before granting write. |
| Exchange Online | Exchange.ManageAsApp | Also needs an Exchange RBAC role assignment. |
| SharePoint (PnP) | Sites.Selected or Sites.FullControl.All | Prefer Sites.Selected where practical. |
Under API permissions → Add a permission, choose the API (Microsoft Graph, Office 365 Exchange Online, or SharePoint), select Application permissions, pick what you need, and add them.
Step 4: Grant admin consent
Application permissions don't take effect until an administrator consents. On the API permissions page, review the list carefully, click Grant admin consent for [tenant], confirm, and verify the Status column shows Granted.
Step 5: Validate end-to-end
From the computer that has the certificate's private key, run this — replacing the placeholders with your values:
$AppId = "00000000-0000-0000-0000-000000000000"
$TenantId = "00000000-0000-0000-0000-000000000000"
$Thumbprint = "PASTE_CERTIFICATE_THUMBPRINT_HERE"
Connect-MgGraph -ClientId $AppId -TenantId $TenantId -CertificateThumbprint $Thumbprint
Get-MgContext
Disconnect-MgGraph
Get-MgContext should show an AppOnly authentication context and should not prompt for a username or password. If it does, you've got certificate authentication working.
Common Mistakes and Fixes
| Symptom | Cause | Fix |
| Scheduled task can't find the certificate | Created under a different user profile | Create/import the certificate under the task's account, or use LocalMachine with correct private-key permissions. |
| Authentication fails; only the CER exists locally | The private key is missing | Import the PFX into the correct certificate store. |
Connect command fails on a valid-looking thumbprint | Hidden spaces/characters copied from the portal | Paste into Notepad first, or strip non-hex characters before use. |
| Command connects but returns "access denied" | Missing API permission or admin consent | Add the correct Application permission and grant admin consent. |
| Certificate expired | Past the NotAfter date | Create a replacement and upload it to the app registration (covered in Part 3). |
Security best practices
- No client secrets for production automation — use certificates.
- Grant only the API permissions each script needs; document why each is granted.
- Record the thumbprint and expiration date, and schedule a rotation reminder before it expires.
- Protect and restrict access to PFX backups; never commit them to source control.
- Keep the app's owners current and appropriate.
What's Next
You now have a certificate created, its private key verified, and a Microsoft Entra app registration configured to trust it with least-privilege permissions. In Part 2, we'll convert real scripts from client secrets to certificates and connect to Microsoft Graph, Exchange Online, and SharePoint PnP. In Part 3, we'll cover rotating certificates without downtime and troubleshooting.
Want your Microsoft 365 automation set up securely — or a broader security review? Explore our Managed IT Services and Microsoft 365 Services, and see our related guides on why MFA is valuable and Conditional Access.
References