This guide provides a brief introduction to administering Microsoft 365 using
the m365
CLI targeted at new PowerShell users. Many Microsoft 365 administrative tasks can be easily automated with
PowerShell and the Microsoft 365 command line
interface (cli), such as creating and deleting user
accounts, granting and revoking licenses and many others!
The m365
CLI for Microsoft 365 is a Microsoft 365 Platform Community (PnP)
project. Microsoft 365 Platform Community is a virtual team consisting of
Microsoft employees and community members focused on helping the community
make the best use of Microsoft products. A detailed guide to the m365
cli
can be found
in the official docs.
This guide uses PowerShell which is a cross platform (Windows, Linux, OSX)
general purpose scripting language. A few PowerShell basics are introduced in
the accompanying powershell-basics.md
file.
If PowersShell is not available to you, the m365
cli can be used to similar
effect in other shells like CMD, bash, zsh, etc. Some tips for those
terminals are in the accompanying scripting-with-csv.md
file.
The m365
CLI uses NodeJS, which can be installed from
nodejs.org. You can verify that node
has
been installed by calling node --version
at the command line, which should
print a version number to the console (e.g. v16.15.1
). (Aside: in this document I use the terms "terminal" or "console" to refer to the powershell terminal)
Once NodeJS has been installed, the m365
cli tool can be
installed
by calling npm i -g @pnp/cli-microsoft365
in the PowerShell terminal.
There are several ways to log in to the m365
cli tool, and the easiest (and
default) methods is called the Device Code Flow. Typing m365 login
in the
powershell will prompt you to use a login into via
aka.ms/devicelogin using a one time code that
will be included in the prompt. The first time you login, you will be
prompted to grant permissions to the PnP Management Shell. These permissions
are required for the app to act on your behalf as you interact with the
m365
cli.
More details and alternatives to the device-code-flow can be found in the official docs.
Detailed documentation on the (many!) commands can be found here. In brief,
-
m365
commands are grouped into areas such as Azure AD (aad
),teams
,docs
,outlook
,graph
, etc. -
areas my have topics, such as the
messages
topic withinoutlook
. -
topics may have one or more verbs, such as
add
,list
,get
, andremove
-
areas, topics and verbs are combined when issuing a command. For example, messages in your outlook inbox can be listed with:
m365 outlook message list --folderName inbox
Most commands have arguments that are specific to that command that provide
additional context for the command, such as such as --folderName inbox
which previous example. The arguments for each command are listed and
explained in the docs.
Finally, there are a handful of options that are common to all commands that modify the value(s) returned by the command. The two most important common arguments are:
-
--output
( or-o
) which determines the format of the output (such asjson
,csv
,text
).json
andcsv
is obviously useful for programmatic usa of the outputs, and thetext
option is great for human readability. -
--query
which applies a JMESPath query to the command's return value in order to extract specific outputs. This is used in them365 aad license list
example below to include the SKU, Sku Name, and licenses available in the command output.
Creating users: aad user add
To create a set of users, we'll start by creating a text file called
new-user-params.txt
with the following content:
FirstName,LastName,alias
CLI-Test,User1,clitestuser1
CLI-Test,User2,clitestuser2
CLI-Test,User3,clitestuser3
Some things to note about this CSV file:
- The headers are valid PowerShell variable names, i.e. they start with a letter and contain only letters and numbers.
- The strings (like
John
) do not have quotes and the values do not contain commas. (Note that PowerShell can use CSV files with quoted strings, though other terminals mentioned in the accompanyingscripting-with-csv.md
file below cannot) - There are no spaces before or after the commas
Regarding the following code:
- For convenience we won't require the user to create a new password on
first log-in (
--forceChangePasswordNextSignIn false
) - To illustrate gathering outputs, we will not provide a password, but
instead allow aad to create a random password which will be gathered the
output file
new-users.csv
- In order to apply licenses, users must be assigned a country code. A complete list of country codes can be found here
# the name of the tenant (used as part of the username)
$tenant="mytesttenant"
# Country code
$country_code="US"
# Create the users
$user_details = Import-CSV -Path new-user-params.csv | ForEach-Object {
m365 aad user add `
--displayName "$($_.FirstName) $($_.LastName)" `
--firstName $_.FirstName `
--lastName $_.LastName `
--userName "$($_.alias)@$tenant.onmicrosoft.com" `
--accountEnabled true `
--forceChangePasswordNextSignIn false `
--usageLocation $country_code `
--forceChangePasswordNextSignInWithMfa false `
| convertFrom-Json
}
# store the user details (including the auto-generated passwords) in a CSV file
$user_details | Export-CSV -Path new-user-details.csv
# Store the User Principal Names (UPNs) in a text file
$user_details | Foreach-Object { $_.userPrincipalName } | out-file -Path userPrincipalNames.txt
This code will create a csv file (new-user-details.csv
) with the following content:
"id","businessPhones","displayName","givenName","jobTitle","mail","mobilePhone","officeLocation","preferredLanguage","surname","userPrincipalName","password"
"584de8da-3ceb-44e4-986b-02fd87fb899e","System.Object[]","CLI-Test User1","CLI-Test",,,,,,"User1","[email protected]","124skY87z9@7890"
"b5c3206f-6e9e-4255-a079-d5cdfc016744","System.Object[]","CLI-Test User1","CLI-Test",,,,,,"User1","[email protected]","1234LiVgYum7890"
"2309e050-6afd-47c4-b293-a48ec8d005f1","System.Object[]","CLI-Test User1","CLI-Test",,,,,,"User1","[email protected]","1234Joqx&9b7890"
and a text file (userPrincipalNames.txt
) with the following content:
Updating user accounts: m365 aad user set
The following code updates the user's country code and removes the requirement to change the password on next login:
$country_code="US"
# Read the file line-by-line. | Run the code block {...} for each line in the file
Get-Content userPrincipalNames.txt | foreach-Object {
m365 aad user set `
--userPrincipalName $_ `
--usageLocation $country_code `
--forceChangePasswordNextSignIn false
}
List Available licenses: m365 aad license list
To get details of the licenses that can be granted to users we can use the following command:
m365 aad license list -o text --query "[].{skuId:skuId,sku:skuPartNumber,licensesUsed:consumedUnits,totalLicenses:prepaidUnits.enabled}"
which produces the following output:
skuId sku licensesUsed totalLicenses
------------------------------------ -------------------------- ------------ -------------
b05e124f-c7cc-45a0-a6aa-8cf78c946968 EMSPREMIUM 3 250
f30db892-07e9-47e9-837c-80727f46fd3d FLOW_FREE 7 10000
3227bcb2-8448-4f81-b3c2-8c2074e15a2a Microsoft_Viva_Sales 3 200
84a661c4-e949-4bd2-a560-ed7766fcaf2b AAD_PREMIUM_P2 3 300
359ea3e6-8130-4a57-9f8f-ad897a0342f1 DYN365_CUSTOMER_VOICE_BASE 3 3
06ebc4ee-1bb5-47dd-8120-11324bc54e06 SPE_E5 262 300
Note that the GUID in the skuId
field is used in later commands to grant or
revoke licenses in the code snippets below.
Assign Licenses to Users: m365 aad user license add
In this example, the Flow Free
and AAD_Premium
licenses are granted to each user:
# comma separated list of the skuId's of the licenses to assign
$licenses = "84a661c4-e949-4bd2-a560-ed7766fcaf2b,f30db892-07e9-47e9-837c-80727f46fd3d"
Get-Content userPrincipalNames.txt | foreach-Object {
m365 aad user license add --userName $_ --ids $licenses
}
Revoke Licenses from Users: m365 aad user license remove
In this example, the Flow Free
and AAD_Premium
licenses are revoked from each user:
# comma separated list of the skuId's of the licenses to assign
$licenses = "84a661c4-e949-4bd2-a560-ed7766fcaf2b,f30db892-07e9-47e9-837c-80727f46fd3d"
Get-Content userPrincipalNames.txt | foreach-Object {
m365 aad user license remove --userName $_ --ids $licenses --confirm
}
Note that without the --confirm
option, the CLI will prompt you to confirm each license revocation.
Delete users: m364 aad user remove
.
To clean up after yourself, you may wish to delete users we created earlier like so:
Get-Content userPrincipalNames.txt | Foreach-Object {
m365 aad user remove --userName $_ --confirm
}
Nice samples!