Skip Ribbon Commands
Skip to main content
 

 Follow Me

 
 
 
 
 

 SharePoint Blog List

 
  
  
  
  
  
  
  
  
  
  
  
  
  
  
Todd Klindt's home page > Todd Klindt's Office 365 Admin Blog
What's going on with TK.
October 08
How to Get the Microsoft 365 Connected User with PnP PowerShell

As I’ve alluded to before, I spend a lot of time in PowerShell, and most of it in the loving embrace of the PnP.PowerShell module. From time to time I find myself wanting to include logic in my scripts based on who the script is being run as, who connected to Microsoft 365. The majority of my connections where with the good old username and password combination. When that’s the case, I could use this to find how who I had connected as:

Connect-PnPOnline -Url https://m365x995492.sharepoint.com/
admin@M365x995492.onmicrosoft.com
((Get-PnPConnection).PSCredential).username

It looks like this:

image

That worked great, right up to the point where I didn’t just log in with username credentials. For instance, the Sympraxis tenant requires MFA so I have to connect with the –Interactive parameter:

image

Old Faithful let me down. Back to the drawing board. Poking around the Internet I saw some smart folks were using this method:

$ctx = Get-PnPContext
$ctx.Load($ctx.Web.CurrentUser)
$ctx.ExecuteQuery()
$ctx.Web.CurrentUser
$UPN = $ctx.Web.CurrentUser.Email

image

I haven’t tried it yet with Certificate authentication, so I’m not sure how it reports that. The good news is that method also works with a username and password login:

image

I’ve created a Function, Get-TKPnPCurrentUser, to make that short and easy to use.

tk

ShortURL: https://www.toddklindt.com/POSHGetCurrentPnPUser

September 16
Rapid Digital Transformation webinar with SysKit

Like mixing chocolate and peanut butter I’m mixing two of my favorite things, SysKit and Sympraxis, in one magnificent webinar. Toni, Marc, Derek, and I will be talking about how technology can improve business and how we work with customers. And then, we’ll spend a lot of time with the mics open metaphorically and taking questions from the audience. So if you want to stump Marc or Derek, now is your chance. Smile 

The magic happens Wednesday, Sep 22 at 10:00 am CDT. Please sign up free and join us. We’d love to have you.

tk

September 14
Delete _Some_ File Versions in SharePoint Online with PowerShell

I recently got a fun email from a client. Their tenant had run over its storage allocation. After some quick investigation they realized that they had a few dozen files with a lot of versions, the average number of versions was over 1200 per file. Some files had over 3000 versions. The kicker is that these files were big, dozens or hundreds of MBs each. Thousands of files, 100s of MBs a piece? Pretty soon you’re talking about some real storage space! His question to me was how he could easily delete these unneeded versions quickly and easily? PowerShell, of course!

Before we get to the golden PowerShell nuggets at the end, how did they end up here in the first place? Versions have been around forever in SharePoint but in the last couple of years there was a change. As part of their strategy to protect us from ransomware, Microsoft turned on versioning for all Document Libraries in SPO, and set the maximum number of versions to 500. That way if malware encrypted your site you could just roll your documents back to an unencrypted version. Pretty clever approach. The bad news is that if you don’t know this is in place, like my customer didn’t, you can chew up a lot of space by frequently uploading large files. Back to our story…

My mancrush on the PnP PowerShell is well documented, so of course that’s the first place I looked for a solution. Sure enough, there it was, shining like a beacon of hope on a foggy night, Remove-PnPFileVersion. Do you hear angels singing? I know I do. I sent that off to my customer, dusted off my hands, and leaned back, put my hands behind my head and basked in self satisfaction. It was glorious.  Until the customer replied…

He didn’t want to delete all of the versions of a file. He wanted to something like delete all except the last 5 versions. Remove-PnPFileVersion has 2 parameter sets. One that deletes a single version (by its ID number) and one that deletes all versions. No middle ground. While my previous victory was short lived, I knew PowerShell would come through for me here too.

Long story short, I scribbled down a quick PowerShell script that will delete the file versions beyond the number you wish to keep. This customer had a CSV file of the files they wanted to prune, so I added support for that. I have posted the files on GitHub (DeleteOldVersions.ps1 and VersionDelete.csv ) and I’ll go over the mechanics here.

Here is what the CSV file looks like, if you want to use that:
image

The Remove-PnPFileVersion cmdlet natively handles URLs in the forms of lines 2-5. I added support for the URL having the tenant name as well because a report the customer had included that and I wanted to make it easy for them to read. I’m good like that.

Here’s the code:

First I connect to the site:
$SiteUrl = https://m365x995492.sharepoint.com/sites/blah
Connect-PnPOnline -Url $SiteUrl

You’ll need to adjust those to your own situation. Then I load up the CSV file. If you don’t want to do this with a CSV file you don’t have to. You can manually put the file name in.

$FileList = Import-Csv .\VersionDelete.csv

Then I pick how many file versions I want to keep:

$VersionsToKeep = 5

Next I walk through the $FileList.

foreach($File in $FileList) {

If the FileName property has the $SiteUrl in it I take it out.

$Filename = $File.FileName.Replace($SiteUrl,"")

Then I grab all the FileVersions of the file:

$FileVersions = Get-PnPFileVersion -Url $Filename

Get-PnPFileVersion does not show the Current Version, so it will always show one fewer version than what you see in the UI. If the number of versions is greater than the version we said we wanted to keep in $VersionsToKeep then I create a list of versions in $DeleteVersionList to delete:

if ($FileVersions.Count -gt $VersionsToKeep) { 
$DeleteVersionList = ($FileVersions[0..$($FileVersions.Count - $VersionsToKeep)])

With that list in hand I walk through it and run Remove-PnPFileVersion against it. In the code in GitHub I have commented out the line (Line 33 as of 9/14/21) that actually deletes the version. You’ll have to uncomment that to do anything.

foreach($VersionToDelete in $DeleteVersionList) {
Remove-PnPFileVersion -Url $Filename -Identity $VersionToDelete.Id –Force

To make piping easy I output the versions deleted as a CustomObject:

$Output = [PSCustomObject]@{
PSTypeName = 'TKDeletedFileVersion'
Filename     = $Filename
DeletedVersion     = $($VersionToDelete.VersionLabel) 
            }

$Output

When you look at the code you’ll notice a line at the top,
$VerbosePreference = "Continue" 

If you uncomment that line it will light up the Write-Verbose statements in the code. Set it back to SilentyContinue to make them go away.

This is what it looks like with Verbosity off:

image

If you need to audit which versions are being deleted you can pipe that to Export-CSV and save it to a file.

If you want to create some versions to test this with you can use the following PowerShell:

$DocLib = “Shared Documents”
Get-PnPListItem -List $Doclib | select Id,@{l="FileLeafRef";e={$_.FieldValues.FileLeafRef}}

That’ll get you the files in the Document Library. You can use this command to touch them and create 6 new versions:

0..5 | foreach {Set-PnPListItem -List $Doclib -Identity 4 -Values @{"FileLeafRef"="Building materials licences to budget for Storytelling.docx"}}

It is also possible to filter versions by date. That way instead of deleting all but the last 5 versions, you would be able to delete all of the versions older than 30 days. That’s a blog post for a different day though.

tk

ShortURL: https://www.toddklindt.com/PoshDeleteSPOFileVersions

 

September 08
The Great Blog Crash of 2021 (so far…)

I don’t know if I could pick my favorite blog crash. There have been so many over the years. It would be like picking my favorite kid, or favorite tablet. I just can’t.

This most recent crash was caused by the drive the VM my blog is running on failing. “But what about backups??” I hear you all shouting in unison. I had VM backups in place, but I had moved the VM to a different server and the backup job didn’t follow along. I also had database backups. These were going directly to my NAS device. Unfortunately when I replaced my NAS in December I forgot to point the SQL backup job to the new location. My only backup was a lonely database backup from December. So, with my SharePoint 2010 and SQL 2012 CDs in hand, I reinstalled everything, and here we are.

I was able to republish the blog posts I’ve written since the backup, so there was no loss there. I did lose the comments you’ve left and I’ve responded to. Sorry about that. I’m in the process of putting it all back together, but there are a few things I haven’t finished yet. I’m putting the list here, so we can all keep track of it. If you see something wrong and it’s not on this list, send me a tweet or leave me a comment (I’ll try not to lose it) and I’ll add it to the list. 

To Do List:
Get Search working
Update SharePoint 2010 Builds list (9/10/21)
Update SharePoint 2013 Builds list (9/13/21)
Go through IIS Logs and look for failed requests for ShortURLs I’ve missed
Verify all the http to https redirects work properly
Create SQL backup job​ (9/13/21)
Create VM backup job
Fix broken images in blog posts

Thanks for sticking around,

tk

ShortURL: https://www.toddklindt.com/FirstBlogCrash2021

June 25
Create Lots of Test SharePoint Sites (or Teams or Groups) with PowerShell

Throughout my IT career I have had to create tens (or hundreds, or thousands) of objects to test something. It could be a bunch of Windows Users, a bunch of folders, files, etc. It seems like every time that happens I end up starting from scratch on the process. To stop that silly cycle I decided to make the process official by blogging it. Let’s stop this madness!

This time it started with my friend Michal Pisarek posting this tweet:

image

Orchestry needs to test their lifecycle features and he wanted to stress test it real good!

As is often the case, I see a tweet like that and my first thought is “Challenge Accepted!” The mechanics of how to create Teams with PowerShell is pretty simple but where this really gets tricky, at least for me, is the names. Especially if you’re looking at creating 20,000 like Michal is. In the past the way I’ve handled that is the old tried and true “Adjective Noun Number” formula. To get near 20,000 I wanted a long list of nouns and adjectives to pull from. I scoured the Internet and pulled together two files, nouns.txt and adjectives.txt. You can find them in this GitHub repo. Then I tack a random two digit number at the end to reduce the chance of collision. I put those files in the same directory as this PowerShell script and let ‘er rip!

Connect-PnPOnline -Url https://CONTOSO-admin.sharepoint.com -Interactive

# import the files with nouns and adjectives
$Nouns = Get-Content .\nouns.txt
$Adjectives = Get-Content .\adjectives.txt

# Number of Teams to create
$NumberOfTeams = 3
$Index = 1

while ($Index -le $NumberOfTeams) {
    # Generate Random stuff
    $TeamNoun = $Nouns | Get-Random
    $TeamAdjective = $Adjectives | Get-Random
    $TeamNumber = Get-Random -Maximum 100
    $TeamDisplayName = "$TeamAdjective $TeamNoun $TeamNumber"
    Write-Host "$Index - $TeamDisplayName"
    New-PnPTeamsTeam -DisplayName $TeamDisplayName -MailNickName $($TeamDisplayName.Replace(" ","")) -Description $TeamDisplayName -Visibility Public -AllowGiphy $true
    $Index++
}

You can find the file CreateLotsofTeams.ps1 in that same GitHub repo.

You can alter the nouns and adjectives files as you see fit. Set the the $NumberofTeams variable to how many Teams you want and you’re set. This script uses the venerable PnP.PowerShell module. You’ll need that installed and its Azure Application registered before you can run this. Be sure to change the Connect-PnPOnline line to reflect your tenant’s name, unless you actually work for Contoso.

Because of some weird timing, the current version of the PnP.PowerShell, 1.6.0, won’t work with this script as there is a bug in New-PnPTeamsTeam that prevents it from actually creating a Team. Ironic, I know. I put notes in the CreateLotsofTeams.ps1 file on how to handle that. But if you’re running it and it looks successful but no Teams are being created, look there first.

Also, for whatever reason, when you look at the Groups Sites in SharePoint they don’t show up as being Teams enabled, but they really are.

image

You can see in this crudely mocked up screenshot that the Teams are in the Teams client even though SharePoint Admin Center swears they don’t exist.

And while this script’s purpose in life is to create lots and lots of Teams, it could be easily modified to create lots and lots of anything. If you just need Groups, swap out New-PnPTeamsTeam with New-PnPMicrosoft365Group. If you just need SharePoint sites, use New-PnPTenantSite. Folders? Add-PnPFolder. I think you see where I’m going with this. Smile 

If you’re like Michal and you’re going to create 20,000 Teams, or anything, I hope you have a comfortable chair. It’s going to take a while. Michal is seeing about 1 Team a minute. It’s going to take him a couple of weeks at that pace. Almost certainly PowerShell is the bottleneck in this situation. If you’re looking at a similar situation, my advice is to open up another PowerShell window and run another instance of CreateLotsofTeams.ps1 there. And maybe run a few instances on another machine entirely. In the past that has helped me speed this things up considerably.

Enjoy.

tk

ShortURL: https://www.toddklindt.com/PoshLotsandLotsofTeams

June 25
How to Register the PnP.PowerShell App Registration if You’re not a Tenant Admin

I’ve done a few articles about the new PnP.PowerShell module. One of the biggest changes from its ancestor, SharePointPnPPowerShellOnline, is that it requires the registration of an Azure Application before you can connect with it. In this blog post I’m going to explain how to get that Azure App registered if you’re not a Tenant Admin in your tenant.

You don’t need to be a Tenant Admin to use the PnP.PowerShell cmdlets. You don’t even need to be a SharePoint Admin or a site collection admin. There are plenty of cmdlets you can run, like Add-PnPFile if you’re only a Member of the site. However, before you can run the most import PnP cmdlet of all, Connect-PnPOnline, the PnP Azure Application has to be registered in your tenant by a tenant admin. If it’s not, you’ll get a sad message that looks like this: 

image

Here’s the text:

Connect-PnPOnline: AADSTS65001: The user or administrator has not consented to use the application with ID '31359c7f-bd7e-475c-86db-fdb8c937548e' named 'PnP Management Shell'. Send an interactive authorization request for this user and resource.

In most cases the person introducing the PnP.PowerShell module is a tenant admin, so it’s not an issue. They run Register-PnPManagementShellAccess and Bob’s your uncle. But it’s not uncommon for an organization to be large enough that the SharePoint or Microsoft 365 Admin team is not a tenant admin. In that case the Tenant Admin, who likely doesn’t know what a PnP.PowerShell is, has to register the Azure App before the SharePoint Admin can enjoy the bliss that is PnP.PowerShell. Fortunately, there’s an easy enough solution, the Consent URL.

The Consent URL is the URL to a web page your Tenant Admin can go to to consent the PnP.PowerShell Azure App without needing to install anything, or really know anything about the PnP.PowerShell. There are a few ways to get the Consent URL. It doesn’t matter how you do it, they all get you to the same place.

The easiest way to remember is to run Register-PnPManagementShellAccess –ShowConsentUrl after installing the PnP.PowerShell. You’ll be asked to log in, but you don’t need to be an sort of admin. It’s only logging in so it knows when tenant you’re in. Then it will give you the Consent URL. It looks like this:

image

https://login.microsoftonline.com/651c433d-d221-4bb3-ac77-392f4bf06a6b/adminconsent?client_id=31359c7f-bd7e-475c-86db-fdb8c937548e

The part in the red box is your tenant’s ID.You had to log so the cmdlet could get that number. The Client_id refers to the PnP.PowerShell, so it’s the same everywhere. 

You can also specify your tenant’s name instead of its ID. This works as well:


https://login.microsoftonline.com/tenantname.onmicrosoft.com/adminconsent?client_id=31359c7f-bd7e-475c-86db-fdb8c937548e

Whether you get the URL from running Register-PnPManagementShellAccess –ShowConsentUrl or by copying it out of this blog post and putting your tenant’s information, send that URL to your Tenant Admin. When they browse to the page it will look like this:

image

All they need to do is click Accept and you’re ready to go.

That’s a pretty long, scary list of permissions, and it might spook some admins. Accepting this does not give everyone in your tenant all of those permissions. The PnP.PowerShell Azure App uses Delegation, which means any user using it to access objects in Microsoft 365 has to have permission to access it. The PnP.PowerShell does not allow anyone access to anything they don’t otherwise have access to. If they don’t believe you, have them try. Have someone that cannot open up a SharePoint site in the browser try to connect to it with Connect-PnPOnline. They won’t be able to.

If they want to check out what the Azure App has permission to, or heaven forbid, remove it, you can browse to the Azure AD Portal and find it in the Enterprise Applications.

image

The Permissions blade will show you all delegated permissions the app has. Feel free to poke around, but resist the urge to change any, even if you’re positive you’ll never use them. I promise it’ll only hurt you in the future.

After your tenant admin has done all of that you should be able to get back to all that PowerShell and PnP goodness.

tk

ShortURL: https://www.toddklindt.com/PoshRegisterPnP

February 09
What is a “Dev Tenant” and why would you want one?

My fellow Sympraxian Julie Turner recently published an outstanding blog post on what a Microsoft 365 Dev Tenant is and why you should get one. I’ve been a long time user of Dev or Demo tenants and I find them to be an invaluable resource both for people trying to gain skills in Microsoft 365, but also grizzled old veterans like myself. Like the old saying goes, “The question isn’t whether you have a Test Site, it’s whether you have a Production Site.” If you don’t have a Dev or Demo Tenant to test things in then you’ve just demoted your Production Tenant to a Test Tenant. Don’t be that person. Smile I did a session on the PnP PowerShell today for Petri.com. One of the people there was hesitant to try out PowerShell on their tenant. I recommended they get one of these Dev tenants and hone their skills there.

Be one of the cool kids. Read Julie’s blog post and grab a Dev Tenant.

tk

ShortURL: https://www.toddklindt.com/GetDevTenant

January 19
The new PnP.PowerShell Module is live!

As of January 19th, 2021 the new and improved PnP.PowerShell module is no longer prerelease and is ready for the masses. You can also find it in a GitHub repo. It replaces the venerable SharePointPnPPowerShellOnline module we all know and love. The new module has a lot changes, but here are a few of the highlights:

  • PnP.PowerShell only works with online products like SharePoint Online and Microsoft 365. It does not work with SharePoint Server platforms like SharePoint 2013, 2016, or 2019.
  • It is built on .NET core 3.1 so it runs on non Windows platforms like Linux, Mac, and Azure Functions. SharePointPnPPowerShellOnline is built on .NET 4.6.1 and only runs on Windows.
  • PnP.PowerShell is focused on all of Microsoft 365, not just SharePoint. SharePointPnPPowerShellOnline started out focused on SharePoint and other things have snuck in over the years, but were never supported as well as we would have liked.
  • Authentication relies on App Registrations. To facilitate the cross application support the module relies on Graph API permissions as opposed to straight up usernames and passwords. SharePointPnPPowerShellOnline uses App Registrations as well, but it’s not built on that premise like PnP.PowerShell is.

If you can, you should uninstall the SharePointPnPPowerShellOnline module and install PnP.PowerShell everywhere. You should also stop using Windows PowerShell 5.1 and move over to PowerShell 7.x. There are a couple of legit reasons why you can’t though. I have a whole blog post, “Using both PnP PowerShell Modules with PowerShell 5 and PowerShell 7” that covers how to have both modules and both shells available to you. For those of you with short attention spaces I’ll give you the tl;dr:

Open PowerShell 7.x in Administrator mode and uninstall all the modules:

Uninstall-Module SharePointPnPPowerShellOnline -Force –AllVersions
Uninstall-Module PnP.PowerShell -Force –AllVersions

That should clear all the appropriate modules out of PowerShell 7.x. You’re good there.

Now open Windows PowerShell 5.1 in Administrator mode. Here you’ll uninstall all the modules and reinstall them:

Uninstall-Module SharePointPnPPowerShellOnline -Force –AllVersions
Uninstall-Module PnP.PowerShell -Force –AllVersions

For good measure I close down the Windows PowerShell 5.1 host and open a new one before I install the new version. That might not be necessary, but it feels like cheap insurance. Either way, in a Windows PowerShell 5.1 Administrator shell install both modules:

Install-Module -Name SharePointPnPPowerShellOnline -Scope Allusers
Install-Module -Name PnP.PowerShell -Scope Allusers –AllowClobber

I added a little chocolate to the install commands to make them go down easier. First, I added -Scope Allusers to make sure Windows PowerShell 5.1 installs the module in a place that PowerShell 7.x can find it. I think any modules installed in an Admin window are installed there by default, but I’m not sure. Again, cheap insurance. Second, to the second (and any subsequent) module I added –AllowClobber. This tells PowerShell that it’s okay to install a module that has cmdlets that collide with cmdlets already on the system. This is okay because of the difference between installing a module and importing a module. You can read more about that in my previous post.

If you need one of the on-prem SharePoint Server modules use a line like this:

Install-Module -Name SharePointPnPPowerShell2019 -Scope Allusers –AllowClobber

You can have as many of these modules installed as long as you install the later ones with –AllowClobber.

Once you’ve got the modules installed you need to tell PowerShell which one to load. That’s done with the Import-Module cmdlet. When you open a new PowerShell window, or at the top of any PowerShell scripts, you can specify which PnP Module you want PowerShell to use. For instance:

Import-Module PnP.PowerShell

or

Import-Module SharePointPnPPowerShellOnline

or

Import-Module SharePointPnPPowerShell2019

Then you know you’ll get the version of Connect-PnPOnline (or whatever) your code is running.  In theory you could swap them in and out by using Remove-Module, but in reality there are some fiddley PowerShell things in the background (the AppDomain) that keeps this from being successful. My previous blog post does show you how to have multiple versions of the PnP module imported in the same window or script at the same time.

But really, stick to PnP.PowerShell and PowerShell 7 if humanly possible. Smile

I hope you find that all helpful. I don’t know about you, but I’m really excited about this new module.

tk

ShortURL: https://www.toddklindt.com/PoshNewPnPModule


January 13
Using both PnP PowerShell Modules with PowerShell 5 and PowerShell 7.

Since the PnP team announced that the venerable SharePointPnPPowerShellOnline module was going to be replaced by the shiny, new PnP.PowerShell module there has been some confusion on which module to use and which PowerShell to use. I have good news, the answer is “D. All of the above.” In this blog post I’ll show you how you can have both modules installed and use them interchangeably as well as use either of them on whatever version of PowerShell tickles your fancy.

A Few Words about Modules

Before we can get into how to do this I want to spend some pixels on why we have to do it the way we do. I intended to publish this blog post a couple of weeks (okay, months) ago but as I was doing my research I kept learning more. Most of learning was around the “Module” cmdlets and what each of them does. In order to get the two modules to cooperate we need to use the right Module cmdlets at the right time. Here are the Cliff’s Notes:

The System

Several Module cmdlets deal with what PowerShell Modules are installed on your system with PowerShellGet. There are a lot of ways to install Modules into PowerShell and PowerShellGet is one of the most popular and it’s built into PowerShell. Here are the pertinent cmdlets and what they do:

  • Install-Module – Downloads a module from a repository (the PowerShell Gallery by default) and installs it on the system.
  • Get-InstalledModule – Lists the modules that were installed on the computer with PowerShellGet
  • Uninstall-Module – Uninstalls a packages from that computer that was installed with PowerShellGet

The key here is that the scope of those commands, and the other PowerShellGet cmdlets, is the whole computer

The Host

“Host” is a fancy word for PowerShell window or console. There are a few common Module cmdlets that deal only with the host they’re run in. Here are some of my favorites:

  • Import-Module – Imports a module that was already installed on the computer into the host
  • Get-Module – Lists the modules currently imported into the host. The –ListAvailable parameter shows modules installed on the computer that can be imported into the host
  • Remove-Module – Unloads (unimports? exports?) a module from the host

These cmdlets are all part of Microsoft.PowerShell.Core as opposed to PowerShellGet.

The Import Business

Now we know how to get a module installed onto our computer, and how to manually coax it into our host. But most of us have never done all this Import-Module business but everything seems to work. How’s that? PowerShell has the ability to automatically load modules when they’re needed. The entire, exciting, story of how PowerShell imports modules is chronicled here, but I’ll give you the highlights. If you try to use (or reference it with something like Get-Command) a cmdlet that isn’t in a module already loaded PowerShell will walk through the PSModulePath locations looking into each module for the cmdlet you’re trying to run. If it finds it, it implicitly imports that module and Bob’s your uncle. You can use $env:PSModulePath to see where PowerShell will look. It’s important to note that Windows PowerShell 5 and PowerShell 7 have similar, but different PSModulePaths. Here’s what it looks like in both. I added -split “’;” to put each path on its own line, and I piped it through Sort-Object to make them easy to keep track of.

image

image

To make either Module, SharePointPnPPowerShellOnline or PnP.PowerShell, available to both Windows PowerShell 5 and PowerShell 7 it has to be installed in one of the paths that both versions of PowerShell will look in. For backwards compatibility PowerShell 7 looks in the PowerShell 5 paths, so I do the Install-Module bit in PowerShell 5 and PowerShell 7 gets it for free. You could install it in both, that works. But this way keeps you from having to update it both places every time a new version comes out. The PnP.PowerShell module is aimed for PowerShell 7, but also works fine in PowerShell 5. Erwin told me they may remove that support in future, but for now it’s safe.

You can see from the screenshots that I have my Known Folders redirected to OneDrive. When installing these modules PowerShell was installing them to my personal folders and it was causing problems. A couple of the Module cmdlets don’t handle that well. To get around some of that chicanery I had to install the modules in a different path. The easiest way I found to do that was the specify the scope Allusers, like this:

Install-Module PnP.PowerShell -Scope AllUsers

In PowerShell 5 that installed into C:\Program Files\WindowsPowerShell\Modules\PnP.PowerShell, which PowerShell 7 can see so it can be imported into a host of either version and it’s not in OneDrive.

Using Both Modules in Both PowerShells

We know both PowerShells can run both modules if we install it right. To get everything playing nicely I uninstalled both modules from both versions of PowerShell using Uninstall-Module -Force –AllVersions. In one case Uninstall couldn’t clean it all up so I had to go into the file system and delete the folder manually. I also had to close the host various times as it had imported the module I was trying to uninstall and I couldn’t get it unloaded in that host.

After all the uninstalling was done I closed all of the PowerShell windows I had open and I opened a PowerShell 5 window in Admin mode. I installed the old module with this:

Install-Module -Name SharePointPnPPowerShellOnline -Scope Allusers

That made it so it could be imported into both PowerShell 5 and PowerShell 7. Next I installed the PnP.PowerShell module. This one took a bit of extra coaxing.  It’s currently in prerelease so Install-Module requires the –AllowPrerelease parameter. The version of PowerShellGet in PowerShell 5 does have that. I had to upgrade PowerShellGet first with this line:

Update-Module -Name PowerShellGet

I close the window and opened a new one for good measure. This put me at version 2.2.5 of PowerShellGet. One problem solved. The second problem is that PnP.PowerShell and SharePointPnPPowerShellOnline share most of their cmdlet names, so a regular Install-Module is going to fail. To fix that we need to use the –AllowClobber parameter. The whole thing looks like this:

Install-Module -Name PnP.PowerShell -AllowPrerelease –AllowClobber

At first this sounds scary, but remember, Install is just dropping the bits onto your computer. You can still control which module gets loaded in a script or host. To do that using Import-Module. The key is to use Import-Module before you do anything that will trigger PowerShell implicitly loading the module for you. If you want to force your script to use a specific module include one of these lines at the top:

Import-Module PnP.PowerShell

or

Import-Module SharePointPnPPowerShellOnline

You would do the same thing in a PowerShell window when you open it to run some cmdlets. I’ve been doing this for a few weeks and it works well.

Using Both Modules in the Same Host

Now I’m just going to show off a bit. Smile We know we can load either module into a host, but what if we need both modules into the same host or script at the same time? It came be done! It sounds like magic, but it works. The key is using the –Prefix parameter of Import-Module. If you want both modules loaded and available in the same window you need to import one with a prefix. It looks like this:

Import-Module SharePointPnPPowerShellOnline -Prefix Old

image

You can see from the screenshot that both sets of cmdlets are available, with the SharePointPnPPowerShellOnline versions having the additional prefix of “Old.” I wouldn’t recommend doing this as a normal course of action, but it’s good to know it’s there. PowerShell 7 is a little fussy when doing this. I had issues importing the old version if I had already imported the PnP.PowerShell module explicitly or implicitly, which is why I didn’t in this screenshot. Get-Command implicitly loaded it for me, so that wasn’t an issue. PowerShell 5 handles it more gracefully.

The End

There it is, the culmination of weeks (maybe months) of me fiddling around to understand the inner workings of PowerShell module installing and importing. Thanks to Jeff Hicks for holding my hand and answering all my dumb questions. Hopefully my pain will make your transitions from PowerShell 5 to PowerShell 7 and from SharePointPnPPowerShellOnline to PnP.PowerShell easier.

tk

ShortURL: https://www.toddklindt.com/PoshBothModules

December 28
Making the move from SharePointPnPPowerShellOnline to PnP.PowerShell

As the new year approaches I find myself making changes. Like everyone on the planet I plan to eat less and exercise more. I also plan on making another change, I’ll be moving away from my friend the venerable old SharePointPnPPowerShellOnline module to the new, sexier PnP.PowerShell. I thought I’d blog the experience so you all can follow along.

Why the Move?

The SharePointPnPPowerShellOnline module has been good to me over the last few years. It has helped me do amazing things both in PowerShell and in SharePoint. Why would I ever give that all up? Because the times, they are a-changing. The SharePointPnPPowerShellOnline module is being retired and all of that effort is being channeled to the PnP.PowerShell module. The SharePointPnPPowerShellOnline module will continue to be around for the on-prem versions of SharePoint Server (though not updated), but for SharePoint Online/Office 365/Microsoft 365 you’ll need to use the PnP.PowerShell module.

Why did the PnP team decide this? You can get the whole story at the official PnP PowerShell page, but there are two main reasons. The first is wider platform support. SharePointPnPPowerShellOnline had .NET dependencies that meant it would only run on Windows PowerShell. Emphasis on Windows. When that module was first created 6 years ago that made perfect sense, but since then PowerShell has gone open source and now runs on a variety of platforms like Mac, Linux, a variety toasters, etc. Since SharePointPnPPowerShellOnline required Windows it couldn’t follow PowerShell onto those other platforms. The PnP.PowerShell module was written without those Windows dependencies so it can run anywhere PowerShell can run. You may not use a Mac, but this also means that you can use PnP.PowerShell more easily in cloud environments like Azure Functions.

The second reason was to give wider application support. The SharePointPnPPowerShellOnline mostly supported SharePoint, as the name would suggest. These days you can’t manage SharePoint Online without also needing to touch Microsoft Groups, Teams, etc. The PnP added more and more cmdlets to handle that, but there were some big authentication changes that needed to be made to fully support all of the other applications. They started adding some of that to SharePointPnPPowerShellOnline, but it was cobbled together a bit. While they were refactoring the module for .NET Standard they went ahead and made some sweeping authentication changes that mean we’ll be able to use it more easily for SharePoint and all of the other applications we know and love.

First Step PowerShell 7

The first step to moving to the PnP.PowerShell module is to install PowerShell 7, also called PowerShell Core. I’m on Windows, so that’s the process I’m going to walk through. Windows comes with PowerShell, Windows PowerShell 5.1. To use PowerShell 7 we’ll have to install it. Windows PowerShell 5.1 and PowerShell Core 7 can happily coexist on your machine, so you don’t have to worry about breaking any of your other existing PowerShell scripts or tools.

image

You can see from this screenshot I have both versions of PowerShell installed and running on this machine. You can use the system variable $PSVersionTable to see which version of PowerShell your host is currently using. It’s also pretty easy to pick the version of PowerShell you want when firing it up.

image

While PowerShell 7.x can coexist with Windows PowerShell 5.x, it will replace PowerShell 6.x if you have that installed.

I installed PowerShell 7 by downloading the installation MSI from the GitHub page. For me that was the Windows x64 platform and I grabbed the stable build. That file was PowerShell-7.1.0-win-x64.msi, but obviously that filename will change as PowerShell 7 advances. Then I popped open a Windows PowerShell 5 (oh, the irony) prompt in Admin mode and ran the MSI.

image

The friendly wizard walked me through the process. There are few installation choices to make, but I’ve found the defaults are usually fine.

After the installation is finished you’ll have both Windows PowerShell 5.x and PowerShell Core 7.x. Time to install some modules.

The Module

Installing the PnP.PowerShell module is pretty easy, but if you have the older SharePointPnPPowerShellOnline module installed you’ll need to uninstall it first. The cmdlet names in both modules are the same, So PnP.PowerShell won’t install all of its cmdlets while there are collisions with the old version. To uninstall the SharePointPnPPowerShellOnline module open up a Windows PowerShell 5 host in Admin mode and enter this:

Uninstall-Module SharePointPnPPowerShellOnline –AllVersions

and wave a fond farewell to our old friend. After that’s finished, and you’re done sobbing, open up a shiny new PowerShell Core 7 host in Admin mode. While you’re in there opening up in Admin mode, go ahead and add PowerShell 7 to your Start Menu and your Taskbar. You’ll thank me later.

image

Then issue this command:

Install-Module PnP.PowerShell –AllowPrerelease

image

You’re all done. Since PnP.PowerShell is not an official release yet you’ll need the scary sounding –AllowPrerelease parameter. After January of 2021 you won’t need that anymore.

Finally, Authentication

I mentioned earlier that one of the reasons for changing modules was a change in authentication. I don’t want to get too deep into it in this blog post but I do want to mention that you should run Register-PnPManagementShellAccess to set up a an Azure Application Registration. This is the magic that allows the new PnP PowerShell access to all of the applications in Office 365. You may have seen this referred to as Graph API. I’ll dig into it later, but for now all you need to know is that a tenant admin needs to run the PnP.PowerShell version of Register-PnPManagementShellAccess once in your tenant. After that is in place you should be able to use Connect-PnPOnline and get connected.

tk

ShortURL: https://www.toddklindt.com/NewPnPPowerShell

1 - 10Next
 

 Subscribe to my Netcast

 

You can watch Shane and I's Cloudy Podcast live every Wednesday Morning at 10:00 am Central US time at http://www.toddklindt.com/Netcast

You can subscribe to the Podcast in the following ways:

MP3 Audio

Windows WMV video

YouTube Channel

 

 SPDocKit

 
 

 Todd's Upcoming Events

 
  
  
There are no items to show in this view of the "Events" list.