Introduction to Microsoft Graph API
Microsoft Graph API is the unified REST endpoint for accessing data across Microsoft 365 services including Azure Active Directory, Exchange Online, SharePoint, Teams, OneDrive, and Intune. Rather than managing multiple service-specific APIs, Graph consolidates them under a single base URL: https://graph.microsoft.com. On Windows Server 2022, PowerShell combined with the Microsoft.Graph SDK gives administrators a powerful way to automate user provisioning, license assignment, group management, device queries, and much more — all without needing to leave the command line.
Understanding how Graph API authentication works is essential before writing any script. Graph supports two primary permission models: Delegated permissions, where an application acts on behalf of a signed-in user, and Application permissions, where the application acts as itself using its own identity. For automated, unattended scripts running on Windows Server 2022 (such as scheduled tasks or CI/CD pipelines), Application permissions using the client credentials flow are standard practice.
Registering an Application in Azure AD
Before connecting PowerShell to Graph, you must register an application in Azure Active Directory. This gives your script a client identity with specific Graph permissions. Navigate to the Azure portal, go to Azure Active Directory, select App registrations, and click New registration. Provide a name such as “Server2022-GraphAutomation”, select the appropriate account type (typically single-tenant), and register the app.
Once registered, note the Application (client) ID and the Directory (tenant) ID — both are required for authentication. Under Certificates & secrets, create a new client secret (or upload a certificate for production use). Under API permissions, add the Microsoft Graph permissions your script requires. For example, to read and write users, add User.ReadWrite.All. For group management, add Group.ReadWrite.All. After adding permissions, click Grant admin consent for your tenant so the application can act without per-user approval prompts.
Installing the Microsoft Graph PowerShell SDK
Windows Server 2022 ships with PowerShell 5.1 and also supports PowerShell 7.x. The Microsoft Graph PowerShell SDK works on both, but PowerShell 7 is recommended for performance and modern TLS support. Install the SDK from the PowerShell Gallery using the following command:
Install-Module Microsoft.Graph -Scope AllUsers -Force
The SDK is modular. The meta-module Microsoft.Graph installs all sub-modules. If disk space is a concern on your server, install only what you need:
Install-Module Microsoft.Graph.Users -Scope AllUsers -Force
Install-Module Microsoft.Graph.Groups -Scope AllUsers -Force
Install-Module Microsoft.Graph.Identity.DirectoryManagement -Scope AllUsers -Force
Verify the installation and check the version:
Get-Module Microsoft.Graph -ListAvailable | Select-Object Name, Version
Connecting to Microsoft Graph with Client Credentials
For unattended scripts, use the client credentials flow with a client secret. The Connect-MgGraph cmdlet supports this via the ClientSecretCredential parameter available in the SDK. First, build a PSCredential-style object or use the MSAL.PS module pattern. The cleanest approach with the v2 SDK is:
$tenantId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$clientId = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"
$clientSecret = ConvertTo-SecureString "YOUR_SECRET_HERE" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($clientId, $clientSecret)
Connect-MgGraph -TenantId $tenantId -ClientSecretCredential $credential
For interactive/delegated access (useful during testing and development), connect with scopes:
Connect-MgGraph -Scopes "User.ReadWrite.All","Group.ReadWrite.All","Directory.ReadWrite.All"
This opens a browser authentication window. After signing in, the token is cached locally. Run Get-MgContext to confirm the active connection and verify which scopes are in use:
Get-MgContext
Querying Users and Groups
Once connected, you can query Azure AD objects immediately. To retrieve all users in the tenant:
Get-MgUser -All | Select-Object DisplayName, UserPrincipalName, AccountEnabled, Id
To find a specific user by UPN:
Get-MgUser -UserId "[email protected]" | Select-Object DisplayName, JobTitle, Department, MobilePhone
Use the -Filter parameter for server-side filtering, which is far more efficient than piping to Where-Object:
Get-MgUser -Filter "Department eq 'Engineering'" -ConsistencyLevel eventual -CountVariable count -All
To list all groups and their membership counts:
Get-MgGroup -All | Select-Object DisplayName, GroupTypes, SecurityEnabled, Id
To get members of a specific group:
Get-MgGroupMember -GroupId "group-object-id-here" | ForEach-Object {
Get-MgUser -UserId $_.Id | Select-Object DisplayName, UserPrincipalName
}
Creating Users with New-MgUser
Provisioning new user accounts through Graph API allows full control over all user attributes at creation time. The following example creates a licensed user account:
$passwordProfile = @{
ForceChangePasswordNextSignIn = $true
Password = "TempP@ss2024!"
}
$newUser = New-MgUser `
-DisplayName "Alice Johnson" `
-GivenName "Alice" `
-Surname "Johnson" `
-UserPrincipalName "[email protected]" `
-MailNickname "ajohnson" `
-AccountEnabled `
-PasswordProfile $passwordProfile `
-UsageLocation "US" `
-Department "Finance" `
-JobTitle "Financial Analyst"
Write-Host "Created user: $($newUser.Id)"
The UsageLocation property is mandatory before assigning Microsoft 365 licenses. It must be a valid ISO 3166-1 alpha-2 country code.
Assigning Licenses with Set-MgUserLicense
License assignment requires knowing the SKU ID of the license. Retrieve available SKUs in your tenant:
Get-MgSubscribedSku | Select-Object SkuPartNumber, SkuId, ConsumedUnits, @{n='Available';e={$_.PrepaidUnits.Enabled - $_.ConsumedUnits}}
Once you have the SkuId, assign the license to the new user. The following assigns Microsoft 365 E3:
$skuId = "05e9a617-0261-4cee-bb44-138d3ef5d965" # Microsoft 365 E3 SkuId
$addLicense = @{
SkuId = $skuId
DisabledPlans = @()
}
Set-MgUserLicense -UserId "[email protected]" `
-AddLicenses @($addLicense) `
-RemoveLicenses @()
To remove a license, pass the SkuId in the RemoveLicenses array and an empty array for AddLicenses.
Managing Devices with Get-MgDevice
Graph API provides access to all Azure AD registered and joined devices. This is valuable for compliance audits and device lifecycle management on Windows Server 2022:
Get-MgDevice -All | Select-Object DisplayName, OperatingSystem, OperatingSystemVersion, TrustType, ApproximateLastSignInDateTime
To find stale devices not seen in the last 90 days:
$cutoff = (Get-Date).AddDays(-90).ToString("o")
Get-MgDevice -Filter "approximateLastSignInDateTime le $cutoff" -All |
Select-Object DisplayName, ApproximateLastSignInDateTime
Using Invoke-MgGraphRequest for Custom API Calls
Not every Graph endpoint has a dedicated cmdlet. Invoke-MgGraphRequest lets you call any Graph endpoint directly using standard HTTP methods. This is the escape hatch for beta APIs or less common operations:
# Get signed-in user details via beta endpoint
$response = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/v1.0/users?$top=10&$select=displayName,userPrincipalName,createdDateTime"
$response.value | Select-Object displayName, userPrincipalName, createdDateTime
To send a Teams chat message via Graph API beta:
$body = @{
body = @{
content = "Automated notification from Windows Server 2022"
}
} | ConvertTo-Json -Depth 5
Invoke-MgGraphRequest -Method POST `
-Uri "https://graph.microsoft.com/v1.0/teams/{team-id}/channels/{channel-id}/messages" `
-Body $body `
-ContentType "application/json"
Certificate-Based Authentication for Unattended Scripts
Client secrets expire and require manual rotation. For production automation on Windows Server 2022, certificate-based authentication is more secure and manageable. First, generate a self-signed certificate:
$cert = New-SelfSignedCertificate `
-Subject "CN=GraphAutomation" `
-CertStoreLocation "Cert:LocalMachineMy" `
-KeyExportPolicy Exportable `
-KeySpec Signature `
-KeyLength 2048 `
-HashAlgorithm SHA256 `
-NotAfter (Get-Date).AddYears(2)
# Export public key to upload to Azure AD app registration
Export-Certificate -Cert $cert -FilePath "C:CertsGraphAutomation.cer"
Upload the .cer file in the Azure portal under your App Registration > Certificates & secrets > Certificates. Then connect using the certificate thumbprint:
Connect-MgGraph -TenantId $tenantId -ClientId $clientId -CertificateThumbprint $cert.Thumbprint
This is the recommended approach for scheduled tasks and automation accounts on Windows Server 2022. The certificate is stored in the machine certificate store and never leaves the server in plaintext.
Using Graph Explorer for Testing
Before writing PowerShell scripts, use Graph Explorer at https://developer.microsoft.com/graph/graph-explorer to test API calls interactively. Sign in with a test account, browse the sample queries, and build your query with the correct URL, query parameters, and request body. Graph Explorer shows the raw JSON response, which helps you understand the structure before parsing it with PowerShell. Switch between the v1.0 and beta endpoints to compare feature availability. The permissions panel on the left shows which consent scopes each query requires, helping you configure your app registration correctly.
Disconnecting and Token Management
Always disconnect your session when scripting is complete to invalidate the cached access token:
Disconnect-MgGraph
For long-running scripts, access tokens expire after one hour. The SDK handles token refresh automatically when using Connect-MgGraph with client credentials. If you are building custom HTTP calls with Invoke-MgGraphRequest across a session longer than one hour, the SDK will automatically re-acquire tokens using the stored credential context without requiring reconnection.