Migrating from Windows PowerShell 5.1 to PowerShell 7 for Microsoft Graph SDK Automation

If you manage Microsoft 365, Exchange Online, or Microsoft Entra ID with automation, you have likely relied on Windows PowerShell 5.1 for years. However, Microsoft has officially initiated the phase-out timeline for running the Microsoft Graph PowerShell SDK on Windows PowerShell 5.1, steering all enterprise automation toward PowerShell 7.x (Core) ahead of the upcoming Graph SDK v3.0 release.

Continuing to run Microsoft Graph scripts on legacy Windows PowerShell 5.1 introduces memory bloat, slower REST API serialization, and imminent deprecation roadblocks.

In this guide, we walk through setting up PowerShell 7.x alongside 5.1, configuring certificate-based authentication for unattended automation, and scheduling production jobs cleanly.

---

Why Move to PowerShell 7 for Microsoft Graph?

Windows PowerShell 5.1 is tied to the legacy .NET Framework (4.x), which was frozen years ago. In contrast, PowerShell 7 runs on modern .NET Core, offering major advantages for cloud administration:

1. Massive Performance Boost: API payload parsing (ConvertFrom-Json and Invoke-RestMethod) is up to 3x–5x faster. 2. Parallel Execution: Native support for ForEach-Object -Parallel allows multi-threaded tenant reporting without complex runspace pools. 3. No SDK Module Collisions: PowerShell 7 isolates modules and avoids .NET assembly binding errors frequently experienced when running multiple Azure and Graph submodules together. 4. Guaranteed Support: Future Microsoft Graph SDK versions (starting with v3.0) will require modern PowerShell runtimes.

---

Step 1: Installing PowerShell 7 in Enterprise Environments

PowerShell 7 installs side-by-side with Windows PowerShell 5.1—it does not overwrite powershell.exe. Instead, it runs as pwsh.exe.

Option A: Install via WinGet (Quickest)

POWERSHELL
winget install --id Microsoft.PowerShell --source winget --accept-package-agreements --accept-source-agreements

Option B: Silent MSI Installation (for Servers & Task Workers)

For headless servers or management jump boxes, download the x64 MSI and run a silent installation with telemetry disabled:

POWERSHELL
$msiUrl = "https://github.com/PowerShell/PowerShell/releases/download/v7.4.5/PowerShell-7.4.5-win-x64.msi"
$installerPath = "$env:TEMP\PowerShell-7.msi"

Invoke-WebRequest -Uri $msiUrl -OutFile $installerPath -UseBasicParsing

Start-Process msiexec.exe -ArgumentList "/i `"$installerPath`" /qn /norestart ADD_EXPLORER_CONTEXT_MENU_OPENPOWERSHELL=1 ENABLE_PSREMOTING=1 REGISTER_MANIFEST=1" -Wait

Remove-Item $installerPath -Force

Verify your installation:

POWERSHELL
pwsh -NoProfile -Command "$PSVersionTable.PSVersion"

---

Step 2: Installing the Microsoft Graph SDK in PowerShell 7

Once PowerShell 7 is installed, launch pwsh.exe. We recommend installing only the required submodules (such as Authentication, Users, and Identity.DirectoryManagement) rather than the monolithic Microsoft.Graph umbrella module to keep load times fast.

POWERSHELL
# Run inside pwsh (PowerShell 7)
$modules = @(
    "Microsoft.Graph.Authentication",
    "Microsoft.Graph.Users",
    "Microsoft.Graph.Identity.DirectoryManagement",
    "Microsoft.Graph.Reports"
)

foreach ($mod in $modules) {
    if (-not (Get-Module -ListAvailable -Name $mod)) {
        Write-Host "Installing $mod..." -ForegroundColor Cyan
        Install-Module -Name $mod -Scope AllUsers -Repository PSGallery -Force -AllowClobber
    }
}

---

Step 3: Unattended Certificate-Based Authentication

Enterprise automation should never use hardcoded credentials or interactive prompts. Instead, register an enterprise application in Entra ID and authenticate using an X.509 certificate.

1. Connect Using Certificate Thumbprint (Stored in Local Machine Store)

If the certificate is installed in Cert:\LocalMachine\My:

POWERSHELL
$tenantId = "your-tenant-id-here"
$appId    = "your-client-app-id-here"
$certThumb = "A1B2C3D4E5F678901234567890ABCDEF12345678"

Connect-MgGraph -ClientId $appId -TenantId $tenantId -CertificateThumbprint $certThumb -NoWelcome

2. Connect Using a Local .pfx or Certificate Object

For servers where the certificate file is secured in a credential vault or encrypted file path:

POWERSHELL
$tenantId = "your-tenant-id-here"
$appId    = "your-client-app-id-here"
$pfxPath  = "C:\Certs\MgAutomationCert.pfx"
$pfxPassword = ConvertTo-SecureString "YourSecurePassphrase" -AsPlainText -Force

$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($pfxPath, $pfxPassword)

Connect-MgGraph -ClientId $appId -TenantId $tenantId -Certificate $cert -NoWelcome

---

Step 4: Production-Ready Script Template for PowerShell 7

Here is a robust, production-ready script template that connects to Microsoft Graph in PowerShell 7, processes inactive users using parallel processing, and logs outputs cleanly:

POWERSHELL
<#
.SYNOPSIS
    Production template for unattended Microsoft Graph automation under PowerShell 7.
.NOTES
    Author: Sunil Chauhan (LAB 365)
    Runtime: pwsh (PowerShell 7.x)
#>

[CmdletBinding()]
param(
    [Parameter(Mandatory = $false)]
    [string]$LogPath = "C:\Logs\GraphAutomation.log"
)

# Ensure script is running in PowerShell 7+
if ($PSVersionTable.PSVersion.Major -lt 7) {
    Write-Error "This script must run under PowerShell 7.x or higher. Current version: $($PSVersionTable.PSVersion)"
    exit 1
}

$tenantId  = "11112222-3333-4444-5555-666677778888"
$clientId  = "aaaabbbb-cccc-dddd-eeee-ffff00001111"
$certThumb = "A1B2C3D4E5F678901234567890ABCDEF12345678"

# Configure logging
function Write-Log {
    param([string]$Message, [string]$Level = "INFO")
    $timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
    $line = "[$timestamp] [$Level] $Message"
    Write-Host $line
    Add-Content -Path $LogPath -Value $line -ErrorAction SilentlyContinue
}

try {
    Write-Log "Connecting to Microsoft Graph via Certificate..."
    Connect-MgGraph -ClientId $clientId -TenantId $tenantId -CertificateThumbprint $certThumb -NoWelcome -ErrorAction Stop
    Write-Log "Connected successfully."

    # Fetch users with specific properties
    Write-Log "Querying tenant user accounts..."
    $users = Get-MgUser -All -Property Id, DisplayName, UserPrincipalName, AccountEnabled, SignInActivity -Select Id, DisplayName, UserPrincipalName, AccountEnabled, SignInActivity

    Write-Log "Retrieved $($users.Count) user records."

    # Leverage PowerShell 7 Parallel execution to inspect sign-in activity
    $thresholdDate = (Get-Date).AddDays(-90)

    $inactiveUsers = $users | ForEach-Object -Parallel {
        $user = $_
        $lastSignIn = $user.SignInActivity.LastSignInDateTime

        if ($lastSignIn -and [datetime]$lastSignIn -lt ($using:thresholdDate)) {
            [PSCustomObject]@{
                DisplayName         = $user.DisplayName
                UserPrincipalName   = $user.UserPrincipalName
                AccountEnabled      = $user.AccountEnabled
                LastSignInDateTime = $lastSignIn
            }
        }
    } -ThrottleLimit 10

    Write-Log "Found $($inactiveUsers.Count) users inactive for over 90 days."

    # Export report
    $reportPath = "C:\Reports\InactiveUsers-$(Get-Date -Format 'yyyyMMdd').csv"
    $inactiveUsers | Export-Csv -Path $reportPath -NoTypeInformation
    Write-Log "Report exported to: $reportPath"

} catch {
    Write-Log "FATAL ERROR: $($_.Exception.Message)" "ERROR"
    exit 1
} finally {
    Write-Log "Disconnecting Graph session..."
    Disconnect-MgGraph | Out-Null
    Write-Log "Automation job completed."
}

---

Step 5: Scheduling the Job in Windows Task Scheduler

When scheduling PowerShell 7 automation, remember that the executable changes from powershell.exe to pwsh.exe.

Configure your Scheduled Task Action with the following parameters:

  • Program/script:
TEXT
  C:\Program Files\PowerShell\7\pwsh.exe
  
  • Add arguments (optional):
TEXT
  -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "C:\Scripts\GraphAutomation.ps1"
  
  • Start in:
TEXT
  C:\Scripts\
  

---

Summary Checklist

Action ItemWindows PowerShell 5.1PowerShell 7.x (Recommended)
Executablepowershell.exepwsh.exe
Graph SDK v3.0 CompatibilityDeprecated / DiscontinuedFully Supported
Multi-threadingRequires runspacesBuilt-in ForEach-Object -Parallel
REST SerializationSlow .NET 4.8 engineHigh-speed .NET Core engine
AuthenticationInteractive / LegacyApp Registration + Certificate
Upgrading your automation infrastructure to PowerShell 7 now ensures your scheduled reports, tenant administration runbooks, and provisioning workflows continue to run reliably without interruption as Microsoft sunsets legacy runtimes.