My Most Used PowerShell Scripts for Managing SharePoint Online

My Most Used PowerShell Scripts for Managing SharePoint Online

Last updated on October 16, 2021

Working with Office 365, you too might have already started building up a collection of useful PowerShell scripts; the ones you always keep on going back to when you need to perform certain operations for a tenant. If you haven’t yet, consider doing so — the earlier, the better!

This month I’m sharing with you some of my toolkit; a collection of PowerShell scripts that are suitable for many purposes, and which I currently find myself using the most while working with SharePoint Online. If you like, you can use them as a base for your collection, or add them to your existing script library. I hope these simple scripts will help you in your day-to-day work with SharePoint Online. Sharing is caring!

Table of Contents

1 Preparations
2 Working with Sites
2.1 Get Site Properties
2.2 Get All Sites (By Type)
2.3 Hub Site Creation
3 Upload Files to a Library
4 Managing Permissions
4.1 Set a Site Collection Administrator
4.2 Remove a Site Collection Administrator
4.3 Set a Site Collection Administrator for All Sites (of Type)
4.4 Add a User to a Site Permission Group
4.5 Add a User to an Office 365 Group
4.6 Setting List Permissions for All Sites
5 Site Customizations
5.1 Hide Default Themes
5.2 Remove All Custom Themes
5.3 Site Designs
5.4 PnP Templates

Preparations

To run these scripts, you need to install the SharePoint Online Management Shell and PnP PowerShell modules if you have not yet done so.

  • SharePoint Online Management Shell
    • For PowerShell 5.0 or newer, run this command
      Install-Module Microsoft.Online.SharePoint.PowerShell
    • For older versions, download and run the SharePoint Online Management Shell installation package.
      After that, run this command in PowerShell as administrator:
      Import-Module Microsoft.Online.SharePoint.PowerShell -DisableNameChecking
  • To install PnP PowerShell, run this command as administrator:
    Install-Module SharePointPnPPowerShellOnline -AllowClobber

It’s good to update both of the modules regularly to get the latest changes and possible new commandlets.

If you want to run the scripts by executing .ps1 files, you also need to run this command as administrator: Set-ExecutionPolicy Unrestricted

And a quick note in case you are using multifactor authentication (MFA): you need to change the authentication in the scripts slightly.

  • In the Connect-SPOService commandlet, remove the -Credential parameter and its value. Doing so brings up the web login instead, which will prompt you for the authentication code.
  • For Connect-PnPOnline, omit the -Credentials parameter and its value, and replace it with -UseWebLogin.

I chose not to use web login by default, because not everyone uses MFA yet, and it is just so handy to be able to Ctrl + V a set of credentials from KeePass!

Working with Sites

Get Site Properties

Let’s start off with the very basics. Use this script when you want to view all property values for a specific site collection or to get a list of all the available properties for site collections.

param(
    [Parameter(Mandatory)]
    [String]$siteUrl
)

Connect-SPOService ("{0}-admin.sharepoint.com" -f ($siteUrl.Substring(0, $siteUrl.IndexOf(".sharepoint.com")))) -Credential (Get-Credential)

Get-SPOSite $siteUrl | select *

Read-Host -Prompt "Press Enter to exit"

Get All Sites (By Type)

There are many situations where you need to get a list of all the site collections in your tenant. PowerShell comes especially handy if you want to get a list of communication sites because there is no such list in the graphical user interface at the moment. To list all site collections in your tenant (and their types), run the script below. You can choose to display more properties (the ones you viewed with the previous script) by adding them to the select statement. I often find myself checking the SharingCapability. You can also optionally filter the results by providing a template code when prompted. The most common ones are the following:

Site type Template
Communication Site SITEPAGEPUBLISHING#0
Modern Team Site GROUP#0
Classic Team Site STS#0
param(
    [Parameter(Mandatory)]
    [String]$tenant
)

Connect-SPOService ("https://{0}-admin.sharepoint.com" -f $tenant) -Credential (Get-Credential)

Get-SPOSite -Limit All | select Template -unique | out-host

$siteType = Read-Host "Optional template to filter by (or just hit Enter to get all sites)"

$sites = Get-SPOSite -Limit All

if ($siteType -ne "")
{
    $sites = $sites | where { $_.Template -eq $siteType }
}

$sites | select Url, Template | Sort-Object Template, Url

Read-Host -Prompt "Press Enter to exit"

Hub Site Creation

If you want to create a hub site, you first need to create a regular site and then convert it into a hub. Here we’ll first create a communication site and then register it as a hub site.

param(
    [Parameter(Mandatory)]
    [String]$tenant,
    [Parameter(Mandatory)]
    [String]$siteTitle,
    [Parameter(Mandatory)]
    [String]$siteUrlName,
    [Parameter(Mandatory)]
    [String]$siteDescription,
    [Parameter(Mandatory)]
    [String]$siteDesign, # Possible values: Topic, Showcase, Blank
    [Parameter(Mandatory)]
    [String[]]$principals
)

Connect-SPOService ("https://{0}-admin.sharepoint.com" -f $tenant) -Credential (Get-Credential)

$siteUrl = "https://{0}.sharepoint.com/sites/{1}" -f $tenant, $siteUrlName

New-PnPSite -Type CommunicationSite -Title $siteTitle -Url $siteUrl -Description $siteDescription -SiteDesign $siteDesign

Register-SPOHubSite $siteUrl -Principals $principals

Read-Host -Prompt "Press Enter to exit"

You can also revert the operation, and convert a hub site back into a regular communication site.

param(
    [Parameter(Mandatory)]
    [String]$siteUrl
)

Connect-SPOService ("{0}-admin.sharepoint.com" -f ($siteUrl.Substring(0, $siteUrl.IndexOf(".sharepoint.com")))) -Credential (Get-Credential)

Unregister-SPOHubSite $siteUrl

Read-Host -Prompt "Press Enter to exit"

Upload Files to a Library

This script uploads all files from a specified folder to a library and updates their metadata. You can add more properties to update after the title if you like.

param(
    [Parameter(Mandatory)]
    [String]$siteUrl,
    [Parameter(Mandatory)]
    [String]$libraryInternalName,
    [Parameter(Mandatory)]
    [String]$drivePath
)

$connection = Connect-PnPOnline $siteUrl -Credentials (Get-Credential) -ReturnConnection 

$files = Get-ChildItem $drivePath

Write-Host ("Uploading {0} files" -f $files.Length)
     
foreach ($file in $files)
{
    Add-PnPFile -Path $file.FullName -Folder $libraryInternalName -Values @{ "Title" = $file.Name; } -Connection $connection
}

Read-Host -Prompt "Press Enter to exit"

Managing Permissions

Even though you are an administrator in a tenant, you don’t automatically have access to all of the sites. To be able to browse sites, you need to give permissions to yourself. For modern team sites, you can do this via Groups in the Admin center, but for communication sites, you always need to give yourself permissions via PowerShell.

Set a Site Collection Administrator

With the script below, you can add yourself or some other user as the site collection administrator.

param(
    [Parameter(Mandatory)]
    [String]$siteUrl,
    [Parameter(Mandatory)]
    [String]$loginName
)

Connect-SPOService ("{0}-admin.sharepoint.com" -f ($siteUrl.Substring(0, $siteUrl.IndexOf(".sharepoint.com")))) -Credential (Get-Credential)

$supress = Set-SPOUser -Site $siteUrl -LoginName $loginName -IsSiteCollectionAdmin $true

$obj = New-Object PSObject
$obj | Add-Member "SiteUrl" $siteUrl
$obj | Add-Member "LoginName" $loginName
$obj | Add-Member "IsSiteAdmin" (Get-SPOUser -Site $siteUrl -LoginName $loginName | select IsSiteAdmin).IsSiteAdmin
 
$obj | out-host

Read-Host -Prompt "Press Enter to exit"

Remove a Site Collection Administrator

There may also be a time when you need to remove those permissions. For example, you might want to give yourself site collection administrator permissions only temporarily for performing some specific operation on a site.

param(
    [Parameter(Mandatory)]
    [String]$siteUrl,
    [Parameter(Mandatory)]
    [String]$loginName
)

Remove-PnPSiteCollectionAdmin -Owners $loginName -Connection (Connect-PnPOnline $siteUrl -Credentials (Get-Credential) -ReturnConnection)

$obj = New-Object PSObject
$obj | Add-Member "SiteUrl" $siteUrl
$obj | Add-Member "LoginName" $loginName
$obj | Add-Member "IsSiteAdmin" (Get-SPOUser -Site $siteUrl -LoginName $loginName | select IsSiteAdmin).IsSiteAdmin
 
$obj | out-host

Read-Host -Prompt "Press Enter to exit"

Set a Site Collection Administrator for All Sites (of Type)

When we want to make a user a site collection admin on all sites, we need to get all of the sites and loop through them. You can filter the sites by a template if you want to grant permissions only on, e.g., communication sites.

param(
    [Parameter(Mandatory)]
    [String]$tenant,
    [Parameter(Mandatory)]
    [String]$loginName
)

Connect-SPOService ("https://{0}-admin.sharepoint.com" -f $tenant) -Credential (Get-Credential)

Get-SPOSite -Limit All | select Template -unique | out-host

$siteType = Read-Host "Optional template to filter by (or just hit Enter to get all sites)"

$sites = Get-SPOSite -Limit All

if ($siteType -ne "")
{
    $sites = $sites | where { $_.Template -eq $siteType }
}

foreach ($site in $sites)
{
    $supress = Set-SPOUser -Site $site.Url -LoginName $loginName -IsSiteCollectionAdmin $true

    $obj = New-Object PSObject
    $obj | Add-Member "SiteUrl" $site.Url
    $obj | Add-Member "LoginName" $loginName
    $obj | Add-Member "IsSiteAdmin" (Get-SPOUser -Site $site.Url -LoginName $loginName | select IsSiteAdmin).IsSiteAdmin
    
    $obj   
}

Read-Host -Prompt "Press Enter to exit"

Add a User to a Site Permission Group

Now, what about other permissions? With the script below, you can add a user to a site permission group on a site. If you add a loop around the script, it becomes even more useful.

param(
    [Parameter(Mandatory)]
    [String]$siteUrl,
    [Parameter(Mandatory)]
    [String]$loginName,
    [Parameter(Mandatory)]
    [String]$siteGroupName # e.g. Owners, Members, Visitors
)

Connect-SPOService ("{0}-admin.sharepoint.com" -f ($siteUrl.Substring(0, $siteUrl.IndexOf(".sharepoint.com")))) -Credential (Get-Credential)

$group = Get-SPOSiteGroup -Site $siteUrl | where {$_.Title -like ("*{0}" -f $siteGroupName) }

Add-SPOUser -Site $siteUrl -LoginName $loginName -Group $group.Title

Read-Host -Prompt "Press Enter to exit"

I’d like to remind you that when you are granting permissions to guests, you might also need to adjust the SharingCapability setting of the site before guests can access. I’ve previously written a blog post about disabling guest access, but you can use those same scripts for enabling access too if it is not more restrictive on tenant level. Also remember that if your site is a modern team site, you most likely also want to adjust the external sharing setting for its Office 365 group, too.

Add a User to an Office 365 Group

Now, I know that the scope of this article is managing SharePoint, but because modern team sites are so tightly connected to their Office 365 groups, I can not just pass them by here. With this script, you can add a user to an Office 365 group with the specified role (e.g., owner or member). This script becomes even handier if you are setting group memberships for several groups and users, e.g., based on a CSV file.

param(
    [Parameter(Mandatory)]
    [String]$siteUrl,
    [Parameter(Mandatory)]
    [String]$loginName,
    [Parameter(Mandatory)]
    [String]$membershipType #e.g. Owner or Member
)

$cred = Get-Credential
$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $cred -Authentication Basic -AllowRedirection
Import-PSSession $session

Connect-PnPOnline $siteUrl -Credentials $cred

$groupId = (Get-PnPSite -Includes GroupId).GroupId.ToString()

# Owners also need to be members
if ($membershipType.ToLower() -eq "owner" -or $membershipType.ToLower() -eq "owners")
{
    Add-UnifiedGroupLinks -Identity $groupId -LinkType "Member" -Links $loginName -Confirm:$false
}

Add-UnifiedGroupLinks -Identity $groupId -LinkType $membershipType -Links $loginName

Remove-PSSession $session

Read-Host -Prompt "Press Enter to exit"

Setting List Permissions for All Sites

Here is a bit longer script. It allows you to change the permission level of a site permission group for specified document libraries or lists on all sites. The script has come in handy in those cases where we have systematically provisioned sites with the same library structure, and later on, there has arisen a need to alter their permissions. You can provide a full group name or just Visitor, Member or Owner for the siteGroupName parameter. You can specify multiple lists/libraries and roles to add and remove. Typical roles to assign are “Read”, “Edit” and “Full control”.

param(
    [Parameter(Mandatory)]
    [String]$tenant,
    [Parameter(Mandatory)]
    [String[]]$listTitles,
    [Parameter(Mandatory)]
    [String]$siteGroupName,
    [Parameter(Mandatory)]
    [String[]]$rolesToAdd,
    [Parameter(Mandatory)]
    [String[]]$rolesToRemove
)

$cred = Get-Credential
Connect-SPOService ("https://{0}-admin.sharepoint.com" -f $tenant) -Credential $cred

$sites = Get-SPOSite -Limit All

foreach ($site in $sites)
{
    Write-Host $site.Url

    $web = Get-PnPWeb -Connection (Connect-PnPOnline $site.Url -Credentials $cred -ReturnConnection)
        
    foreach ($listTitle in $listTitles)
    {
        $list = Get-PnPList $listTitle -Web $web

        if ($list -eq $null) {
            Write-Host "There is no library called" $listTitle
            continue 
        }

        Write-Host "Setting permissions for" $listTitle

        $list.BreakRoleInheritance($true, $true)
        $list.Update()

        $list.Context.Load($list)
        $list.Context.ExecuteQuery()

        $group = Get-SPOSiteGroup -Site $site | where {$_.Title -like ("*{0}" -f $siteGroupName) }

        foreach ($roleToAdd in $rolesToAdd) {
            Set-PnPGroupPermissions -Identity $group.Title -List $listTitle -AddRole $roleToAdd
        }

        foreach ($roleToRemove in $rolesToRemove) {
            Set-PnPGroupPermissions -Identity $group.Title -List $listTitle -RemoveRole $roleToRemove
        }
    }
}

Read-Host -Prompt "Press Enter to exit"

Site Customizations

For adding a new custom color theme to your tenant, please check my viral blog post about creating multicolored themes; it contains the script and instructions for creating color palettes. What I haven’t included in that blog post is removing custom themes and hiding the default themes, and I’ll share those with you here.

Hide Default Themes

By hiding the default themes, you can ensure the site owners always use company branded themes on their sites.

param(
    [Parameter(Mandatory)]
    [String]$tenant,
    [Parameter(Mandatory)]
    [String]$hideThemes
)

switch ($hideThemes)
{
    "true" { $hide = $true }
    "false" { $hide = $false }
}

Connect-SPOService ("https://{0}-admin.sharepoint.com" -f $tenant) -Credential (Get-Credential)

Set-SPOHideDefaultThemes -HideDefaultThemes:$hide

if (Get-SPOHideDefaultThemes) {
    Write-Host "Default themes are now hidden."
}
else {
    Write-Host "Default themes are now visible."
}

Read-Host -Prompt "Press Enter to exit"

Remove All Custom Themes

I mostly run this script in my development tenant to clean up after testing.

param(
    [Parameter(Mandatory)]
    [String]$tenant
)

Connect-SPOService ("https://{0}-admin.sharepoint.com" -f $tenant) -Credential (Get-Credential)

$themes = Get-SPOTheme

foreach ($theme in $themes) {
    Remove-SPOTheme -name $theme.Name
}

Read-Host -Prompt "Press Enter to exit"

Site Designs

For PowerShell scripts related to site designs, I’m again going to direct you to another one of my blog posts: The Ultimate Guide to Site Designs and Site Scripts. See how I’m a good little dev here by not duplicating the code? 😉

PnP Templates

If you are not yet familiar with PnP provisioning, I much recommend you do so here.

This script saves an existing site configuration as a template. There is a list of parameters you can use for defining the template contents in more detail.

param(
    [Parameter(Mandatory)]
    [String]$siteUrl,
    [Parameter(Mandatory)]
    [String]$fileName
)

Connect-PnPOnline -Url $siteUrl -Credentials (Get-Credential)
Get-PnPProvisioningTemplate -Out ("{0}\{1}.xml"-f $PSScriptRoot, $fileName)

Read-Host -Prompt "Press Enter to exit"

And with this script, you can apply the template configurations to an existing site.

param(
    [Parameter(Mandatory)]
    [String]$siteUrl,
    [Parameter(Mandatory)]
    [String]$fileName
)

Connect-PnPOnline -Url $siteUrl -Credentials (Get-Credential)
Apply-PnPProvisioningTemplate -Path ("{0}\{1}.xml"-f $PSScriptRoot, $fileName)

Read-Host -Prompt "Press Enter to exit"

 

Now that you’ve browsed through all these scripts, do tell, would you like to see more of this kind of posts, or did you not find it that useful? I’d also be interested to know if you have assembled your toolkit and shared it somewhere online? Let me and your fellow developers know in the comments!

And speaking about sharing, in addition to these blog posts, I’m also sharing some tips on Twitter which are too small for a blog post. Follow me on Twitter if you are interested in reading the those or getting notifications when I publish new articles. Thank you very much for taking the time to read this article, I hugely appreciate it as always, and until next time!

Laura

Congratulations, you’ve just finished reading one of my blog post classics! Please note that I’ve personally stopped answering questions left in the comments section of this article because I no longer actively work with the topic. Still, you are more than welcome to comment and ask questions as other readers also often offer their help.


114 thoughts on “My Most Used PowerShell Scripts for Managing SharePoint Online”

  • Hi Laura,

    Thank you for publishing your scripts. They have been extremely helpful for examples and tutorials! Your site has been a life saver for some issues I had been running into while building out a script to easily create SubSites within SPO. Looking forward to finding more great material from your site!

    Chris

  • Hi Laura,

    You have a lot of great scripts for managing SharePoint Online. I need assistance either fixing a script or replacing it with a new one. I have about 5 lists in SharePoint Online that I have to refresh nightly with the latest data from our data warehouse. My Powershell scripts run nightly to delete all the items in the list and then query the data warehouse and add items to the list, one row at a time until it is fully populated. The script uses module Microsoft.Online.SharePoint.PowerShell and commands like $Context.Load($List) and $Context.ExecuteQuery().

    This worked fine until recently when I started getting 429 errors which means the site is busy and is throttling any activity.

    I researched ways to handle these situations but I have not yet found the best solution. Is there a way to catch the exception when it runs the ExecuteQuery() and then do a retry of adding the item?

    Or is there a better way to do this, where I could perhaps only update the list items that are different? Every option I think of seems to complicated.

    Can you point me the right direction or is there a better place to ask this question?

    • Hey, I found a solution to the 429 issue!

      The Fix – use ExcecuteQueryAsync() instead.

      By replacing ExecuteQuery() with ExecuteQueryAsync(), it will run the command in Asynchronous mode, which places it in a queue that Microsoft can manage and the script can continue running.  Microsoft finds that more palatable.  It is the same amount of requests, but it does not have to be executed immediately.

      Maybe this will help someone else.

  • Hello Laura! i have a specific question i did not find an answer from anywhere:

    How in the name of all the gods can i create a view on hub site (comms site) to include all and only the folders from associated sites including a custom content type and column with certain value? help me Laura Kokkarinen. You’re my only hope.

    Mikko

  • Hi Laura,
    Great site and nice scripts! Thanks for sharing them. Do you have a script in your back pocket which lists all folders and their permissions? Output like
    /internal/instructions Admin, Joe, Jane
    /internal/IT Admin, Joe
    etc.

    I would like to get list of folders in SPO site and who has permissions to which folder. We have a lot of unique permissions in one site and it would be useful to get the list.

  • Thanks for sharing, it has saved me a lot of time not having to reinvent the wheel over and over again 😊
    Now could be a good time to return the favour: be very careful when creating modern sites as I have seen a number of tenants where Connect-PnPOnline right after New-Pnpsite did not connect to the site as the permissions had not yet been assigned ( I guess) and a call to say Get-pnplist gave a 403 error. So looping until the site is ready (5-10 sec) or just start-sleep – second 10 seems to do the trick.
    Another issue with the async creation of modern sites is that the timer job that creates the Taxonomyhiddenlist can be delayed serveral minutes, so deploying managed metadata fields must be postponed 🙁
    That aside I love direction and speed of sharepoint and Teams

    • Hi Kasper,

      Yes, the delays in modern site and Office 365 group creation before everything is ready can be really annoying. Typically we need to implement some kind of a retry mechanism, so if everything isn’t ready yet, we’ll sleep for a bit and then retry until the operation we are attempting succeeds.

      Laura

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.