Skip to main content
Azure

The Microsoft Graph CLI is retiring: what to use instead

The Microsoft Graph CLI is retiring: what to use instead

Microsoft announced on August 29, 2025 that both the Microsoft Graph CLI (mgc) and the Microsoft Graph Toolkit are being retired. The full shutdown is planned for August 28, 2026. If you’ve been relying on mgc commands in your automation scripts or CI/CD pipeline steps, you’ll need to find an alternative. I’ve been using these tools myself, and before that I was using the O365 CLI (now called CLI for Microsoft 365) in my CI/CD pipeline for Teams apps, which I wrote about in my CI/CD for Teams post. So when I saw this announcement, I immediately started looking at what to move to. Let’s get started.

The old way (retiring)

First off, if you’re not sure whether you’re affected, here’s what mgc commands typically look like:

mgc teams list
mgc teams channels list --team-id "team-guid"
mgc teams apps list --query "displayName eq 'My Bot'"

If any of those look familiar, keep reading. You’ve got until August 2026, but honestly, don’t wait. I’ve been bitten before by waiting until the last minute on deprecations and it’s never fun.

Alternative 1: CLI for Microsoft 365 (my pick)

This is the one I’d recommend for most people. I was already using the O365 CLI (the predecessor) back when I set up my CI/CD pipeline for Teams apps, so moving to CLI for Microsoft 365 felt natural. The syntax is clean, it covers a huge surface area, and the community behind it is fantastic. Waldek Mastykarz and the PnP community have done an incredible job keeping this tool up to date and adding features that Microsoft’s own CLI never had.

m365 login
m365 teams team list
m365 teams channel list --teamId "team-guid"
m365 teams app list --query "[?displayName=='My Bot']"

# Bonus: Teams-specific commands Graph CLI never had
m365 teams app publish --filePath ./my-app.zip
m365 teams app update --id "app-guid" --filePath ./my-app.zip

Now, that last part is what really sells it for me. The publish and update commands for Teams apps are something the Graph CLI never offered. If you’re doing any kind of Teams app deployment in your pipeline, this is a game changer. I wish I had switched sooner, to be honest.

Alternative 2: Microsoft Graph PowerShell SDK

Of course, if you’re more of a PowerShell person, the Microsoft Graph PowerShell SDK is the official replacement Microsoft is pointing people toward. It’s well-maintained and maps pretty closely to the Graph API itself.

Connect-MgGraph -Scopes "Team.ReadBasic.All","Channel.ReadBasic.All"

Get-MgTeam -All | Select-Object DisplayName, Id
Get-MgTeamChannel -TeamId "team-guid" | Select-Object DisplayName, MembershipType
Get-MgAppCatalogTeamsApp | Where-Object { $_.DisplayName -eq "My Bot" }

The cmdlet names are a bit verbose, but once you get used to the Mg prefix pattern it’s pretty intuitive. This way you also get the benefit of proper PowerShell pipeline support, which is great for admin automation scenarios where you need to pipe results into other commands or export to CSV.

Alternative 3: Microsoft Graph SDKs for application code

If you’re not scripting but actually building an application that talks to Graph, you should be using the proper Graph SDKs anyway. This uses the managed identity pattern I wrote about in my post about stopping the use of ClientID and Secret, which I still stand by.

using Microsoft.Graph;
using Azure.Identity;

var graphClient = new GraphServiceClient(new DefaultAzureCredential());

var teams = await graphClient.Teams.GetAsync(config =>
{
    config.QueryParameters.Filter = "displayName eq 'Engineering'";
});

foreach (var team in teams!.Value!)
{
    Console.WriteLine($"Team: {team.DisplayName} ({team.Id})");
    var channels = await graphClient.Teams[team.Id].Channels.GetAsync();
    foreach (var channel in channels!.Value!)
    {
        Console.WriteLine($"  └─ {channel.DisplayName} ({channel.MembershipType})");
    }
}

The DefaultAzureCredential is doing the heavy lifting here. It picks up managed identities in Azure, your Visual Studio credentials locally, and even Azure CLI tokens. No secrets to manage, no certificates to rotate. If you haven’t read that post yet, feel free to check it out because it explains why this approach is so much better from a security perspective.

Alternative 4: Direct HTTP calls

Sometimes you just need to quickly test something against the Graph API without installing anything. We’ve all been there. For those moments, a direct HTTP call does the trick.

TOKEN=$(az account get-access-token --resource https://graph.microsoft.com --query accessToken -o tsv)

curl -s -H "Authorization: Bearer $TOKEN" \
     "https://graph.microsoft.com/v1.0/teams" | jq '.value[] | {name: .displayName, id: .id}'

I wouldn’t build a pipeline around this, but for debugging or quickly checking if a permission is set correctly, it’s perfect. You can also use Graph Explorer in the browser, but sometimes the terminal is just faster.

So which one should you pick?

Here’s how I think about it. For daily scripting and CI/CD pipelines, go with CLI for Microsoft 365. It’s what I use and the Teams-specific commands alone make it worth the switch. For admin automation where you’re already in PowerShell land, the Graph PowerShell SDK is the natural choice. If you’re writing actual application code in C#, Python, Java, or any other supported language, use the proper Graph SDKs with managed identities. And for quick debugging sessions where you just need to poke the API, direct HTTP with curl or Graph Explorer will do just fine.

Don’t wait until August 2026. Start migrating now while you still have both tools running side by side so you can verify everything works.