Agent skill

CLI Microsoft365 Script

by pnp in pnp/cli-microsoft365-mcp-server

Write PowerShell scripts using CLI for Microsoft 365 commands to automate Microsoft 365 management tasks.

MITAuto-check passedDocuments & Office

Install CLI Microsoft365 Script

skills CLI
$ npx skills add pnp/cli-microsoft365-mcp-server --skill cli-microsoft365-script -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install pnp/cli-microsoft365-mcp-server cli-microsoft365-script --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/pnp/cli-microsoft365-mcp-server.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.github/skills/cli-microsoft365-script .claude/skills/cli-microsoft365-script && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
cli-microsoft365-script
GitHub stars
131
Token cost
~3.3k tokens
SKILL.md length
624 words
Files
1
Skills in repo
2
Repo updated
First seen
Licence
MIT

At a glance

Write PowerShell scripts using CLI for Microsoft 365 commands to automate Microsoft 365 management tasks.

  • Works in 5 steps: Never hardcode credentials — use… → Use certificate or managed identity… → Grant minimum required permissions to… → …
  • : writing PowerShell scripts that use m365 commands
  • SKILL.md covers Script Setup, Authentication, Error Handling and Output Handling, plus 3 more sections
  • Reaches contoso.sharepoint.com and graph.microsoft.com; needs CERT_PASSWORD and CLIENT_SECRET

What it does

CLI Microsoft365 Script is an agent skill from pnp/cli-microsoft365-mcp-server. Write PowerShell scripts using CLI for Microsoft 365 commands to automate Microsoft 365 management tasks. Use when: writing PowerShell scripts that use m365 commands, automating SharePoint Online provisioning, automating Entra ID user/group management, automating Teams setup, batch operations on Microsoft 365 resources, CI/CD scripts for Microsoft 365, error handling for m365 commands in PowerShell.

Its SKILL.md is about 3.3k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Documents & Office, covering Cloud office suites and Shell scripting. It works with Microsoft 365, PowerShell, Microsoft Entra ID and Microsoft SharePoint. The repository describes itself as: Manage Microsoft 365 using MCP server. The licence is MIT.

When your agent uses it

  • : writing PowerShell scripts that use m365 commands
  • Automating SharePoint Online provisioning
  • Automating Entra ID user/group management
  • Automating Teams setup

Example prompts

  • “/cli-microsoft365-script”

Requirements

  • A credential in CLIENT_SECRET

Workflow steps

5 steps, taken from the first numbered list in SKILL.md.

  1. Never hardcode credentials — use environment variables, Azure Key Vault, or secure parameter stores
  2. Use certificate or managed identity authentication for automation
  3. Grant minimum required permissions to the app registration
  4. Rotate secrets and certificates regularly
  5. Do not log sensitive output — use --output none when possible for write operations

What it can do on your machine

Read from SKILL.md and the folder at commit 6d89707. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are powershell).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Hosts in commands or code, which the agent is likely to contact:

    • contoso.sharepoint.com
    • graph.microsoft.com

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names these keys or tokens, usually read from environment variables:

    • CERT_PASSWORD
    • CLIENT_SECRET

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

CLI Microsoft365 Script loads about 3.3k tokens when it runs. Until then it costs about 107 tokens; SKILL.md has 624 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~107
When it runs · the whole SKILL.md, loaded when a task matches
~3.3k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from pnp/cli-microsoft365-mcp-server at commit 6d89707, republished under its MIT licence (© pnp). 624 words, ~3,336 tokens.

Download SKILL.mdSave it as .claude/skills/cli-microsoft365-script/SKILL.md (or your agent's skills folder).
name
cli-microsoft365-script
description
Write PowerShell scripts using CLI for Microsoft 365 commands to automate Microsoft 365 management tasks. Use when: writing PowerShell scripts that use m365 commands, automating SharePoint Online provisioning, automating Entra ID user/group management, automating Teams setup, batch operations on Microsoft 365 resources, CI/CD scripts for Microsoft 365, error handling for m365 commands in PowerShell.

CLI for Microsoft 365 — PowerShell Scripting

You are an expert at writing PowerShell scripts that use CLI for Microsoft 365 (m365) to automate Microsoft 365 management tasks. Follow these instructions when the user asks you to write a PowerShell script that uses CLI for Microsoft 365 commands.

Script Setup

Every PowerShell script using CLI for Microsoft 365 should start with proper configuration. Use this template as a starting point:

powershell
#!/usr/bin/env pwsh

$ErrorActionPreference = "Stop"

# Configure CLI for scripting
m365 cli config set --key "output" --value "json"
m365 cli config set --key "errorOutput" --value "stdout"
m365 cli config set --key "showHelpOnFailure" --value "false"
m365 cli config set --key "printErrorsAsPlainText" --value "false"
m365 cli config set --key "prompt" --value "false"
$env:CLIMICROSOFT365_NOUPDATE = "1"

# Ensure authentication
m365 login --ensure
Why these settings matter
SettingValuePurpose
outputjsonStructured data for reliable parsing
errorOutputstdoutAllows PowerShell to capture errors in variables
showHelpOnFailurefalsePrevents help text from polluting output
printErrorsAsPlainTextfalseReturns errors as JSON for structured handling
promptfalsePrevents interactive prompts that block automation

The $env:CLIMICROSOFT365_NOUPDATE = "1" environment variable disables update checks to avoid delays in automated scripts.

Authentication

Check and establish authentication

Always use m365 login --ensure at the start of every script. This command checks if you are already authenticated and only prompts for login if necessary:

powershell
    m365 login --ensure
Authentication for automation and CI/CD

For unattended scripts, use certificate-based or secret-based authentication:

powershell
    # Certificate-based (recommended for CI/CD)
    m365 login --authType certificate --certificateFile "C:\certs\app.pfx" --password $env:CERT_PASSWORD

    # Client secret
    m365 login --authType secret --secret $env:CLIENT_SECRET

    # Managed identity (Azure-hosted only)
    m365 login --authType identity

Never hardcode credentials or secrets in scripts. Use environment variables or secure stores instead.

Environment variables for app identity

Set these environment variables to avoid passing --appId and --tenant on every login:

powershell
$env:CLIMICROSOFT365_ENTRAAPPID = "<app-id>"
$env:CLIMICROSOFT365_TENANT = "<tenant-id>"

Error Handling

Approach 1: Check exit code (simple)

The simplest approach — run the command and check $LASTEXITCODE:

powershell
    $siteData = m365 spo site get --url "https://contoso.sharepoint.com/sites/project"
    if ($LASTEXITCODE -ne 0) {
        Write-Error "Failed to retrieve site"
        exit 1
    }
    $site = $siteData | ConvertFrom-Json
    Write-Host "Site: $($site.Title)"

For detailed error handling, use the Invoke-CLICommand helper function. This requires the CLI configuration from the Script Setup section above (especially errorOutput set to stdout and printErrorsAsPlainText set to false):

powershell
function Invoke-CLICommand {
    [CmdletBinding()]
    param(
        [parameter(Mandatory = $true, ValueFromPipeline = $true)] $input
    )

    $output = $input

    if ($null -eq $output) {
        return $null
    }

    $parsedOutput = $output | ConvertFrom-Json

    if ($parsedOutput -isnot [Array] -and $null -ne $parsedOutput.error) {
        throw $parsedOutput.error
    }

    return $parsedOutput
}

Usage with try/catch:

powershell
try {
    $site = m365 spo site get --url "https://contoso.sharepoint.com/sites/project" | Invoke-CLICommand
    Write-Host "Site: $($site.Title)"
}
catch {
    Write-Error "Failed: $($_.Exception.Message)"
    exit 1
}
When to use which approach
  • Exit code checking: Simple scripts, one-off commands, scripts where you just need pass/fail
  • Invoke-CLICommand helper: Scripts with many commands, when you need the parsed error message, when using try/catch for control flow

Output Handling

Always use JSON for scripting

Set --output json (or configure it globally) and parse with ConvertFrom-Json:

powershell
$sites = m365 spo site list --output json | ConvertFrom-Json

foreach ($site in $sites) {
    Write-Host "Site: $($site.Url) - $($site.Title)"
}

When using the Invoke-CLICommand helper, output is already parsed:

powershell
$sites = m365 spo site list | Invoke-CLICommand

foreach ($site in $sites) {
    Write-Host "Site: $($site.Url) - $($site.Title)"
}
Export to CSV
powershell
m365 spo site list --output csv > sites.csv
Suppress output for write operations

Use --output none when you do not need the response:

powershell
    m365 spo site remove --url "https://contoso.sharepoint.com/sites/old" --force --output none

Filtering Data with JMESPath

Use the --query option for server-side filtering instead of piping to Where-Object:

powershell
    # Good — filter with JMESPath
    $projectSites = m365 spo site list --query "[?contains(Title, 'Project')]" | Invoke-CLICommand

    # Less efficient — filter in PowerShell after fetching everything
    $allSites = m365 spo site list | Invoke-CLICommand
    $projectSites = $allSites | Where-Object { $_.Title -like "*Project*" }
Common JMESPath patterns
powershell
    # Select specific fields
    m365 spo site list --query "[*].{Title: Title, Url: Url}"

    # Filter and project
    m365 spo site list --query "[?contains(Title, 'Project')].{Title: Title, Url: Url}"

    # Get a single value
    m365 entra user get --id "user@contoso.com" --query "id" --output text

JMESPath queries are case-sensitive.

PowerShell-Specific Syntax

Show full SKILL.md (253 more words)Show less
Escaping the @ character

In PowerShell, @ is a special character. When using the CLI file reference syntax or tokens like @meId, escape with a backtick:

powershell
    # File reference
    m365 spo sitescript add --title "Script" --content `@script.json

    # User tokens
    m365 entra user get --id "`@meId"
Values starting with a dash

Use = to pass values that start with a dash:

powershell
    m365 planner task get --id=-9rMKQooUjZdxgv1qQVZYABEuw
Complex JSON payloads

Store complex JSON in files rather than inline strings:

powershell
    $payload = @{
        displayName = "Project Team"
        description = "Team for the project"
    } | ConvertTo-Json -Depth 10

    $payload | Out-File -FilePath "payload.json" -Encoding utf8

    m365 request --url "`@graph/teams" --method post --body `@payload.json
Boolean values

In PowerShell, you can use $true / $false, or the CLI accepted values: 1, yes, true, on / 0, no, false, off.

Common Script Patterns

Check if a resource exists before creating
powershell
try {
    $list = m365 spo list get --webUrl $webUrl --title $listTitle | Invoke-CLICommand
    Write-Host "List '$($list.Title)' already exists"
}
catch {
    Write-Host "List not found, creating..."
    m365 spo list add --webUrl $webUrl --title $listTitle --baseTemplate GenericList | Invoke-CLICommand
    Write-Host "List '$listTitle' created"
}
Batch operations with error handling
powershell
$users = @("user1@contoso.com", "user2@contoso.com", "user3@contoso.com")
$webUrl = "https://contoso.sharepoint.com/sites/project"

$succeeded = 0
$failed = 0

foreach ($user in $users) {
    try {
        m365 spo user add --webUrl $webUrl --loginName $user | Invoke-CLICommand
        Write-Host "Added: $user" -ForegroundColor Green
        $succeeded++
    }
    catch {
        Write-Host "Failed: $user — $($_.Exception.Message)" -ForegroundColor Red
        $failed++
    }
}

Write-Host "`nCompleted. Succeeded: $succeeded, Failed: $failed"
Pipe output between commands
powershell
# Get all team IDs and archive each team
$teams = m365 teams team list --query "[?contains(displayName, 'Old')]" | Invoke-CLICommand

foreach ($team in $teams) {
    m365 teams team archive --id $team.id
    Write-Host "Archived: $($team.displayName)"
}
Read data from CSV and process
powershell
$usersToCreate = Import-Csv -Path "users.csv"

foreach ($user in $usersToCreate) {
    try {
        m365 entra user add `
            --displayName $user.DisplayName `
            --userName $user.UserPrincipalName `
            --password $user.Password `
            --accountEnabled $true | Invoke-CLICommand
        Write-Host "Created user: $($user.DisplayName)" -ForegroundColor Green
    }
    catch {
        Write-Host "Failed to create $($user.DisplayName): $($_.Exception.Message)" -ForegroundColor Red
    }
}
Use the context feature to avoid repeating options
powershell
    # Set up context for repeated operations on the same site
    m365 context init
    m365 context option set --name "webUrl" --value "https://contoso.sharepoint.com/sites/project"

    # Commands now pick up --webUrl automatically
    $lists = m365 spo list list | Invoke-CLICommand
    $items = m365 spo listitem list --listTitle "Tasks" | Invoke-CLICommand
Server-relative URLs for SharePoint

After running any spo command or setting the SPO URL explicitly, you can use server-relative URLs:

powershell
    m365 spo set --url "https://contoso.sharepoint.com"

    # Now use server-relative URLs
    $site = m365 spo site get --url "/sites/project" | Invoke-CLICommand
Using @meId and @meUserName tokens

These built-in tokens resolve to the currently authenticated user:

powershell
    $me = m365 entra user get --id "`@meId" | Invoke-CLICommand
    Write-Host "Logged in as: $($me.displayName)"

Raw API Requests

Use m365 request to call any Microsoft API not covered by specific commands:

powershell
    # GET request with URL tokens
    $profile = m365 request --url "`@graph/me" | Invoke-CLICommand

    # POST with inline body
    m365 request --url "`@graph/me/messages" --method post --body '{"subject":"Test","body":{"content":"Hello"}}'

    # POST with body from file
    m365 request --url "`@graph/teams" --method post --body `@team-payload.json

URL tokens:

  • @graph → https://graph.microsoft.com/v1.0
  • @graphbeta → https://graph.microsoft.com/beta
  • @spo → Current SharePoint URL (set via m365 spo set)

Debugging Scripts

Verbose mode

For development and troubleshooting:

powershell
    m365 spo site get --url "https://contoso.sharepoint.com/sites/project" --verbose

Or set via environment variable for all commands:

powershell
    $env:CLIMICROSOFT365_VERBOSE = "1"
Debug mode

Shows full HTTP request and response details:

powershell
    $env:CLIMICROSOFT365_DEBUG = "1"

Security Best Practices

  1. Never hardcode credentials — use environment variables, Azure Key Vault, or secure parameter stores
  2. Use certificate or managed identity authentication for automation
  3. Grant minimum required permissions to the app registration
  4. Rotate secrets and certificates regularly
  5. Do not log sensitive output — use --output none when possible for write operations

Complete Script Template

powershell
#!/usr/bin/env pwsh

<#
.SYNOPSIS
    Description of what this script does.
.DESCRIPTION
    Detailed description.
.EXAMPLE
    .\Script.ps1 -SiteUrl "https://contoso.sharepoint.com/sites/project"
#>

param(
    [Parameter(Mandatory = $true)]
    [string]$SiteUrl
)

$ErrorActionPreference = "Stop"

# --- CLI Configuration ---
m365 cli config set --key "output" --value "json"
m365 cli config set --key "errorOutput" --value "stdout"
m365 cli config set --key "showHelpOnFailure" --value "false"
m365 cli config set --key "printErrorsAsPlainText" --value "false"
m365 cli config set --key "prompt" --value "false"
$env:CLIMICROSOFT365_NOUPDATE = "1"

# --- Helper Function ---
function Invoke-CLICommand {
    [CmdletBinding()]
    param(
        [parameter(Mandatory = $true, ValueFromPipeline = $true)] $input
    )

    $output = $input
    if ($null -eq $output) { return $null }

    $parsedOutput = $output | ConvertFrom-Json
    if ($parsedOutput -isnot [Array] -and $null -ne $parsedOutput.error) {
        throw $parsedOutput.error
    }

    return $parsedOutput
}

# --- Authentication ---
m365 login --ensure

# --- Script Logic ---
try {
    $site = m365 spo site get --url $SiteUrl | Invoke-CLICommand
    Write-Host "Site: $($site.Title)" -ForegroundColor Green
}
catch {
    Write-Error "Script failed: $($_.Exception.Message)"
    exit 1
}

Write-Host "Script completed successfully" -ForegroundColor Green

© pnp, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .github/skills/cli-microsoft365-script of pnp/cli-microsoft365-mcp-server.

Open the folder on GitHubat commit 6d89707

Compare with similar skills

CLI Microsoft365 Script next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

CLI Microsoft365 Script compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
CLI Microsoft365 Script this skillpnp/cli-microsoft365-mcp-server131—~3.3kAutomated safety check: PassMIT
Entra Agent Usergithub/awesome-copilot40k1 repos~2.3kAutomated safety check: PassMIT
Ms365 Tenant Managerborghei/Claude-Skills874—~1.8kAutomated safety check: PassMIT
Workiqmicrosoft/work-iq1k—~15kAutomated safety check: PassCustom licence
Ms365 Tenant Manageralirezarezvani/claude-skills28k1 repos~2.8kAutomated safety check: PassMIT
Msgraph Filesautomateyournetwork/netclaw674—~1kAutomated safety check: PassApache-2.0

Similar skills

  • Entra Agent User

    github/awesome-copilot

    Official

    Create Agent Users in Microsoft Entra ID from Agent Identities, enabling AI agents to act as digital workers with user identity capabilities in Microsoft 365 and Azure environments.

    40k GitHub starsUsed in 1 repo~2.3k tokens
    Documents & OfficeAuto-check passed
  • Ms365 Tenant Manager

    borghei/Claude-Skills

    Microsoft 365 tenant administration for Global Administrators.

    874 GitHub stars~1.8k tokensUpdated today
    Documents & OfficeAuto-check passed
  • Workiq

    microsoft/work-iq

    Official

    WorkIQ tools for Microsoft 365 workplace data and actions. An agent skill from microsoft/work-iq.

    1k GitHub stars~15k tokensUpdated today
    Documents & OfficeAuto-check passed
  • Ms365 Tenant Manager

    alirezarezvani/claude-skills

    Microsoft 365 tenant administration for Global Administrators.

    28k GitHub starsUsed in 1 repo~2.8k tokens
    Documents & OfficeAuto-check passed
  • Msgraph Files

    automateyournetwork/netclaw

    Read OneDrive and SharePoint files via the Microsoft 365 MCP server — list folder contents, fetch item metadata, inspect versions and sharing permissions.

    674 GitHub stars~1k tokensUpdated yesterday
    Documents & OfficeAuto-check passed
  • Msgraph Visio

    automateyournetwork/netclaw

    Upload and retrieve Visio (.vsdx) and other diagram files in OneDrive/SharePoint via the Microsoft 365 MCP server.

    674 GitHub stars~1k tokensUpdated yesterday
    Documents & OfficeAuto-check passed

More from pnp/cli-microsoft365-mcp-server

  • CLI Microsoft365

    pnp/cli-microsoft365-mcp-server

    Use CLI for Microsoft 365 to manage Microsoft 365 tenants from the terminal.

    131 GitHub stars~3.5k tokensUpdated yesterday
    Auto-check passed

Questions about CLI Microsoft365 Script

What does CLI Microsoft365 Script do?

Write PowerShell scripts using CLI for Microsoft 365 commands to automate Microsoft 365 management tasks. CLI Microsoft365 Script is an agent skill from pnp/cli-microsoft365-mcp-server. Write PowerShell scripts using CLI for Microsoft 365 commands to automate Microsoft 365 management tasks.

When should I use CLI Microsoft365 Script?

CLI Microsoft365 Script fits situations like: : writing PowerShell scripts that use m365 commands; automating SharePoint Online provisioning; automating Entra ID user/group management; automating Teams setup.

How do I install CLI Microsoft365 Script in Claude Code?

Run `npx skills add pnp/cli-microsoft365-mcp-server --skill cli-microsoft365-script -a claude-code`. Or copy the skill folder (.github/skills/cli-microsoft365-script in pnp/cli-microsoft365-mcp-server) into .claude/skills/cli-microsoft365-script in your project. Claude Code loads it when a task matches its description.

How do I install CLI Microsoft365 Script in Codex?

Run `npx skills add pnp/cli-microsoft365-mcp-server --skill cli-microsoft365-script -a codex`. Or copy the skill folder (.github/skills/cli-microsoft365-script in pnp/cli-microsoft365-mcp-server) into .agents/skills/cli-microsoft365-script in your project. Codex loads it when a task matches its description.

Can I use CLI Microsoft365 Script in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add pnp/cli-microsoft365-mcp-server --skill cli-microsoft365-script -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/cli-microsoft365-script, .gemini/skills/cli-microsoft365-script, .github/skills/cli-microsoft365-script and .opencode/skills/cli-microsoft365-script in your project.

What does CLI Microsoft365 Script need to run?

Going by SKILL.md and its folder, CLI Microsoft365 Script needs credentials named CERT_PASSWORD and CLIENT_SECRET. Our summary lists: A credential in CLIENT_SECRET.

Does CLI Microsoft365 Script access the network?

SKILL.md names 2 domains. In commands or code: contoso.sharepoint.com and graph.microsoft.com; the agent is likely to contact these when it follows the instructions. This is read from the text; nothing was executed.

Is CLI Microsoft365 Script safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does CLI Microsoft365 Script use?

CLI Microsoft365 Script is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does CLI Microsoft365 Script use?

About 3.3k tokens (SKILL.md is roughly 13k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to CLI Microsoft365 Script?

Skills that share tags, products or a category with CLI Microsoft365 Script: Entra Agent User (github/awesome-copilot, 40k stars), Ms365 Tenant Manager (borghei/Claude-Skills, 874 stars), Workiq (microsoft/work-iq, 1k stars) and Ms365 Tenant Manager (alirezarezvani/claude-skills, 28k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains CLI Microsoft365 Script?

pnp (a GitHub organization) maintains it in pnp/cli-microsoft365-mcp-server, which has 131 GitHub stars. The repository holds 2 skills in this directory. The repository was last updated on October 5, 2026.

Source: pnp/cli-microsoft365-mcp-server on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.