Streamlining our M365 Provisioning: Transitioning from a Third-Party On-Premise Solution to a In-House Cloud-Based, Serverless Approach

The why

As we were moving all our SharePoint Sites from 2019 to the cloud, we decided that we needed a fresh Provisioning Service to handle the changes brought by M365 in the cloud era. We had a few options on the table: we could either again purchase a ready-made solution from a third-party company or go the open-source route.

Our main aim was to find a solution that stays stable without demanding too much upkeep, all while keeping costs in check and being dynamic enough to keep up with the constant changes Microsoft is pushing out at the moment.

Our managers initially leaned towards the easy route of purchasing an external service and calling it a day. However, during an early discussion with one of those companies, I noticed how straightforward Microsoft had made everything. It struck me that I could probably handle it myself. Even if I can’t, it’s at least a challenge, and I’ll pick up some valuable skills along the way.

The weekend

I didn’t have any experience with Azure Functions, but around the same time, we finally got our Azure subscription. This gave me the opportunity to experiment. On a rainy November weekend in 2023, I had some time to myself because my wife took our daughter to visit her grandparents. I decided to dive into the challenge.

I began by setting up the necessary API permissions in the Azure app registration, allowing me to obtain Graph authorization tokens in JavaScript Azure Functions. I also created the managed identities for the PowerShell and PnP functions. The administrative tasks didn’t take long, maybe just an hour or so. After that, I could start writing code.

I must say, Microsoft’s Graph documentation is the best I’ve ever come across on the web for any topic. It’s clear and helps you understand exactly what you need to achieve your desired outcome. I was able to build the core of our new Provisioning Service in under 10 hours. It involved creating about 20 simple Azure Functions. Around half of them were in JavaScript, handling all the Graph-related tasks like creating the M365 group, setting up the MS Team, and configuring its settings. The other 10 functions used PowerShell and PnP cmdlets for SharePoint-related tasks, including permission group renaming, branding, and sharing settings.

The fine tuning

After creating the core solution, I presented it to my managers. I assured them that I could build our M365 Provisioning Service on my own with just a few more days for code cleanup and fine-tuning. Thankfully, my managers trust me enough for projects like this, and they gave me the green light to proceed.

However, it ended up taking more time than I initially anticipated to handle all the smaller details. I also had to redesign the app to accommodate requests for new Teams simultaneously. This part of the project took longer than expected, mainly due to changing requirements and my oversight in using alpha and beta framework components that weren’t yet in production. Lessons learned… 😀

Despite the time pressure caused by this setback, I managed to complete all aspects of the project, and the system became ready for use within a reasonable timeframe.

The final product

The end result comprises 25 Power Automate cloud flows. Each of them essentially triggers an Azure function through HTTP calls and records the status and outcome in a SharePoint site where all the data is stored.

To give you an idea of how it all works, flows 6 to 11 are connected to JavaScript functions, while flows 12 to 23 involve either PowerShell/PnP Azure functions or utilize pure Power Automate cloud flow actions to accomplish various tasks.


I’ve also begun using Obsidian for managing my tasks and projects, and I must say, it’s incredibly versatile. When properly configured, it provides a seamless overview of all your workflows, code, and dependencies, making things much easier to manage!

That’s it.

Caspar 👻

Post Archive