v12 Upgrade Guidance
This page lists every breaking change in CLI for Microsoft 365 v12 and the actions required to upgrade from v11 to v12.
How to use this page
- Check the summary of breaking changes for commands that you use in your scripts.
- Use the upgrade checklist to determine which changes require action.
- For each change that applies to you, follow the steps under Action required in that change's section.
Each change has a unique ID (for example, SPO-1) and uses the same structure:
- Affected commands: commands whose behavior changed in v12.
- Change type: the kind of breaking change.
- Action required when: the condition under which you need to update your scripts or configuration.
- What changed: v11 behavior compared to v12 behavior.
- Action required: the steps to take.
- Example: v11 and v12 usage, where applicable.
Summary of breaking changes
| ID | Area | Change | Change type | Affected commands |
|---|---|---|---|---|
| AUTH-1 | Authentication | Token cache moved to the OS credential store, sign in again | Authentication | login |
| SPO-1 | SharePoint Online | Existing files are no longer overwritten by default | Default behavior | spo file add |
| SPO-2 | SharePoint Online | Options that you don't specify are no longer reset to default values | Default behavior | spo page header set |
| SPO-3 | SharePoint Online | SharePoint write permissions required, Microsoft Graph permissions no longer required | Permissions | spo contenttype sync |
| SPO-4 | SharePoint Online | Output properties renamed, removed, and added, location option required, isExternal option removed | Output, options | spo navigation node add, spo navigation node get, spo navigation node list, spo navigation node set |
| TEAMS-1 | Teams | Teams specified by name are retrieved using the Microsoft Teams endpoint | Permissions | 15 teams commands |
| GEN-1 | General | Removed items are moved to the recycle bin by default | Default behavior, options | 9 remove commands |
| GEN-2 | General | spo web alert aliases removed | Removed alias | spo web alert list, spo web alert remove |
| GEN-3 | General | properties option renamed to fields | Options | spo listitem get |
Upgrade checklist
| Change | Action required when | Action |
|---|---|---|
| AUTH-1 | You signed in using device code or browser authentication | Run m365 login again after upgrading |
| SPO-1 | You use spo file add to replace files that already exist | Add --overwrite |
| SPO-2 | You use spo page header set and rely on unspecified options being set to default values | Specify these options explicitly |
| SPO-3 | You use spo contenttype sync | Consent SharePoint AllSites.Manage (delegated) or Sites.Manage.All (application) |
| SPO-4 | You use an affected spo navigation node command | Use the new property names, add --location to get and set, remove --isExternal |
| TEAMS-1 | You specify a team by name in an affected teams command | Consent Microsoft Graph Team.ReadBasic.All and update error message checks |
| GEN-1 | You use an affected remove command | Remove --recycle, replace --skipRecycleBin with --permanent, add --permanent where items must be deleted permanently |
| GEN-2 | You use spo web alert list or spo web alert remove | Use spo web rule list or spo web rule remove |
| GEN-3 | You use spo listitem get with --properties | Use --fields |
Authentication
AUTH-1: Token cache moved to the OS credential store
- Affected commands: login and all commands that require authentication
- Change type: Authentication
- Action required when: you signed in to CLI for Microsoft 365 v11 using device code (default) or browser authentication.
After upgrading to v12, users who signed in using device code or browser authentication must sign in again using m365 login.
What changed
In v11, the MSAL token cache, which contains access and refresh tokens, was stored as an unencrypted JSON file in ~/.cli-m365-msal.json. In v12, the token cache is stored securely in the credential store of your operating system:
| Operating system | Credential store |
|---|---|
| Windows | DPAPI |
| macOS | Keychain |
| Linux | libsecret. If libsecret isn't available, the CLI falls back to an unencrypted file in ~/.cli-m365-msal-cache.json |
The first time you run v12, the CLI deletes the old ~/.cli-m365-msal.json file. Tokens from that file aren't migrated, so the new token cache starts empty. Your connections, stored in ~/.cli-m365-connection.json and ~/.cli-m365-all-connections.json, are kept.
The impact depends on the authentication type of your connection:
| Authentication type | Impact |
|---|---|
deviceCode, browser | Sign in again. The CLI can no longer refresh tokens silently. As soon as the access token stored with the connection expires, typically within an hour, the CLI prompts you to sign in. |
password, certificate, secret | No action required. The CLI retrieves new tokens using the credentials stored with the connection. |
identity, federatedIdentity | No action required. These authentication types don't use the token cache. |
For more information, see Persisting connection information.
Action required
- After upgrading, sign in again using
m365 login. If you have multiple connections, use connection list to find the connections with theDeviceCodeorBrowserauthentication type, and sign in again for each of them. - If you persist the CLI's token cache between runs, for example by mounting your home directory in a Linux container without libsecret, persist
~/.cli-m365-msal-cache.jsoninstead of~/.cli-m365-msal.json.
Example
m365 login
SharePoint Online
SPO-1: spo file add no longer overwrites existing files by default
- Affected commands: spo file add
- Change type: Default behavior
- Action required when: your scripts upload files that may already exist in the target folder and must replace them.
What changed
In v11, overwrite was an option, and existing files were overwritten by default. In v12, it is a flag that defaults to false. If a file with the same name already exists in the target folder, the command returns an error unless you specify --overwrite.
Action required
- Add the
--overwriteflag to everyspo file addcommand that must replace an existing file.
Example
m365 spo file add --webUrl https://contoso.sharepoint.com/sites/project-x --folder 'Shared Documents' --path 'C:\MS365.jpg' --overwrite
SPO-2: spo page header set no longer applies default values
- Affected commands: spo page header set
- Change type: Default behavior
- Action required when: your scripts rely on the command setting unspecified options to their default values.
What changed
In v11, options you didn't specify were set to their default values, which could unintentionally reset existing header properties. In v12, unspecified options are left untouched, so the header properties keep their current values. The type, altText, layout, textAlignment, showTopicHeader, showTimeToRead, and showPublishDate options no longer have default values.
Action required
- Find all
spo page header setcommands that rely on the default values of the options listed above. - Explicitly specify these options with the values you want.
Example
m365 spo page header set --webUrl https://contoso.sharepoint.com/sites/team-a --pageName home.aspx --type Default --textAlignment Center --showPublishDate true
SPO-3: spo contenttype sync requires SharePoint write permissions
- Affected commands: spo contenttype sync
- Change type: Permissions
- Action required when: you use this command and your app registration doesn't have the SharePoint permissions listed below.
What changed
In v11, the command used both SharePoint and Microsoft Graph APIs, requiring Microsoft Graph permissions and read-level SharePoint permissions. In v12, the command uses SharePoint only, no longer requires Microsoft Graph permissions, and requires write-level SharePoint permissions.
Required permissions in v12:
| Permission type | Resource | Permission |
|---|---|---|
| Delegated | SharePoint | AllSites.Manage |
| Application | SharePoint | Sites.Manage.All |
Action required
- Add the required SharePoint permission to your app registration and grant consent.
- Optionally, remove the Microsoft Graph permissions that you added for this command. Only do this if no other commands that you use need them.
SPO-4: spo navigation node commands use a different API
- Affected commands: see the table below
- Change type: Output, options
- Action required when: you use any of the affected commands.
What changed
The spo navigation node commands now use a different API that allows you to retrieve more detailed information about navigation nodes. As a result, their output and options have changed.
| Command | Output changed | --location required | --isExternal removed |
|---|---|---|---|
| spo navigation node add | Yes | No | Yes |
| spo navigation node get | Yes | Yes | No |
| spo navigation node list | Yes | Yes | No |
| spo navigation node set | No | Yes | Yes |
Output: the output exposes additional information, such as whether a link opens in a new tab (OpenInNewWindow). The following properties have changed:
| v11 property | v12 property | Change |
|---|---|---|
Id (number) | Key (string) | Renamed, type changed from number to string |
Url | SimpleUrl | Renamed. For linkless labels, the value is an empty string instead of http://linkless.header/ |
Children | Nodes | Renamed |
IsDocLib | - | Removed |
IsExternal | - | Removed |
IsVisible | - | Removed |
ListTemplateType | - | Removed |
| - | CustomProperties | Added |
| - | FriendlyUrlSegment | Added |
| - | IsDeleted | Added |
| - | IsHidden | Added |
| - | IsTitleForExistingLanguage | Added |
| - | NodeType | Added |
| - | OpenInNewWindow | Added |
| - | Translations | Added |
location option: spo navigation node get and spo navigation node set require the location option to specify the navigation in which the node is located. Valid values are QuickLaunch and TopNavigationBar. The error message returned when the navigation node doesn't exist has also changed.
isExternal option: the new API doesn't support marking navigation nodes as external. The isExternal option has been removed from spo navigation node add and spo navigation node set, and specifying it returns an error. SharePoint doesn't allow changing the URL of a navigation node that points to a page in the current site into an external URL. When you try this using spo navigation node set, the URL isn't updated and no error is returned.
Action required
- In scripts that process the output of
spo navigation node add,get, orlist:- Replace
IdwithKey,UrlwithSimpleUrl, andChildrenwithNodes. - Remove references to
IsDocLib,IsExternal,IsVisible, andListTemplateType. - Treat
Keyas a string, for example in comparisons. - Use
Keywhen passing the ID of a navigation node to other commands, such as spo navigation node get, spo navigation node set, or spo navigation node remove.
- Replace
- Add
--location QuickLaunchor--location TopNavigationBarto everyspo navigation node getandspo navigation node setcommand, depending on where the node is located. - Remove
--isExternalfrom everyspo navigation node addandspo navigation node setcommand.
Example
- v11
- v12
m365 spo navigation node list --webUrl https://contoso.sharepoint.com/sites/Marketing --location TopNavigationBar --query "[?Title == 'Navigation Link'].Id"
m365 spo navigation node get --webUrl https://contoso.sharepoint.com/sites/Marketing --id 2209
m365 spo navigation node set --webUrl https://contoso.sharepoint.com/sites/Marketing --id 2209 --url "https://www.microsoft.com" --isExternal true
m365 spo navigation node list --webUrl https://contoso.sharepoint.com/sites/Marketing --location TopNavigationBar --query "[?Title == 'Navigation Link'].Key"
m365 spo navigation node get --webUrl https://contoso.sharepoint.com/sites/Marketing --location TopNavigationBar --id 2209
m365 spo navigation node set --webUrl https://contoso.sharepoint.com/sites/Marketing --location TopNavigationBar --id 2209 --url "https://www.microsoft.com"
Compare the full JSON output of spo navigation node list in v11 and v12
- v11 output
- v12 output
[
{
"AudienceIds": [
"5786b8e8-c495-4734-b345-756733960730"
],
"CurrentLCID": 1033,
"Id": 2032,
"IsDocLib": true,
"IsExternal": true,
"IsVisible": true,
"ListTemplateType": 0,
"Title": "Navigation Link",
"Url": "https://contoso.sharepoint.com",
"Children": [
{
"AudienceIds": null,
"CurrentLCID": 1033,
"Id": 2033,
"IsDocLib": true,
"IsExternal": true,
"IsVisible": true,
"ListTemplateType": 0,
"Title": "Microsoft 365",
"Url": "https://microsoft365.com",
"Children": []
}
]
}
]
[
{
"AudienceIds": [
"5786b8e8-c495-4734-b345-756733960730"
],
"CurrentLCID": 1033,
"CustomProperties": [],
"FriendlyUrlSegment": "",
"IsDeleted": false,
"IsHidden": false,
"IsTitleForExistingLanguage": false,
"Key": "2032",
"Nodes": [
{
"AudienceIds": [],
"CurrentLCID": 1033,
"CustomProperties": [],
"FriendlyUrlSegment": "",
"IsDeleted": false,
"IsHidden": false,
"IsTitleForExistingLanguage": false,
"Key": "2033",
"Nodes": [],
"NodeType": 0,
"OpenInNewWindow": true,
"SimpleUrl": "https://microsoft365.com",
"Title": "Microsoft 365",
"Translations": []
}
],
"NodeType": 0,
"OpenInNewWindow": false,
"SimpleUrl": "https://contoso.sharepoint.com",
"Title": "Navigation Link",
"Translations": []
}
]
Teams
TEAMS-1: Teams specified by name are retrieved using the Microsoft Teams endpoint
- Affected commands: see the list below
- Change type: Permissions
- Action required when: you specify a team by name (for example, using
--teamNameor--name) in any of the affected commands. No action is required if you only specify teams by ID.
Affected commands:
- teams channel add
- teams channel get
- teams channel list
- teams channel remove
- teams channel set
- teams channel member add
- teams channel member list
- teams channel member remove
- teams channel member set
- teams message remove
- teams message restore
- teams tab get
- teams team archive
- teams team remove
- teams team unarchive
What changed
In v11, a team specified by name was retrieved using the Microsoft Entra groups endpoint, followed by a check whether the group is a team. In v12, the team is retrieved directly using the Microsoft Teams endpoint.
Because of this, the permissions required to retrieve a team by name changed from group permissions to the Microsoft Graph Team.ReadBasic.All permission.
Action required
- Add the Microsoft Graph
Team.ReadBasic.Allpermission to your app registration and grant consent.
General
GEN-1: Removed items are moved to the recycle bin by default
- Affected commands: see the table below
- Change type: Default behavior, options
- Action required when: you use any of the affected commands.
What changed
To prevent unintentional data loss, remove commands that previously deleted items permanently by default now move items to the recycle bin by default. To permanently delete an item, use the new --permanent flag. For consistency, the --skipRecycleBin flag has been replaced with --permanent in the commands that already moved items to the recycle bin by default.
| Command | v11 default | v11 option | v12 default | v12 option |
|---|---|---|---|---|
| entra m365group remove | Recycle | --skipRecycleBin to delete permanently | Recycle | --permanent to delete permanently |
| spe container remove | Delete permanently | --recycle to recycle | Recycle | --permanent to delete permanently |
| spo file remove | Delete permanently | --recycle to recycle | Recycle | --permanent to delete permanently |
| spo folder remove | Delete permanently | --recycle to recycle | Recycle | --permanent to delete permanently |
| spo list remove | Delete permanently | --recycle to recycle | Recycle | --permanent to delete permanently |
| spo listitem batch remove | Delete permanently | -r, --recycle to recycle | Recycle | --permanent to delete permanently |
| spo listitem remove | Delete permanently | --recycle to recycle | Recycle | --permanent to delete permanently |
| spo page remove | Delete permanently | --recycle to recycle | Recycle | --permanent to delete permanently |
| spo site remove | Recycle | --skipRecycleBin to delete permanently | Recycle | --permanent to delete permanently |
Action required
- Remove the
--recycle(-r) flag from your scripts. - Replace
--skipRecycleBinwith--permanent. - Add
--permanentto commands that must delete items permanently.
Example
- v11
- v12
m365 spo file remove --webUrl https://contoso.sharepoint.com/sites/project-x --url "/Shared Documents/Test.docx" --recycle
m365 spo file remove --webUrl https://contoso.sharepoint.com/sites/project-x --url "/Shared Documents/Test.docx"
m365 spo site remove --url https://contoso.sharepoint.com/sites/project-x --skipRecycleBin
m365 spo file remove --webUrl https://contoso.sharepoint.com/sites/project-x --url "/Shared Documents/Test.docx"
m365 spo file remove --webUrl https://contoso.sharepoint.com/sites/project-x --url "/Shared Documents/Test.docx" --permanent
m365 spo site remove --url https://contoso.sharepoint.com/sites/project-x --permanent
GEN-2: spo web alert aliases removed
- Affected commands: spo web alert list, spo web alert remove
- Change type: Removed alias
- Action required when: your scripts use
spo web alert listorspo web alert remove.
What changed
Following the retirement of SharePoint alerts, the spo web alert aliases have been removed. The commands remain available under their spo web rule names.
| Removed alias (v11) | Command to use (v12) |
|---|---|
spo web alert list | spo web rule list |
spo web alert remove | spo web rule remove |
Action required
- Replace
spo web alertwithspo web rulein your scripts.
Example
- v11
- v12
m365 spo web alert list --webUrl https://contoso.sharepoint.com/sites/Marketing
m365 spo web rule list --webUrl https://contoso.sharepoint.com/sites/Marketing
GEN-3: properties option renamed to fields in spo listitem get
- Affected commands: spo listitem get
- Change type: Options
- Action required when: your scripts use
--propertieswithspo listitem get.
What changed
To make it consistent with other commands, the properties option of spo listitem get has been renamed to fields.
Action required
- Replace
--propertieswith--fields.
Example
- v11
- v12
m365 spo listitem get --webUrl https://contoso.sharepoint.com/sites/project-x --listTitle "Demo List" --id 147 --properties "Title,Created"
m365 spo listitem get --webUrl https://contoso.sharepoint.com/sites/project-x --listTitle "Demo List" --id 147 --fields "Title,Created"