Microsoft 365 governance PowerShell scripts

PowerShell automation guide for M365 admin tasks

Industry and PowerShell expert Todd Klindt will guide you through all the different ways to use PowerShell with M365.

PowerShell automation can be a really powerfull tool in admin’s work.

If you read my last article, Using AI to write PowerShell scripts, you’re probably very eager about the idea of using AI to show off your PowerShell skills. But where should you point all that excited energy? Allow me to suggest Microsoft 365.

It’s a huge platform with thousands of repeatable tasks that are just begging to be automated. And if you’re an M365 admin, you probably have some tasks that would benefit from automation. While there are other ways to automate M365, let’s stick with PowerShell for now. In this paper I’ll talk about all the different ways to use PowerShell automation with M365 and show you some examples of my favorites.

PowerShell for automating admin tasks

I mentioned there are many ways to automate Microsoft 365. Automation, by whatever means, in general, is great for repetitive tasks. It gives you consistency and easy repeatability. It also frees you up to do more exciting tasks. PowerShell takes that even further.

Since it’s a scripting language it provides free documentation for the task you’re automating. Of course you should properly document it, and your PowerShell code is a great starting place. It’s even better if you’ve commented it well. PowerShell gives you the opportunity to be the toolmaker, not just the tool user.

Properly written PowerShell can be run by other people, giving you even more time to hone your skills in other areas. You can spend your day automating additional tasks while other people run your code when they need it.

If you really want to take your skills to another level, you can use PowerShell to write Azure Functions. This is code that runs in Azure and can be triggered by any number of activities. For instance, you could have any Azure Function, written in PowerShell, that’s triggered when a list item is created in SharePoint.

This Azure Function could then create a user in Entra ID, or create a new SharePoint site, or give a user access to a SharePoint site, anything you can imagine. The beauty is that your simple PowerShell scripts can very quickly be used to power very powerful cloud functionality. With that in mind, let’s talk about some of the things you can do.

Which tasks are not worth automating with PowerShell

I like to break my administration tasks down into two areas: tenant and service level, and object level. Tenant and service level tasks include editing SharePoint-wide sharing settings or adding a domain to a tenant. While you can do these things with PowerShell—sometimes, that’s the only way—they aren’t good candidates for scripting or automation.

They don’t happen often enough to really be worth the effort. As you start exploring which tasks to do with PowerShell and which ones to try to automate, keep that distinction in mind. While you should do tenant activities in PowerShell, you’ll get more benefits from automating object-level tasks than tenant-level tasks.

PowerShell-automation

The PowerShell basics you need

In order to make the most of PowerShell with M365 you’ll need some foundational pieces in place. You’ll need to make sure you’ve got PowerShell 7 installed on your machine. Windows machines come with PowerShell 5 installed. That’s an older version of PowerShell, and the modern modules won’t work with it.

Not only is PowerShell 7 a free download, but it will install on more than just Windows. You can install it on Windows, Linux, MacOS, ARM, and even Docker. If you’re on a platform that supports it, like Windows, Linux, or MacOS, you’ll also want to install Visual Studio Code.

While not strictly required like PowerShell 7, VS Code is Microsoft’s recommended scripting environment for PowerShell. With the help of Microsoft’s PowerShell extension, it provides help like proper formatting and IntelliSense. It will be a great help as you’re honing your PowerShell automation skills.

Finally, before we get our hands dirty with PowerShell, I also have to suggest that you get a development tenant to practice on. This basically gives you the freedom and flexibility to try whatever you’d like to without the fear of breaking your production tenant. If you don’t already have a tenant to test in, you can sign up for a Developer Sandbox Subscription from Microsoft.

Once you’ve got PowerShell 7 installed and you’ve got a tenant to work with, it’s time to load up some modules. Modules are how you add functionality to PowerShell. Each M365 product team has a PowerShell module for their product. Do you want to manage SharePoint Online? Load up the Microsoft.Online.SharePoint.PowerShell module. Does Teams need something tweaked? That’s the MicrosoftTeams module. This approach has its pluses and its minuses. The list of pluses is short, but powerful.

The main advantage of this model is that no one knows the product better than its own product team. If anyone should be in charge of writing the tooling for a product, it should be the product team. The other major advantage is that any time a team wants to put out an update to their PowerShell module, they can do it regardless of where the other products are in their release cadence.

As soon as Exchange has something new they want to release, they can release it and the PowerShell to manage it without waiting for PowerApps to be ready. In general, when new functionality is rolled out to a product, that product’s PowerShell module is the first way to interact with it. 

The downsides of this model can be significant, too. Each of these teams knows their product well, but they may not know PowerShell as well. This sometimes leads to PowerShell modules and cmdlets that don’t conform to well-known PowerShell standards. Once you start using PowerShell consistently, you notice some common conventions.

For instance, all nouns are singular. All of the verbs are on the approved list. Containers are created with New- cmdlets, items in those containers are created with Add- cmdlets. Those are a few examples. As you start using the different modules, you’ll find they aren’t consistent with PowerShell conventions or even with each other. It’s a small price to pay for the functionality we get from these modules.

Exchange PowerShell automation

What modules do we get from Microsoft? There’s plenty. Like I said above, each product group has their own. We’ll start by looking at the Exchange module. Its name is ExchangeOnlineManagement and you can install it with this line:

Install-Module -Name ExchangeOnlineManagement

Depending on which platform (Windows, Linux, Mac, etc) you’re installing the module on you have to do some additional tweaking to get it ready to log in. Like most of the Microsoft modules, if you have the Exchange Administrator role, you connect to Exchange Online with a Connect command:

Connect-ExchangeOnline

Once you’re logged in you have cmdlets to do all the high-level Exchange management you need to do like create connectors, set compliance, etc. You can also use this module to manage individual objects like mailboxes and groups. As you start automating practices and requiring more reports this module is your goto for Exchange based processes. The Exchange Online team was the first to make a PowerShell module, and its maturity shows.

Teams PowerShell automation

There’s also a module for Teams management. It can be installed with the following line: 

Install-Module -Name MicrosoftTeams

The Teams module is relatively young. You’ll notice the majority of the cmdlets have a “CS” prefix and are inherited from the old Communications Server module. They deal primarily with policies and configuration. There are a handful of Teams native cmdlets for creating Teams, Channels, and Apps. For the most part you’ll only use this module to configure Teams and any voice configurations you need to make. You use Connect-MicrosoftTeams to get started.

SharePoint PowerShell automation

The SharePoint Module from Microsoft has been around a while and like Exchange, is also fairly mature. Like the other modules, it can be installed from the PowerShell Gallery with this command: 

Install-Module Microsoft.Online.SharePoint.PowerShell 

Once you have it installed, use Connect-SPOService to get connected to SharePoint Online. Once you’re connected you can use the cmdlets in this module to set tenant wide settings, connect hub sites, and other administrative tasks. The smallest object you can manipulate is a site. 

Power Platform PowerShell automation

Not to be left out, the Power Automate team has not one, but two PowerShell modules for you to use when interacting with Power Apps and Power Automate flows. One module is for admins, and the other is for creators. The installation is a bit tricky, so I won’t get too deep into it here.

There are a few things you should know before installing it. First, it only runs on PowerShell 5. The installation guide walks you through how to make sure you’re using the right PowerShell environment when you install and use it. Second, the functionality is spread across two modules, and the order in which you install them is important, so make sure you follow the guide.

Finally, unlike the other modules, this module doesn’t have a Connect cmdlet. To connect to the Power Platform you use the cmdlet Add-PowerAppsAccount.

PowerShell automation and Graph SDK

I mentioned before that each group having their own module led to a fragmented experience. One potential solution to this problem is the Microsoft Graph PowerShell SDK module. The aim of this module is to replace the deprecated MSOnline and AzureAD modules and be an all-encompassing module for all your M365 management needs.

It connects directly to the Graph API to do all of this. Sounds pretty great, doesn’t it? Unfortunately, it has some rough edges. It loads dozens of modules and over 9000 cmdlets. By comparison, the SharePoint module has around 300 cmdlets. With all those cmdlets, finding what you’re looking for can be tough.

Another downside of the Graph SDK is that most, if not all of the cmdlets are computer generated, as opposed to being coded manually by humans. This is good, in that nothing gets overlooked, but it’s bad because most of the cmdlets are tough to use and have little to no documentation or examples. The promise of the Microsoft Graph PowerShell SDK is great, but it’s not there yet.

PowerShell automation and PnP.PowerShell

Last, but not least, I want to tell you about the PnP.PowerShell module. This module, while not made by Microsoft has earned being mentioned in the same circles as the official ones when it comes to PowerShell automation. It started as a simple set of SharePoint cmdlets made by the Patterns and Practices team, but it’s grown into an indispensable tool that every M365 admin should have. With an active team of highly skilled developers volunteering their time to work on it, it gets updated and improved upon on a near constant basis. It currently does a good job covering SharePoint, Teams, Power Platform, and Entra ID. 

Which should you use?

I get this question a lot, especially when folks notice there’s an overlap between the modules. My advice is to stay in one module, if your task allows you to. For instance, if you’re creating a bunch of Exchange resources, don’t make your Microsoft 365 Groups with the Teams module, unless you have to.

Using one module generally means you only have to connect to M365 once, as opposed to once for each module. This also makes your scripts easier for you, and other people, to follow. They will be dependent on fewer external resources, which is good as fast as this all changes.

I don’t deal with Exchange much, so my goto module is PnP.PowerShell. It handles all the SharePoint, group, and user activities I need. I’m able to get things done with a single, high-quality, mostly consistent module that is updated regularly. If I find I need to do something, the PnP.PowerShell module can’t do I may be able to do it through the Graph with the Invoke-PnPGraphMethod cmdlet. That lets us interact directly with the Graph without needing to load the Graph PowerShell SDK.

PowerShell examples

Let’s look at some of the stuff you can do with PowerShell and M365. One common task is creating an M365 Group, including all its resources. Here’s how each module handles that:

$DisplayName = "Accounting Team" 

$Alias = "Accounting" 

$Description = "Accounting Team for Contoso" 

 

# Exchange 

Connect-ExchangeOnline -UserPrincipalName admin@contoso.com 

New-UnifiedGroup -DisplayName $DisplayName -Alias $Alias -Description $Description -AccessType Private 

# No Team 

 

# Teams 

Connect-MicrosoftTeams 

New-Team -DisplayName $DisplayName -Description $Description -MailNickname $Alias -Visibility Private 

# Everything 

 

# SharePoint 

Connect-sposervice -url https://contoso-admin.sharepoint.com 

New-SPOSite -Url "https://contoso.sharepoint.com/sites/$($Alias)" -Title $Description -Template STS#3 -Owner admin@contoso.com -StorageQuota 1000 

# No Group, No Team 

 

# Microsoft Graph SDK 

Connect-MgGraph -Scopes "User.Read.All", "Group.ReadWrite.All" 

New-MgGroup -DisplayName $DisplayName -Description $Description -MailEnabled $true -MailNickname $Alias -SecurityEnabled $false -Visibility "Private"  

# No Team 

 

# PnP.PowerShell 

Connect-PnPOnline -Url https://contoso.sharepoint.com 

New-PnPMicrosoft365Group -DisplayName $DisplayName -Description $Description -MailNickname $Alias -Visibility Private -CreateTeam 

# Everything

As you can see, it’s quite a mess. Many of the cmdlets don’t create the Teams Team, since you could also connect the group to Yammer (now known as “Viva Engage”). The SharePoint cmdlet doesn’t even create the Group. You have to “Groupify” the SharePoint site separately. This mess is why I generally use the PnP.PowerShell. It does it all in one command, all neat and tidy.

While we’re talking about Groups, let’s see how you get a list of all the Groups in your tenant:

# Exchange Online 
Get-UnifiedGroup 

 

# SharePoint 

Get-SPOSite | Where-Object Template -eq "GROUP#0" 

 

# Microsoft Graph SDK 

Get-MgGroup 

 

# PnP.PowerShell 

Get-PnPMicrosoft365Group

Teams doesn’t have a way to get Groups without a Team.

Once again, I find myself leaning towards the PnP.PowerShell cmdlet. It has the information I want; DisplayName, ID, HasTeam, and SiteURL with the option to get more information if I need it.

Conclusion on PowerShell automation

Automating commonly performed tasks with PowerShell is a great way to increase your skills and advance your career. PowerShell is an outstanding automation tool, and Microsoft 365 is a platform just itching to be automated. While PowerShell and M365 are a powerful combination of technologies, the relationship can get a bit complicated.

You have many options for interacting with M365 using PowerShell. Many of the product teams at Microsoft have created their own, and the community has also stepped up with the PnP.PowerShell module. Knowing what your options are, and when to use them is powerful and will serve you well.

Related Posts