PowerShell scripts for managing Microsoft 365 components

Master Key for the Cloud

Long-Term Group Management

To manage AAD groups in a valid way in the long term, it makes sense to have a lifecycle policy that supports all or selected groups. In the following example, the selected groups need to be renewed after 180 days. Groups that have no owner are reported to the notification email:

New-AzureADMSGroupLifecyclePolicy -GroupLifetimeInDays 180 -ManagedGroupTypes "Selected" -AlternateNotificationEmails "<user@example.com>"

Alternatively, you can specify None or All for ManagedGroupTypes; these Microsoft 365 groups are subject to the lifecycle policy. The policy ID is returned, which you can then pin on group objects:

Get-AzureADMSGroup -SearchString "Project" | % { Add-AzureADMSLifecyclePolicyGroup -Id 5d69168d-b3e4-410a-b0a5-c729703ebc86 -GroupID $_.Id }

SharePoint Inspection

Microsoft Teams uses SharePoint to store the data in the background, securing access to all documents and resources for collaboration. Of course, SharePoint Online is also used without Teams in many places, either as a team and collaboration platform or for personal data storage on OneDrive. With the SharePoint PowerShell modules [1], you start an inventory and connect with:

Connect-SPOService -Url <https://frickelsoftnet-admin.SharePoint.com>

You can display an overview of the sites with

Get-SPOSite -Limit all

Then, you will see not only the URLs but also the storage used and – where available – the owners of the sites. You will notice that owners cannot be found for all sites. However, if you look more closely, the Owner field is listed in the details:

Get-SPOSite -Identity <https://frickelsoftnet.SharePoint.com/sites/Project> | fl

The Owner field contains a GUID that indicates a user or group but has a special suffix: _o. On closer inspection, this phenomenon occurs for all sites that originated from Microsoft 365 Groups or Teams. If you cut off the suffix and ask the AAD for a group with the resulting correct GUID, the group name is found:

Get-SPOSite -Identity <https://frickelsoftnet.SharePoint.com/sites/Project> | SELECT Owner | % { Get-AzureADGroup-ObjectID ($_.Owner).TrimEnd("_o") }

In this way, you can quickly build a command for all sites. Microsoft 365 and Teams sites are recognized because they result from a SharePoint template that has Group* in its name:

Get-SPOSite -Limit all | % { if($_.Template -like '<GROUP*>'){ $owner = Get-SPOSite -Identity $_.URL | SELECT -ExpandProperty Owner;$owner = (Get-AzureADGroup -objectID $owner.TrimEnd("_o")).displayName } else { $owner = $_.Owner} Write-Host $_.URL $owner }

Alternatively, the group on which the team, and thus Microsoft SharePoint, is based can also be read out in the GroupID attribute in the SPO site details – without the suffix.

Whom collaboration is allowed with is determined either at the tenant or site level. The SharingCapability attribute describes the tenant setting:

Get-SPOTenant | SELECT SharingCapability

The supported values are Disabled for disabled external sharing and internal-only use, ExistingExternal-UserSharingOnly for collaboration with existing guests, and External-UserAndGuestSharing for existing as well as new guests included by SharePoint email one-time password (OTP). The sharing settings for sites can differ from the tenant setting, so it is worth taking a look at the site:

Get-SPOSite -Identity <https://frickelsoftnet.SharePoint.com/sites/Project> -Detailed | SELECT SharingCapability

The ExternalUserAndGuestSharing setting in particular integrates partners and suppliers outside of your organization with an OTP that reaches the user by email. You then create a SharePoint account for the external party. If you use a managed Azure AD and want to prevent SharePoint accounts being created, you can use PowerShell to force the SharePoint tenant to send Azure AD business-to-business (B2B) invitations – even by email OTP if necessary:

Set-SPOTenant -EnableAzureADB2BIntegration $true
Set-SPOTenant -SyncAadB2BManagementPolicy $true
Set-SPOTenant -CustomizedExternalSharingServiceUrl <https://sharing.frickelsoft.net/external-access>

The function is still in the final stages of development at Microsoft but is already available in the Public Preview. Once enabled, the external accounts between Azure AD and SharePoint are no longer any different, and SharePoint's own OTP solution is no longer necessary.

Logging Activities

Provided you have the appropriate licenses, you can use the Compliance Center in Microsoft 365 to generate alerts when something happens on your tenant that you either don't want to happen or at least want to keep a closer eye on. These alerts are structured such that you can define what action triggers the alert and who is notified. Also, you can stipulate that the alert is only triggered if someone specific takes the action. An overview of existing alerts is displayed with:

Get-ActivityAlert | ft Description,Name

A simple example would be when a user shares a SharePoint page or file with an external party. If you want to watch more closely when user Jenny generates a new invitation, create the alert with:

New-ActivityAlert -Name "<External Sharing Alert>" -Operation sharinginvitationcreated -NotifyUser <alarms_m365@frickelsoft.net>, <florian@frickelsoft.net> -UserId <jenny@frickelsoft.net> -Description "<Triggers an alert ifJenny generates a sharing invitation>"

If you are interested in knowing when user Sarah deletes a team, use the new alert:

New-ActivityAlert -Name "Team deletion alert" -Operation teamdeleted -NotifyUser <alarms_m365@frickelsoft.net>, <florian@frickelsoft.net> -UserId sarah@frickelsoft.net -Description "<If Sarah deletes aMicrosoft 365 group or team, an alertis triggered>"

The email then mailed to your Inbox refers you to the admin Compliance Center, where you can investigate the details.

The AAD log also contains the group changes, and the AAD Preview module (set up with Import-Module AzureADPreview) allows you to perform specific searches there:

Get-AzureADAuditDirectoryLogs -Filter "initiatedBy/user/userPrincipalName eq '<user@example.com>' and ActivityDisplayName eq 'Delete group'"

If you are interested in the last changes to the group before it was deleted, you can expand the previous command and extract the object ID of the group. Simply add

| SELECT -ExpandProperty TargetResources | SELECT ID

The object ID (1c9c09d7-4b3c-4f37-b2cf-e3b8ad0a2ecf in this example) can then be used to search for audit entries:

Get-AzureADAuditDirectoryLogs -Filter "targetResources/any (tr:tr/id eq '1c9c09d7-4b3c-4f37-b2cf-e3b8ad0a2ecf')" | ft ActivityDisplay Name,ActivityDateTime

As far back as the audit log goes (30 days in Azure AD Premium tenants), you will then see a history of the changes that the group has undergone – from the creation of new owners, as well as new members and updates to attributes, to deletion.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy ADMIN Magazine

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

comments powered by Disqus