CategoryPowershell

Creating Organization Events Centre

white printer paperr
Photo Credits: https://unsplash.com/@erothermel

Holding events is a crucial task for any environment to keep employees up to date with everything taking place in the firm, however what do we do when we have too many departments, each having its own set of events? In this case an events centre to consolidate events is a good way to think about it, and this is what we did at BDO Canada!

Many companies like BDO have many departments, from Taxes, BDO Law, Industries, Digital office .. to IT, Marketing and Human Resources.. just to name a few! Each department has its own events going on and each department is its own hub site with more associated sites connected to it.

Our plan was to create yet one more site, name it Events Centre as part of the Intranet Home hub site that will show events across ONLY hub sites. For example, we have an Industries hub site, “under” that industry site we have associated sites such as Agriculture, Cannabis, etc..

You can use an Events web part on the Industries site to show events across the hub itself, this is a setting that’s available on the Events web part to show events across all sites in the hub:

Now what we want to appear on the events centre is a rollout of all “Intranet” events. To do that we created a content type specific for “Intranet Events”. To have that, we’ll need to create a content type inheriting from the basic Event content type and add it to each Events list and make it the default content type. On top of that, we want to have our own categories, because the categories that exist by default in the Events list don’t match our needs. So to achieve this, we used PnP Provisioning Templates in combination with PnP PowerShell.

PnP Templates will provision everything to the department sites once they are created such as any required site columns, content types, modifications to lists/libraries such as adding content types to these libraries, adding the custom event content type to the events list and making it the default, adding page templates, pages and so on.

The PnP PowerShell will do extra work such as changing the category column values (plus other extra stuff we needed it to do). Now we have a consistent structure for all departments created and all of them have the same content type. Back to our Events centre, we can place an Events web part for each category filtered by that categories name.

In fact we can use the category right without having to create a custom content type, but if you want to make your events scalable for later (for example, get all “Intranet” events in Search) then having your own events content type is very handy.

The end result of this would look like:

One catch is when you filter a category that contains a character such as “-“, you’ll need to replace the – with a space. For example, “Firm-wide” category will be filtered as “Firm wide”.

Notice that the categories don’t represent departments. They are shared across all departments in the environment. With the help of PnP Provisioning templates and PnP PowerShell Scripts we are able to keep this consistency across all departments.

Hope this would inspire you on the possibilities of things you can achieve with such a simple web part like the Events web part.

Feel free to reach out if you need to implement a similar functionality in your environment or if you have any questions, It’d be my pleasure to discuss it with you!

Working with XML & PnP Provisioning

If you work with PnP Provisioning Engine that much, you might reach a point where you want to have your configuration sitting in an XML file, and read its settings and pass it to your provisioning engine. However, you might run into some issues when dealing with special characters if you try for example to name your sites something such as “R&D”.

We’ll have a look at a case. Let’s say you have an XML configuration file named config.xml, that contains some settings regarding site urls and site names that you want to use in your provisioning engine. One of the values in XML might look like this:

Config.XML

If you try to read its value like this:

Reading XML file with PowerShell

You’ll end up with such an error:

Cannot convert value “System.Object[]” to type “System.Xml.XmlDocument”. Error: “‘<‘ is an unexpected token. The expected token is ‘;’

This means, that we’ll need to encode our value in XML, so we’ll end up with something like this:

Encoding values in XML

Or we can keep the value is XML as R&D, and replace the & with &amp; in our PowerShell script like the following:

Getting XML as string then replacing invalid characters


Now when we get the right value from $Config in PowerShell, it works just fine, and we’ll have a string as “R&D”. However, when we pass this value to our provisioning template:

It will complain again with such an error:

‘ ‘ is an unexpected token. The expected token is ‘;’.

The reason is, when it’s passed from PowerShell as “R&D” to the provisioning engine, it has to be encoded one more time. To do so, we’ll need to execute this before passing our values to the provisioning engine:

[System.Web.HttpUtility]::HtmlEncode($config.parameters.SiteTitle)

We store this in a variable, and pass it as a parameter to our provisioning engine. It will work like a charm!

Query Microsoft 365 subscriptions with PowerShell

There are 2 main PowerShell modules for managing Azure AD (and access O365 licenses): AzureAD module and MSOnline module. (There are 2 more modules for AzureAD for preview, but I haven’t worked with them yet).

Let’s say that you want to get all users in a specific office, who have O365 licenses? We can do something like this:

We can do something like this:

Get-AzureADUser  -all $true  | Where { $_.PhysicalDeliveryOfficeName  -eq ‘Office Name’ -and $_.AssignedLicenses -ne $null} | select UserPrincipalName , DisplayName | export-csv [AddCsvPath]

That might work, but.. it might also give you more results than what’s expected. What if some users use PowerBI (Free), Flow (Free) or Teams Exploratory license (free Teams license for those who aren’t yet assigned a license).

If you run the previous command, it will get those users too since their AssignedLicenses isn’t actually null.

To do that, let’s first query all O365 available licenses in our tenant and see what we get. We can run something like the following:

Get-AzureADSubscribedSku

You’ll get a result of ObjectId and SkuPartNumber, the SkuPartNumber is the description for the licenses, and the ObjectId will be formatted like this: [TenantID]_[LicenseId]

For example, for the E5 license, the SkuPartNumber is SPE_E5, and the ObjectId is:

[GUID FOR TENANT]_06ebc4ee-1bb5-47dd-8120-11324bc54e06

Now that we know the license ID exactly, we can use it to get users in the required office, with the required license ID:

Get-AzureADUser  -all $true  | Where { $_.PhysicalDeliveryOfficeName  -eq ‘Office Name’ -and ($_.AssignedLicenses).SkuId -eq "06ebc4ee-1bb5-47dd-8120-11324bc54e06"}  | export-csv [AddCsvPath]

For reference, here are some of the most common license information that you might need:

NameID
E118181a46-0d4e-45cd-891e-60aabd171b4e
E305e9a617-0261-4cee-bb44-138d3ef5d965
E506ebc4ee-1bb5-47dd-8120-11324bc54e06
E5 Developer Licensec42b9cae-ea4f-4ab7-9717-81576235ccac
TEAMS Exploratory710779e8-3d4a-4c88-adb9-386c958d1fdf
Teams Commercial29a2f828-8f39-4837-b8ff-c957e86abe3c
Flow Freef30db892-07e9-47e9-837c-80727f46fd3d

This list will only save you time executing Get-AzureADSubscribedSku !


Restrict Yammer Groups Creation

Yammer is an awesome tool to have company-wide communications. With open communities that people can join and interact with each other without having to work with each other on a daily basis. It’s a perfect network where people can reach out together, interact with the upper management, and share thoughts across the whole organization.

The bad thing about Yammer however, is by default it would allow everyone in the company to create Groups (newly named as Communities), and there’s no way by default for a Yammer admin to restrict the creation of these communities to be just for a subset of people. So how can we do it?

Knowing that the creation of Microsoft 365 groups creation can be restricted, so why not making Yammer follow the same policies as Microsoft 365?

Let’s have one step back and see the restriction on creating new Microsoft 365 groups. To restrict the creation of new groups in M365, you’d have follow steps mentioned in this article:

https://docs.microsoft.com/en-us/microsoft-365/admin/create-groups/manage-creation-of-groups?view=o365-worldwide

Basically, you’ll need to creation a group in Azure AD, and use Azure AD PowerShell module to restrict the creation of new groups to only this security group as the article describes:

$GroupName = "[AddSecurityGroupNameHere]"
$AllowGroupCreation = "False"

Connect-AzureAD

$settingsObjectID = (Get-AzureADDirectorySetting | Where-object -Property Displayname -Value "Group.Unified" -EQ).id

if(!$settingsObjectID)
{
$template = Get-AzureADDirectorySettingTemplate | Where-object {$_.displayname -eq "group.unified"}
$settingsCopy = $template.CreateDirectorySetting()
New-AzureADDirectorySetting -DirectorySetting $settingsCopy
$settingsObjectID = (Get-AzureADDirectorySetting | Where-object -Property Displayname -Value "Group.Unified" -EQ).id
}

$settingsCopy = Get-AzureADDirectorySetting -Id $settingsObjectID
$settingsCopy["EnableGroupCreation"] = $AllowGroupCreation


if($GroupName){
$settingsCopy["GroupCreationAllowedGroupId"] = (Get-AzureADGroup -Filter "DisplayName eq '$GroupName'").objectId
}

else {
$settingsCopy["GroupCreationAllowedGroupId"] = $GroupName}
Set-AzureADDirectorySetting -Id $settingsObjectID -DirectorySetting $settingsCopy
}

This is exactly the same code mentioned in the article, just make sure to have the correct group name you created in M365 that contains people who are allowed to create groups (Make sure to add the required people as members, adding them as owners only won’t grant them permissions to create M365 groups)

Now that we managed to have a security group whose members are allowed to create M365 groups. We’ll need to let Yammer follow same rules as M365. For that, we’ll have to put Yammer in Native Mode for M365. So what’s the native mode for M365 in Yammer?

The native mode is a way for Yammer to follow same rules of M365, so the documents will be stored in SharePoint online. The creation of a new Yammer community (group) will result in the creation of a new M365 group, you can search for Yammer content in the M365 security and compliance center.

So how do we put Yammer in native mode for M365? First we’ll need to enforce O365 identity in Yammer, which basically tells Yammer to let users login with their O365 accounts (which makes sense since you’d want them to login to Yammer that way). You do this step by going to Yammer admin page, and on the left menu under “Content and Security” click on “Security Settings”. Make sure to have “Enforce Office 365 identity”:

Now after enforcing O365 identity, we’ll finally tell Yammer to follow same rules as M365, but putting it in native mode for M365, to do so go to “M365 Native Mode” from the left menu in the Yammer admin page. You’d have to generate the Alignment Report. This report will show any warnings in case some users won’t be able to exist in the network if it’s migrated to the new mode, or if some old groups are available that aren’t already connected to O365 groups.

Old groups will have O365 groups provisioned and connected to them. If you have any naming convention policies for M365 groups, they won’t be applied in this case for these existing Yammer groups.

After running the alignment report, you can download it, I opened it in vscode like this:

and the page will be updated to show you the results of the analysis:

At the very end of the page, you confirm that you want to proceed with the conversion, the reason for that is that this change won’t be rolled back, once you go Native Mode, that’s it.

Now users who aren’t allowed to add a community (group), won’t see this option in Yammer:

The new Yammer experience, without being able to add a new community

Managing Teams Private Channels With PowerShell

In the previous post we talked about the need to upgrade to TLS 1.2 to install PowerShell modules related to Office 365.

When using this method, specifically for Microsoft Teams, it will install a module where you won’t be able to execute commands related to private channels, such as the -MembershipType parameter when creating a new channel using the New-TeamChannel command.

To do this, we’ll need to install the Teams module from the PowerShell Test Gallery instead: https://www.poshtestgallery.com/

When going to that website, you’ll notice that the most known module there is the Microsoft Teams module, which as of now, had around 9,200 downloads in the last 6 weeks. To install this module, you would to remove the existing Teams Module if you already had it installed:

Uninstall-Module -Name MicrosoftTeams 

Then we’ll need to register the test gallery with PowerShell so we can use it later when we do installations, note the name of the gallery you choose to be used in PowerShell can be anything you like, for me I chose PSTG, short for PowerShell Test Gallery:

Register-PSRepository -Name PSTG-SourceLocation https://poshtestgallery.com -InstallationPolicy Trusted


Then we can install Teams module again:

Install-module -Name MicrosoftTeams -Repository PSTG -Force

Now you’re ready to go.

Common issues you might face:

1- If you get an error that the MicrosoftTeams module is already in use and you can’t remove it, just restart the PowerShell session.

2- If you get an error with something like:


The specified uri ‘https://www.poshtestgallery.com’ for parameter ‘SourceLocation’ is an invalid web uri. Please ensure that it meets the Web Uri Requirements.


Then it’s the same case as the previous post, where you need to set the PowerShell session to run on TLS 1.2, so commands will be like:

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 

Register-PSRepository -Name PSTG -SourceLocation https://poshtestgallery.com -InstallationPolicy Trusted

April-2020 TLS Upgrade Needed for PowerShellGet

When trying to install any PowerShell module to work with Microsoft 365 (such as AzureAD v1, AzureAD v2, Teams, Exchange Online, SharePoint), I got the following issue:

WARNING: Source Location ‘https://www.powershellgallery.com/api/v2/package/PackageManagement/1.4.7’ is not valid. PackageManagement\Install-Package : Package ‘PackageManagement’ failed to download. At C:\Program Files\WindowsPowerShell\Modules\PowerShellGet\1.0.0.1\PSModule.psm1:1809 char:21 + … $null = PackageManagement\Install-Package @PSBoundParameters + ~~~~ + CategoryInfo : ResourceUnavailable: (C:\Users\mderha…anagement.nupkg:String) [Install-Package], Exception + FullyQualifiedErrorId : PackageFailedInstallOrDownload,Microsoft.PowerShell.PackageManagement.Cmdlets.InstallPackage

The reason is that the PowerShell gallery has deprecated the support for TLS 1.0 and 1.1 in April 2020. So an upgrade needs to take place for the PowerShellGet module to support TLS 1.2 (or later version).

RESOLUTION

The following commands needs to be executed to update PowershellGet.

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12  
Install-Module PowerShellGet -RequiredVersion 2.2.4 -SkipPublisherCheck 

Now you can install other modules for Teams, Azure, etc 

  • AzureAD v2:  Install-Module -Name AzureAD 
  • AzureAD v1:  Install-Module –Name MSOnline 
  • Teams:  Install-Module -Name MicrosoftTeams 
  • SharePoint:  Install-Module  Microsoft.Online.SharePoint.PowerShell 
  • Exchange Online: Install-Module -Name ExchangeOnlineManagement
  • Skype for business: https://www.microsoft.com/en-us/download/details.aspx?id=39366 

Here’s the list of modules related to Microsoft for reference:

To update a module:

Install-Module Microsoft.Online.SharePoint.PowerShell -force


AzureAD v1 still has richer commands, but AzureAD v2 will be the recommended one later. You can have both installed at the same time.

Exception calling “SaveAsTemplate” with SharePoint 2019

I was trying to use PowerShell with a modern site (Communication site in my case) to save a list/library as a template and getting this error:

Having the UnauthorizedAccessException can be little confusing, so what’s the catch?

To solve this, we’ll use PowerShell, code looks like this:

Microsoft introduced prevented custom scripts from running by default in SharePoint online, and now with the introduction of modern sites in SharePoint 2019, they’re prevented by default in modern sites in SharePoint 2019. With custom scripts feature enabled, saving the site as a template and saving lists/libs as a template won’t be possible, hence you get the UnauthorizedAccess error. When custom scripts is active, the DenyPermissionsMask propert of the site collection will be: “AddAndCustomizePages” .. in order to allow custom scripts, it has to be “EmptyMask”.

You’ll need to run this command:

$site.DenyPermissionsMask = [Microsoft.SharePoint.SPBasePermissions]::EmptyMask

Now you can save the list/lib as a template.

External Sharing Report In SharePoint Online

Hello readers! This is a short blog post to share with you a PowerShell script that would be helpful for Office 365 admins in order to know who shared content in SharePoint Online. This script will create a .csv file with a list for each site collection that has content shared outside of the organization, as well as the name of the person with access to your environment, his email, the date when he accessed your environment the first time as well as who invited him to your environment.

Here’s a link to the script on codeplex. Hope this helps someone out there when he’s asked to do a such a report!

PowerShell Error When Restoring Site Collection

SharePoint doesn’t allow the backup of a site collection and restore it in the same content database. If you backup a site collection and restore it, it will give you the following error.

So you need to make a new content database in the web application (in case the replication of the site collection at the same content database) so that the backed up site collection and the restored one are in different content database, but be careful to put the first database offline before you restore the new site collection.

 

Error occurred in deployment step ‘Recycle IIS Application Pool’: Cannot connect to the SharePoint site

One of the frustrating problems that you might face while developing SharePoint applications is some of the errors that you really don’t know how to troubleshoot, or where to start your investigation, one of the errors I had is the following:

Error occurred in deployment step ‘Recycle IIS Application Pool’: Cannot connect to the SharePoint site: << Site Collection URL >>. Make sure that this is a valid URL and the SharePoint site is running on the local computer. If you moved this project to a new computer or if the URL of the SharePoint site has changed since you created the project, update the Site URL property of the project.

For this error, my environment was 1 SharePoint development server connected to separate SQL server, I was assigned as the farm administrator, and the local administrator on the SharePoint server, but the problem is getting this error once you try to deploy using visual studio 2010, the solution here is that the user deploying using visual studio to have db_owner permission on the content database for the web application.