Getting started automating Microsoft 365 administration with the Graph

https://www.youtube.com/watch?v=e1ypZGfifSQ

The Microsoft Graph is a unique and powerful way to administrate Microsoft 365. This session will provide you with a introduction to what the Microsoft Graph is, how to access it and how to use to improve the way you maybe currently administrating your customers environments. The session will also be jammed packed with live demonstrations and best practices for automating any Microsoft 365 environment. Save time, save money and save effort by viewing this session.

Using PowerShell to create Teams from a CSV file

There was another Teams import from CSV PowerShell script that someone was working on and having issues with. That script was a bit old and used commands that had been changed in the Microsoft Teams PowerShell module since the script was created. So I have taken that script (unfortunately original source specifically unknown but credit noted) and modified it and uploaded to my Github repo here for all to use:

https://github.com/directorcia/Office365/blob/master/o365-tms-import.ps1

and the CSV file in the format required is here:

https://github.com/directorcia/Office365/blob/master/o365-tms-import.csv

image

Now that it is in my Github it is easy for me to update when and if required. I encourage you to also go in and have a look at the comments to understand what is going on.

In essence the script will import the data from CSV file and loop through all the entries creating a new Microsoft Team and then the channels specified in this Team as well as assign member and admin roles for you.

If you use the –debug command line parameter it will record a log file for you.

I have also added some error checking and improved output, as shown above, to give you a better idea of what is going on in each step.

I will note that when you assign member and admin permissions to the Team created via this script they seem to take  while to show up in the portal. So be patient, as they will appear. This isn’t a limitation of this script but just the refresh cycle of the portal.

There are some additional items I want to add but take a look and let me know what you’d like to see added or if I have made any errors that need fixing. Don’t forget to check back regularly for updates.

We all need to automate more

A common challenge today many business, including IT resellers, face is the lack of suitably qualified staff. There seems little doubt this situation will continue for an extended period due to various reasons. However, I believe the situation has been greatly exacerbated by many businesses failing to truly embrace the power of automation, especially IT automation, in their business.

A common process I see in many businesses undertake is simply adding more technology without the drive for improved effectiveness. Many business seem to add more technology because they ‘believe’ it will make them more effective. However, unless there is a plan and desire to become more effective it rarely happens. In reality what happens is, they become less effective, as technology systems add increased burdens, costs and demand more resources. In many cases, they make things far worse and introduce more inefficiencies due to complexity and a lack of integration.

If you take a step back and look at the wider picture here, it was a common belief that computers and technology systems in general would remove the mundane burden of repetitive and undesired work in our society. However, if you look around today most people and businesses are far more stressed, are far more ‘time poor’ than they have even been and yet we have all this wonderful and powerful technology at our finger tips. We carry super computers in our pocket, but when was the last time everyone turned up to a meeting on time?

One of the reasons I suggest is that fact that most are merely using the ‘system default’ configuration that comes with any technology. They don’t invest in learning and exploring how to make the technology work best for them. Technology developers can only supply their products with a single configuration that ‘hopefully’ suits most users. That is rarely the case. Unfortunately, merely ‘accepting the defaults’ leads to inefficiencies which leads to being more time poor, which leads to less time being available to learn and optimise technology, which leads to merely ‘accepting the defaults’…. and so on through an ever more inefficient spiral.

Another reason is the fact that today we live a very much ‘now’ society. People don’t want to wait. They want instant gratifications. The days of investing for the long term no longer take primacy, which is strange, given that things like compound interest have been referred to by some pretty smart people as the eighth wonder of the world.

“Compound interest is the eighth wonder of the world. He who understands it, earns it … he who doesn’t … pays it.”

― Albert Einstein

Unfortunately, this fallacy of constant technology efficiency is unlikely to change. I would actually contend that it is, in fact, only going to accelerate. That, however, does present an opportunity for those who are prepared to invest some time and truly take advantage of what things like automation can achieve.

I like to think of technology automation along a spectrum. In the past, we have had things like scripting such as PowerShell that provides a huge mount of leverage. You can write a PowerShell script to do just about anything in the Microsoft environment these days. It allows a single person to obtain a consistent result once it has been developed. The problem is however, someone still needs to run that script. If an IT reseller is managing an environment for a customer, that customer can’t run the script themselves, they need to get the IT reseller to run that script. That is still not as efficient as it could be. It also centralises the power of automation, placing it in the hands of a small, technically savvy audience.

Next on this spectrum is the automation we see today in technologies like the Power Platform that allow an army of citizen developers to easily create ‘low-code’ solutions for business challenges. This means that most customers using Microsoft 365 have this capability. You can also see Microsoft raising the awareness of this audience to the possibilities without the need for dependencies on IT resellers to run these automation processes. Sure, they solutions developed by ‘citizen developers’ may not be as elegant as those created by a skilled developer, but does that really matter any more if it gets the job done? A growing army of people entering the workplace are ‘digitally native’ and more than willing to ‘try’ technology. By doing so, using the Power Platform, they are not only solving business challenges but they are increasing their own skill and career value.

Where I think we are going is to a world where automation are wrapped up inside a bot or something like Power Virtual Agents. Recently, I wrote this article:

Automated user tenant access control

In essence, it allows you to control when users can login to a Microsoft 365 tenant. Even thought this solution was created using Power Automate, it still needs someone to execute it if perhaps you wanted to use it when an employee goes on leave. The friction is much lower than using a PowerShell script, but it still could be better.

Think about wrapping this tenant access control solution inside a Power Virtual Agent bot and having that published to a channel in Teams. Then, when a user wants to take leave, they converse with the bot for approvals as well as automating the process of denying access while they are away. The bot can ensure that all the correct information is collected and correct processes are followed and documented, every time! Most importantly, this puts the power of automation in the hands of the people that use it. Using a bot, it does this in a friendly a conversational manner. It doesn’t introduce IT complexity to scare the end user away from actually using it. There are lots and lots of benefits.

The good news is that there are already lots of automation options you can take advantage of. The bad news is that you are unlikely to using them enough in your business. A modern approach to staff shortages shouldn’t be ‘where do I get good staff’. Instead, it should be ‘how can I automate more to reduce that dependency’.

Without doubt, the best place to start for most ‘non-IT’ types is the Power Platform in Microsoft 365. Yes, there is an upfront investment to be made, but like compound interest it will produce significant returns over time.

There are real opportunities for smart businesses to leverage the automation capabilities already inside most systems today. These are available to all but the smart ones will take them, implement and transform their impact on the market and their own businesses. Let’s put technology to work to do thing it supposed to do. That is, to make our lives easier and better, not simply as adding more that we don’t need. Simplicity is truly the effectiveness superpower.

Incentivising Microsoft Teams contributions with Power Automate

One of the most important parts of technology adoption is incentivising people. One simple way to do this is to acknowledge their contributions in Microsoft Teams. I’ve spoken about this before using something like:

Custom Praise badges in Microsoft Teams

The challenge there is, that this is a manual process. How can the same be achieved the same kind of thing using automation I recently wondered?

What if you could automatically determine the number of chat messages that someone posted into Microsoft Teams, and then acknowledge the highest contributors with a custom ‘thank you’ message? Here’s how I managed to do just that using the Microsoft Graph and Power Automate.

The core information needed about messages posted in Microsoft Teams will come the Microsoft Graph, specifically:

getTeamsUserActivityUserDetail

The first step in this process will be to create an Azure AD application in the tenant, which I have detailed previously:

https://blog.ciaops.com/2019/04/17/using-interactive-powershell-to-access-the-microsoft-graph/

the credentials for this Azure AD application will then need to be uploaded into Azure Key Vault as I have covered here:

Uploading Graph credentials to Azure Key Vault

You’ll also need to set the API permissions for this application to :

image

Reports.Read.All

 You’ll need to trigger your Flow the way that suits you. Typically you’d schedule it to run on the first of each month. You’ll then need to use the Get secret action from the Azure Key Vault as shown above. You should also note that this is a premium connectors, so you’ll need an appropriate Power Platform license to use this.

image

Next, will be the HTTP action that also a premium connector. The URI to use here will be:

https://graph.microsoft.com/beta/reports/getTeamsUserActivityUserDetail(period=’D30’)?$format=application/json

The D30 parameter determines how many previous days to consider. Valid options for the number of days is 7,30, 90, 180 according to the documentation.

The security parameters come from the Azure Key Vault Get secret actions.

image

The data returned from this HTTP action will be in JSON format, so that is fed into the Parse JSON action as shown above.

One tricky things about parsing json is to get the right schema. When I first attempted this I received this error:

“message”: “Invalid type. Expected String but got Null.”,

After some digging I found that sometimes the lastactivitydate parameter returned a string or NULL value. Thus, I need to modify the original JSON for that parameter to accommodate both potential results:

“lastActivityDate”: {
     “type”: [
         “string”,
         “null”
     ]
},

Here the whole working JSON schema I used:

{
     “type”: “object”,
     “properties”: {
         “value”: {
             “type”: “array”,
             “items”: {
                 “type”: “object”,
                 “properties”: {
                     “reportRefreshDate”: {
                         “type”: “string”
                     },
                     “userId”: {
                         “type”: “string”
                     },
                     “userPrincipalName”: {
                         “type”: “string”
                     },
                     “lastActivityDate”: {
                         “type”: [
                             “string”,
                             “null”
                         ]
                     },
                     “isDeleted”: {
                         “type”: “boolean”
                     },
                     “deletedDate”: {},
                     “assignedProducts”: {
                         “type”: “array”
                     },
                     “teamChatMessageCount”: {
                         “type”: “integer”
                     },
                     “privateChatMessageCount”: {
                         “type”: “integer”
                     },
                     “callCount”: {
                         “type”: “integer”
                     },
                     “meetingCount”: {
                         “type”: “integer”
                     },
                     “meetingsOrganizedCount”: {
                         “type”: “integer”
                     },
                     “meetingsAttendedCount”: {
                         “type”: “integer”
                     },
                     “adHocMeetingsOrganizedCount”: {
                         “type”: “integer”
                     },
                     “adHocMeetingsAttendedCount”: {
                         “type”: “integer”
                     },
                     “scheduledOneTimeMeetingsOrganizedCount”: {
                         “type”: “integer”
                     },
                     “scheduledOneTimeMeetingsAttendedCount”: {
                         “type”: “integer”
                     },
                     “scheduledRecurringMeetingsOrganizedCount”: {
                         “type”: “integer”
                     },
                     “scheduledRecurringMeetingsAttendedCount”: {
                         “type”: “integer”
                     },
                     “audioDuration”: {
                         “type”: “string”
                     },
                     “videoDuration”: {
                         “type”: “string”
                     },
                     “screenShareDuration”: {
                         “type”: “string”
                     },
                     “hasOtherAction”: {
                         “type”: “boolean”
                     },
                     “urgentMessages”: {
                         “type”: “integer”
                     },
                     “postMessages”: {
                         “type”: “integer”
                     },
                     “replyMessages”: {
                         “type”: “integer”
                     },
                     “isLicensed”: {
                         “type”: “boolean”
                     },
                     “reportPeriod”: {
                         “type”: “string”
                     }
                 },
                 “required”: [
                     “reportRefreshDate”,
                     “userId”,
                     “userPrincipalName”,
                     “lastActivityDate”,
                     “isDeleted”,
                     “deletedDate”,
                     “assignedProducts”,
                     “teamChatMessageCount”,
                     “privateChatMessageCount”,
                     “callCount”,
                     “meetingCount”,
                     “meetingsOrganizedCount”,
                     “meetingsAttendedCount”,
                     “adHocMeetingsOrganizedCount”,
                     “adHocMeetingsAttendedCount”,
                     “scheduledOneTimeMeetingsOrganizedCount”,
                     “scheduledOneTimeMeetingsAttendedCount”,
                     “scheduledRecurringMeetingsOrganizedCount”,
                     “scheduledRecurringMeetingsAttendedCount”,
                     “audioDuration”,
                     “videoDuration”,
                     “screenShareDuration”,
                     “hasOtherAction”,
                     “urgentMessages”,
                     “postMessages”,
                     “replyMessages”,
                     “isLicensed”,
                     “reportPeriod”
                 ]
             }
         }
     }
}

image

I filtered the results produced to firstly remove users with zero messages and then I removed specific users (in this case me) from the results using the two Filter action shown above.

image

One of the limitations I found during this development process was that Flows are not very good at sorting information. It would have been nice if I could have just sorted my results from largest to smallest. I solved this but firstly creating a temporary variable into which I would store the highest number of chat messages. I then looped through all the remaining results using the Apply to Each action shown above, and set this variable to the greatest value found throughout the results.

image

I then employed another Filter action again to remove any results that didn’t match this maximum value.

The next challenge came when considering external users, who have a UPN in the format of:

user_domain.com#EXT#tenant.onmicrosoft.com

Turns out the the Power Automate lookup actions don’t seem to work using these type of UPNs, they only work with an email address apparently!

image

To solve this, I now created two more variables to use when extracting the external users email address.

image

I need to extract and create a user email address before using it in the Search for users (V2) action. To do this I used two functions inside two Set variable actions. The first is:

slice(items(‘Apply_to_each_2’)?[‘userPrincipalName’],0,lastIndexOf(items(‘Apply_to_each_2’)?[‘userPrincipalName’],’#EXT’))

which removes anything from the #EXT and beyond. This typically leaves the format of:

user_domain.com

The second function is:

replace(variables(‘EmailAddress’),’_’,’@’)

that replaces the underscore character with an “at” symbol. The output after both of these functions complete is the users full email address which I can then feed into the Search for users (V2) action to locate my user(s).

image

Now that I have the user information I can use it with the Get an @mention token for a user action and then post a message with that @mention and anything else into the Team to publicly recognise the user(s) using the Post message in a chat or channel action.

image

The end result is a nice message, like shown above, that acknowledges the user(s) for their contribution in the Microsoft Team. Best part is that now it is automated it’ll do all the hard work for you going forward!

The moral of the story here is that Power Automate combined with the Microsoft Graph can achieve just about anything you want. Yes, it does a little bit of setting up with an Azure AD application, Keyvault and so on but they are typically only once off. There is also some advanced techniques when it comes to the JSON parameters but once you start working with, it becomes easier. In short, none of that should put you off from what is possible with the Microsoft Cloud environment and putting the technology to work for you to give you more freedom and improve your business processes.

Defender for Endpoint remediation levels

If you read the Microsoft documentation:

Automation levels in automated investigation and remediation capabilities

you find that there are 5 different levels of remediation automation you can set:

– No automated response

– Semi – require approval for all folders

– Semi – require approval for non-temp folders

– Semi – require approval for core folders

– Full – remediate threats automatically

which are all detailed here:

Levels of Automation

Note:

Full automation is recommended and is selected by default for tenants that were created on or after August 16, 2020 with Microsoft Defender for Endpoint, with no device groups defined yet.

Thus, Automation levels rely on Device Groups in Defender for Endpoint.

image

You see this when you create a Device Group as shown above.

image

With Defender for Endpoint P2 you find Device Groups via https://security.microsoft.com | Settings | Endpoints | Device groups as shown above.

image

However, with Defender for Business (above), you’ll see that there are no options currently for Device Groups. This basically means that the all remediation will be performed automatically.

I don’t that it is really a problem, but is another difference between Defender for Endpoint P2 and Defender for Business. I have not tested Defender for Endpoint P1 but I assume that it have the same lack of Device Groups as Defender for Business has, but I would to check to be 100% sure.

Adoption with fun

The majority of IT products and services are not actually used by IT people (amazing eh?). They are in fact, used by ordinary people (aka Muggels) in businesses, trying to do their job. For these people, changing the way that they work is frustrating because they need to adopt new approaches and tools. Helping with this adoption is a key to the success of modern approaches to IT I believe.

A handy technique that I have found to work well is make using new systems fun. In the distant past, when I was implementing SharePoint on premises, I used to implement the Daily Dilbert web part to post a Dilbert cartoon onto the front page of the SharePoint Intranet each day. The idea was to help drive adoption by getting people to visit the company Intranet to read the Dilbert comic and then, hopefully, dive into the other content that was there.

Today, the technology has changed but the adoption challenge hasn’t. I thought that I’d therefore share with you a way to get a Dilbert comic into your Teams channel daily using Power Automate.

This is all made possible via APIs and a suitable one I found is:

https://dilbert-api.glitch.me/json

which will produce an output that looks like this:

{"title":"Simulation TestingElbonia University Partial Win","image":"https://assets.amuniversal.com/4f2025a02e0d013a8769005056a9545d.png"}

In here you’ll see an image link to the Dilbert Cartoon.

Step one is to create a new Flow that is triggered at a recurring time.

image

Next, you want to add the HTTP action. In here, use the GET method and the URI set to the above API link as shown above.

The HTTP action is actually a ‘premium’ connector and may not be available to you by default. Thus, you may need an upgraded Power Platform license to have this available. Remember however, you’ll only need that license for the user creating and running that Flow.

image

You’ll then need to the Parse JSON action as shown above. The content here will be the Body from the HTTP action above and simply copy and paste the output of the API above into the option Generate from sample.

image

Now add Post message in a chat or channel action.

image

Enter option to post into the Team and Channel of your choice as shown above.

image

For the Message field select the </> option from the menu bar across the top, as shown. This will allow you to use raw HTML code here.

Type the following:

<img src = ”

then select the option to insert dynamic content like so:

image

(the lightning bolt icon)

image

In this list that appears you should be able to select image as shown above.

image

add the following text after the dynamic field

” width=”738″ height=”229″>

so the completed Message field looks like:

image

It is important that the HTML formatting is correct, otherwise the image will not display.

image

If you now test your Flow you should see the cartoon appear in your Teams channel as shown above. If you have scheduled your Flow daily then you should see a new comic every day. Remember, there is only one cartoon every 24 hours! Rerunning the Flow before then will simply display the same strip.

When the daily comic is more than three frames then it is cut off by default like so:

image

However, clicking on the comic will enlarge it for full viewing. This limitation is due to the height and wide parameters the HTML code used inside the Flow. Most strips are only three frames, that is why I used those height and width defaults for most readability most of the time, but you can vary those parameter if you wish.

So, the idea is to make visiting a Team a more fun place to visit regularly, hopefully with people engaging about the content to help drive adoption.

This Flow/API method can be utilised with just about anything that supports an API. Another I have found (although somewhat more risqué) is a Chuck Norris API here:

https://api.chucknorris.io/

which can be moulded to give a similar result (be it text only).

The only limitation of all of this is the need for the premium Flow HTTP action, but as I said, it is well worth the investment and is only really necessary for the user creating the Flow. Having a premium license for Flow opens up so many more capabilities, so it is highly recommended if you want to get serious about automation inside your environment.

Happily, Daily Dilbert is back baby! And now in Microsoft Teams.

Power Platform Community November webinar–Sessions

As mentioned here:

Power Platform Community November webinar

We had some issues with the screen recordings. The presenters graciously agree to re-record each of their sessions and they are now available for viewing here:

Patron Power Platform Community November 2021 Webinar – Session 1 – YouTube

Patron Power Platform Community November 2021 Webinar – Session 2 – YouTube

Patron Power Platform Community November 2021 Webinar – Session 3 – YouTube