Challenges of PowerShell Scripting with Microsoft 365


If you are looking for a way to automate and simplify your Microsoft 365 administration tasks, PowerShell is a great option. However, PowerShell scripting is not without its challenges. Not proactively maintaining code can quickly become an issue because of the changes made to dependencies such as modules, as well as the cmdlets you use.

In an article I wrote for Practical 365, related to the presentation of the same name held at the The Experts Conference 2023 in Atlanta this year, I discuss some of the challenges administrators might encounter with PowerShell scripts. Also, I provide some guidance and point out a few tools that can assist with rewriting or refactoring code, i.e., updating code while keeping its external functionality.

Click here to read the full article on Practical 365.

Change Teams Guest Profile Picture


In tenants with lots of Guests, the massive display of user initials – the default profile picture – isn’t very pleasing to the eye. Now while regular user can change their profile picture, Guest users cannot. Therefor, a long standing and popular request on UserVoice has been the ability to change profile pictures for Guest users. End of 2020, this request was updated, indicating a change was planned to incorporate the ability to change this profile picture. No signs on the roadmap yet, so what options – if any – does one currently have?

Last week, Teams MVP Yannick Reekmans posted a blog containing instructions on how to change your profile picture for tenants in which you are a Guest. It requires quite some steps and fiddling to accomplish this. One might also wonder, isn’t this possible “the programmatic way”? Well, yes, and here are the steps:

First, open up PowerShell, install the AzureAD PowerShell module it is not yet installed using Install-Module AzureAD, and connect to Azure Active Directory, specifying the tenant where you are a guest:

Connect-AzureAD -TenantDomain contoso.onmicrosoft.com

When the authentication challenge pops up, specify the credentials of the Guest account and approve the Multi-Factor Authentication challenge when required.

Next, use Set-AzureADUserThumbnailPhoto to set the profile picture for your Guest account, specifying your User Principal Name as ObjectId, as well as the picture you want to use, e.g.

Set-AzureADUserThumbnailPhoto -ObjectId 'michel_fabrikam.com#EXT#contoso.onmicrosoft.com' -FilePath 'c:\pic.jpg'

Regarding the User Principal Name, you can use Yannick’s method of determining your Guest’s ID. You can also try to guesstimate it by taking the e-mail address of your original account, replacing ‘@’ with ‘_’, adding a trailing #EXT# followed by ‘@’ and the default domain of the hosting tenant. The picture can be JPEG or PNG format, size 100kb at most, and square dimensions work best.

To verify the image has been set successfully, use Get-AzureADUserThumbnailPhoto -ObjectId <ID>. Then, have some patience for the change to propagate throughout the directories and caching mechanisms. To verify your update was successful and your picture looks properly, you can close the Teams client, clear the locally cached Teams data by removing everything under %AppData%\Microsoft\Teams (Windows), and start Teams again.

To easily spot tenants where you are a guest user and not a regular user, you might want to alter your standard issue profile picture a bit. For example, I have added a text label ‘Guest’ to mine. It doesn’t look as good as the high resolution photos that you can store in Exchange Online, but it certainly looks less boring than a set of intilials.

Note that all of the above is not an officially supported way to manage this picture. So, until there is one, these steps might help you out.

Module Updates: What’s New?


After updating your PowerShell modules which support managing parts of the Microsoft 365, some of us are curious about what changes are introduced with the updated module. In the world of continuous change, it is hard to keep track of these changes. New cmdlets or parameters get added to support new features, and some get removed as they become obsolete. So, how to discover what those changes are after updating to the latest module?

Time to blog on a small script I created for this purpose a long time ago, Compare-Cmdlets.ps1. This script has two operating modes:

  • Export currently available cmdlets and parameters for supported modules.
  • Compare two exports of cmdlets & parameters and report the differences.

Currently, the following command sets are supported:

ModuleTest CmdletExport File
AzureADGet-AzureADUserAzureAD-<version>.xml
ExchangeOnlineGet-MailboxExchangeOnline-<version>.xml
ExchangeOnlineManagementGet-ExoMailboxExchangeOnlineManagent-<version>.xml
MicrosoftOnlineGet-MsolUserMSOnline-<version>.xml
TeamsGet-TeamMicrosoftTeams-<version>.xml

Command sets are exported per module, where a module is assumed to be present by a simple check for cmdlet availability (specified in column Test Cmdlet). That is, if Get-Mailbox is available, the ExchangeOnline module is assumed to be available. It does not distinguish between the Exchange PowerShell module or ‘classic’ Remote PowerShell session, nor will it take into account the repository origin of the module, nor if the Get-AzureADUser is coming from the AzureAD or AzureADPreview module.

That said, here’s how this is works. Load up PowerShell and have your modules installed and ready. Some modules like ExchangeOnlineManagement require connecting to the service first to import the cmdlet functions, so for ExchangeOnlineManagement run Connect-ExchangeOnline first. Same applies to the newer Teams modules, where the Skype Connector functions are only available after running New-CsOnlineSession.

Then run Compare-Cmdlets to export the cmdlets and parameters for those modules. The commands will by default be exported to an XML in a subfolder named ‘data’. The name of the file is mentioned in the table above. If you want to use a different folder to store the XML files, use DataFolder parameter.

Note that with Exchange, the cmdlets available to you depend on which role you have been assigned in Exchange’s Role-Based Access Control model. For example, if you haven’t explicitly assigned Mailbox-ImportRequest to your account, you will not see it in the exports. Therefor, when exporting module changes, it is required using an account with the same roles assigned to have proper exports. But when needed, you can also use it to report on command set differences between two Exchange Online accounts.

After updating some of the modules, or downloading one of the command set reference XMLs I stored with the script on GitHub, you can use Compare-Cmdlets to compare different versions of module exports. For example, to compare the cmdlets of Microsoft Teams module 1.1.4 with those after updating to 1.1.5, use

.\Compare-Cmdlets.ps1 -ReferenceCmds data\MicrosoftTeams-1.1.4.xml -DifferenceCmds data\MicrosoftTeams-1.1.5.xml

From the output, we see for example that:

  • The cmdlet Get-TeamChannel has a new GroupId parameter.
  • The cmdlet New-CsGroupPolicyAssignment parameter PolicyType has been removed.
  • The cmdlet Add-TeamChannelUser is new.

Note that common parameters (e.g. Verbose and ErrorAction) and optional common parameters (e.g. WhatIf) are left out of the equation. Also, parameters are not compared in depth and only presence is checked. If for example a parameter changes type (e.g. string to multivalue), Compare-Cmdlets does not pick that up.

As-is, the script is made to run on demand from an interactive PowerShell session. Ideally, this would run scheduled and serverless from within the service, reporting changes by e-mail.

The script Compare-Cmdlets.ps1 can be downloaded from GitHub here. If you find this useful, would like to comment or have suggestions, use the comments below or leave them on GitHub.

Comparing Sets of Cmdlets


powershellWith the speed of development in Office 365, it is sometimes hard to track which changes have been made to your tenant. Of course, there is the roadmap and message board which you can use to keep up to date, but those are in general high level descriptions. Sometimes you may want to see what are the changes at the cmdlet level in your tenant, between tenants, or Azure Active Directory module. And there is also the occasional gem in the form of a yet undocumented cmdlet or parameter which could hint at upcoming features.

For this purpose I have created a simple script which has two purposes:

  1. Export information on the current cmdlets available through Exchange Online or Azure Active Directory.
  2. Compare two sets of exported information, and display changes in a readable way.

The script is in PowerShell (of course), and is called Compare-Cmdlets.ps1. To export information, you need to be already connected to either Exchange Online or Azure Active Directory (or both).

To export cmdlet information, use:

.\Compare-Cmdlets.ps1 –Export

For Exchange Online and Azure Active Directory, separate export files are created. The files are prefixed with a timestamp and postfixed with the Exchange Online build or Azure Active Directory module version, e.g. 201803121814-ExchangeOnline-15.20.548.21.xml or 201803121815-AzureAD-2.0.0.137.xml.

After a few days/week, or when connected to another tenant or using a new Azure Active Directory PowerShell module, run the export again. You will now have 2 sets of Exchange Online or Azure Active Directory cmdlets, which you can compare using the following sample syntax:

Compare-Cmdlets.ps1 -ReferenceCmds .\201801222108-ExchangeOnline-15.20.428.21.xml -DifferenceCmds .\201803120926-ExchangeOnline-15.20.548.21.xml

image

A progress bar is shown as comparison might take a minute. When the script has finished checking the two sets, you will see output indicating changes in cmdlets, parameters or switches, e.g.

image

Download
You can find the script on the TechNet Gallery or GitHub.

Ignite 2016 Sessions + Downloader


imageNote: Due to Microsoft putting Ignite 2016 contents on YouTube and a new portal, I had to rewrite the download script. Mattias Fors was also working on this, and after integrating his contents pointers, I present you Ignite2016Download.ps1. Check the description on Technet Gallery page for usage options.

Today, the Ignite 2016 event will kick off in Atlanta, US. The agenda contains the whopping number of 1412 sessions, of which 395 touch Office 365 and 133 Exchange in some way or another.

With those numbers it is impossible to attend every session for folks interested in these topics, but luckily Microsoft will also publish Ignite 2016 sessions on Channel 9 this year.

Some of the interesting sessions to watch out for are (links should resolve to on-demand sessions, as they become available):

Session Description Speaker(s)
BRK1021 Unplug with the Microsoft Outlook experts Julia Foran, Gabe Bratton, Allen Filush, JJ Cadiz, Eduardo Melo, Amanda Alvarado, Victor Wang, James Colgan
BRK1044 Dive deeper into what’s new and what’s coming in Outlook on the web Dave Meyers, Eduardo Melo
BRK2033 Discover Office 365 Groups – overview, what’s new and roadmap Amit Gupta, Christophe Fiessinger
BRK2035 Learn about advancements in Office 365 Advanced Threat Protection Jason Rogers, Phil Newman
BRK2053 Connect your business critical applications to Outlook and Groups David Claux
BRK2044 Discover what’s new and what’s coming for Office Delve Cem Aykan, Mark Kashman
BRK2093 Design your Exchange infrastructure right (or consider moving to Office 365) Boris Lokhvitsky, Robert Gillies, Adrian Moore
BRK2139 Protect your business and empower your users with cloud Identity and Access Management Nasos Kladakis
BRK2170 Discover what’s new with Microsoft Exchange Public Folders Sampath Kumar
BRK2215 Debate the top 10 reasons not to move your Exchange on-premises mailboxes to Exchange Online Tony Redmond, Greg Taylor, Steve Conn
BRK2216 Unplug with the experts on Exchange Server and Exchange Online Greg Taylor, Timothy Heeney, Jeff Mealiffe, Ross Smith IV, Wendy Wilkes
BRK2217 Discover modern support in Outlook for Exchange Online Julia Foran, Amir Haque, Gabe Bratton
BRK2218 Move from Exchange 2007 to Modern Exchange Greg Taylor, Steve Conn
BRK2219 Meet twin sons of different mothers – Exchange Engineers and Exchange MVPs Tony Redmond, Jeff Mealiffe, Andrew Higginbotham, Jeff Guillet, Karim Batthish
BRK2220 Peer behind the curtain – how Microsoft runs Exchange Online Paavany Jayanty, Eddie Fong, Karim Batthish, Mike Swafford
BRK3000 Unplug with the experts on Microsoft Exchange Top Issues Nino Bilic, Nasir Ali, Amir Haque, Shawn McGrath, Timothy Heeney, Gabe Bratton, Angela Taylor
BRK3001 Explore the ultimate field guide to Microsoft Office 365 Groups Tony Redmond, Amit Gupta, Benjamin Niaulin
BRK3007 Investigate tools and techniques for Exchange Performance Troubleshooting Nasir Ali, Jeff Mealiffe
BRK3019 Manage Microsoft Office 365 Groups Eric Zenz, Vince Smith
BRK3023 Understand how Microsoft protects you against Spoof, Phish, Malware, and Spam emails Jason Rogers
BRK3045 Use Microsoft Graph to reach users on hybrid Exchange 2016 Venkat Ayyadevara
BRK3046 Build intelligent line-of-business applications leveraging the Outlook REST APIs Venkat Ayyadevara
BRK3074 Discover what’s new in Active Directory Federation and domain services in Windows Server 2016 Sam Devasahayam
BRK3109 Deliver management and security at scale to Office 365 with Azure Active Directory Brjann Brekkan
BRK3139 Throw away your DMZ – Azure Active Directory Application Proxy deep-diveThrow away your DMZ – Azure Active Directory Application Proxy deep-dive John Craddock
BRK3216 Plan performance and bandwidth for Microsoft Office 365 William Looney, Ed Fisher
BRK3217 Run Microsoft Exchange Hybrid for the long haul Timothy Heeney, Nicolas Blank
BRK3219 Migrate to Exchange Online via Exchange Hybrid Michael van Horenbeeck, Timothy Heeney
BRK3220 Deploy Microsoft Exchange Server 2016 Brian Day, Jeff Guillet
BRK3221 Understand the Microsoft Exchange Server 2016 Architecture Ross Smith IV, Mike Cooper
BRK3222 Implement Microsoft Exchange Online Protection Jennifer Gagnon, Wendy Wilkes
BRK3227 Ask us anything about Microsoft Office 365 Groups Eric Zenz, Darrell Webster, Christophe Fiessinger, Martina Grom
BRK3253 Experience Scott Schnoll’s Exchange tips and tricks Scott Schnoll
BRK3254 Cert Exam Prep: Exam 70-345: Designing and Deploying Microsoft Exchange Server 2016 Vladimir Meloski
BRK4031 Overcome network performance blockers for Office 365 Deployments Paul Collinge
BRK4032 Dive deep into Microsoft Exchange Server High Availability Andrew Higginbotham
PRE18 The previous decade called…they want their Exchange Server back Michael van Horenbeeck, Greg Taylor, Sampath Kumar, Andrew Higginbotham, Timothy Heeney, David Espinoza, Nicolas Blank
THR1005R Dive deeper into what’s new and what’s coming in Microsoft Outlook 2016 for Windows Misbah Uraizee
THR1011R Dive deeper into what’s new and what’s coming in Outlook mobile Allen Filush, Victor Wang, James Colgan
THR2007R Fight back with advancements in Office 365 Advanced Threat Protection Phil Newman, Atanu Banerjee
THR2054 Understand the risk and value of your public folder data BEFORE you migrate Dan Langille
THR2190R Secure your sensitive email with Office 365 message encryption Gagan Gulati, Ian Hameroff
THR3001R Migrate DL to Microsoft Office 365 Groups Siva Shanmugam, Loveleen Kolvekar
THR3015 Use RMS in Microsoft Office 365 Nathan O’Bryan
THR3040 Automate Exchange deployment with Powershell Desired State Configuration Ingo Gegenwarth
THR3082 Secure Office 365 in a hybrid directory environment Alvaro Vitta

For those that wish to view sessions offline, there is a script to download the slidedecks and videos. It does so by scraping the Ignite portal, downloading slidedecks from the portal itself, and videos from the related YouTube video link using an utility youtube-dl.exe (which you can also use to download playlists, quite neat). The script can take some parameters:

  • DownloadFolder to adjust the download folder.
  • Format to alter the dimensions and quality of the downloaded videos (see help for supported formats).
  • Title to filter on title keyword
  • Keyword to filter on description keyword.
  • Start to use a different version number to start scraping. Scraping is done sequentially; in the output you will notice a (#nnn) next to the title. That is the current post number.
  • NoVideos to skip downloading videos.

You can download the script from the TechNet Gallery here.