Match an M365 Cloud Account with Local AD

OK, so you need to Match an M365 Cloud Account with Local AD? You just realized you created a cloud only account for a bunch of users in your M365 tenant, but you need them to access local AD resources because you run a Hybrid environment.
You are just not ready yet to pull the trigger and go “Cloud Only”. Don’t worry, you are not alone. I am here to help.
Reasons to Match an M365 Cloud Account with Local AD
There are many reasons to do this. I can think of two. One, you are still running on on-premises Exchange Server and there are some email groups that the account(s) you created in the cloud need access that are only available locally.
The same goes for AD security groups and other attributes that can only be accessed through local AD.
The Easiest Way to Achieve This
The easiest way to Match an M365 Cloud Account with Local AD is to basically create an account in your local AD with the minimum attributes to make what is called a “soft match”. Local AD and the cloud will see them as the same record and synchronize them. Voila, they are both in the cloud and in local AD. Now you don’t have to rack your brain on how to do this anymore!!
One Way to Share Data from VB.Net to PowerShell

Sometimes you need to Share Data from VB.Net to PowerShell. It is not enough to use one to help the other. They need to go back and forth between each other, kind of like a good hockey pass! Sorry I couldn’t resist I was just watching the World Juniors. I won’t be any more though….
The good news is with the right coding and scripting you can easily pass data back and forth between PowerShell and VB.net. I am going to show you an example but by any means, this is not exhausting about what you can do. With a little research and practice I am sure you will find many ways!
High Level View on How Data is Shared
The example I am going to show you builds on an earlier post I had about using PowerShell and MS Teams. Here we will use VB.Net to generate a PowerShell Script based on inputs you put into the program. It then runs the script. The PowerShell script will send its output to a text file. The VB.Net program will read the content of the file and determine if the script ran successfully or not and inform the user through the VB.Net program.
Example on Sharing Data from VB.Net to PowerShell
For the purpose of this post, I am sampling the program and script. Be mindful of error checking and security when Sharing Data from PowerShell to VB.Net.
The VB.Net Code:
Dim FILE_NAME As String = "setforwarder.ps1"
Dim i As Integer
Dim aryText(24) As String
Dim creds As String
Dim dlgr As DialogResult
Dim objShell = CreateObject("WScript.Shell")
Dim sps1 As String
Dim myPrimary As String
Dim mySecondary As String
Dim firstChar As String
Dim firstCharSec As String
myPrimary = txtPrimary.Text
mySecondary = txtSecondary.Text
If myPrimary = "" Or mySecondary = "" Then
dlgr = MsgBox("You did not specify a number as Primary and Secondary.", vbOKOnly)
Else
firstChar = myPrimary.First()
firstCharSec = mySecondary.First()
If Not IsNumeric(myPrimary) Then
dlgr = MsgBox("Primary - Only Numbers are allowed.", vbOKOnly)
ElseIf firstChar <> 1 Then
dlgr = MsgBox("Primary - The first digit must be the number 1. Please follow the format 1XXXXXXXXXX.", vbOKOnly)
ElseIf Len(myPrimary) <> 11 Then
dlgr = MsgBox("Primary - The phone number must be exactly 11 digits. Please follow the format 1XXXXXXXXXX.", vbOKOnly)
ElseIf Not IsNumeric(mySecondary) Then
dlgr = MsgBox("Secondary - Only Numbers are allowed.", vbOKOnly)
ElseIf firstCharSec <> 1 Then
dlgr = MsgBox("Secondary - The first digit must be the number 1. Please follow the format 1XXXXXXXXXX.", vbOKOnly)
ElseIf Len(mySecondary) <> 11 Then
dlgr = MsgBox("Secondary - The phone number must be exactly 11 digits. Please follow the format 1XXXXXXXXXX.", vbOKOnly)
Else
creds = ".\creds.txt"
aryText(0) = "try"
aryText(1) = "{"
aryText(2) = "# Define Credentials"
aryText(3) = "[string]$Username = 'username@company.com'"
aryText(4) = "[String]$Password = Get-Content " & """" & creds & """"
aryText(5) = ""
aryText(6) = "# Convert To secure String"
aryText(7) = "[SecureString]$pass = ConvertTo-SecureString -AsPlainText $Password -Force"
aryText(8) = "$SecureString = $pass"
aryText(9) = "# Create credential Object"
aryText(10) = "$MySecureCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Username,$SecureString"
aryText(11) = "Connect-MicrosoftTeams -Credential $MySecureCreds"
aryText(12) = ""
aryText(13) = "Set-CsUserCallingSettings -Identity username@company.com -IsForwardingEnabled $True -ForwardingType Simultaneous -ForwardingTargetType SingleTarget -ForwardingTarget " & Strings.Chr(34) & myPrimary & Strings.Chr(34)
aryText(14) = "Set-CsUserCallingSettings -Identity username@company.com -IsUnansweredEnabled $True -UnansweredDelay 00:00:30 -UnansweredTargetType singleTarget -UnansweredTarget " & mySecondary
aryText(15) = ""
aryText(16) = "Disconnect-MicrosoftTeams"
aryText(17) = "Set-Content .\Exit.txt -Value '1'"
aryText(18) = "}"
aryText(19) = "catch"
aryText(20) = "{"
aryText(21) = ""
aryText(22) = "Write-Output $_ | Set-Content .\Error.txt"
aryText(23) = "Set-Content .\Exit.txt -Value '99'"
aryText(24) = "}"
Dim objWriter As New System.IO.StreamWriter(FILE_NAME)
For i = 0 To 24
objWriter.WriteLine(aryText(i))
Next
objWriter.Close()
'Clear Error file before writing And running PS Script
Dim stream As New IO.StreamWriter(".\Error.txt", False)
'stream.WriteLine("")
stream.Close()
'Clear Exit file before writing And running PS Script
Dim stream2 As New IO.StreamWriter(".\Exit.txt", False)
'stream.WriteLine("")
stream2.Close()
Dim ErrCode As String
Dim ErrLen As Integer
ErrCode = ""
sps1 = ".\setforwarder.ps1"
objShell.Run("powershell.exe -executionpolicy unrestricted -WindowStyle Hidden -noprofile -noexit -ExecutionPolicy Bypass " + sps1)
Do Until ErrLen > 2
Threading.Thread.Sleep(1000)
ErrCode = System.IO.File.ReadAllText(".\Error.txt")
ErrLen = System.IO.File.ReadAllText(".\Exit.txt").Length
'MsgBox(ErrLen)
Loop
If ErrLen = 3 Then
MsgBox("Forwarding Has Been Confgured.")
Else
MsgBox("The PowerShell command failed with Error: " + ErrCode + Chr(13) + ".")
End If
End If
End If
The PowerShell Script
It was generated by VB.Net and Looks like this:
try
{
# Define Credentials
[string]$Username = 'username@company.com'
[String]$Password = Get-Content ".\creds.txt"
# Convert To secure String
[SecureString]$pass = ConvertTo-SecureString -AsPlainText $Password -Force
$SecureString = $pass
# Create credential Object
$MySecureCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Username,$SecureString
Connect-MicrosoftTeams -Credential $MySecureCreds
Set-CsUserCallingSettings -Identity username@company.com -IsForwardingEnabled $True -ForwardingType Simultaneous -ForwardingTargetType SingleTarget -ForwardingTarget "<Phone Number set in VB.Net>”
Set-CsUserCallingSettings -Identity username@company.com -IsUnansweredEnabled $True -UnansweredDelay 00:00:30 -UnansweredTargetType singleTarget -UnansweredTarget <Phone Number set in VB.Net>
Disconnect-MicrosoftTeams
Set-Content .\Exit.txt -Value '1'
}
catch
{
Write-Output $_ | Set-Content .\Error.txt
Set-Content .\Exit.txt -Value '99'
} What holds this all together is Exit.txt and Error.txt that PowerShell writes to and VB.Net reads from. This gives VB.Net the info it needs to inform the user if an error occurred when running the PowerShell Script. This is one way to Share Data between VB.Net to PowerShell
The Bottom Line on Sharing Data from VB.Net to PowerShell
You may have to think a bit outside the box, but if you research enough accompanied by your programming and scripting skills, you will be able to share data from VB.Net to PowerShell. I know it has helped me with my day-to-day M365 administration tasks. It will help you!
Deactivate Entra PIM Roles with PowerShell

So, you would like to Deactivate Entra PIM Roles with PowerShell? Doing it through the admin portal is a manual procedure but it can be automated. I thought, “You should be able to do this in PowerShell”. You can do a lot of things, so why not do this?
Read on and I will show you who to create and run this script that can start your day without waiting for your currently activated PIM’s to expire.
You should read my previous post on activating Entra PIM roles with PowerShell
But first, what are Entra PIM Roles?
Entra PIM Roles Explanation
As Microsoft states, Privileged Identity Management (PIM) is a service in Microsoft Entra ID that enables you to manage, control, and monitor access to important resources in your organization. These resources include resources in Microsoft Entra ID, Azure, and other Microsoft Online Services such as Microsoft 365 or Microsoft Intune. The following video explains important PIM concepts and features.
Simply put, if your organization is big enough and takes security seriously, it won’t give Global Admin access to just anyone. It is like giving a kid keys to the candy store! Instead, it is better to be more granular with your organization’s admin access. If your roles are already activated, you will have to wait for the duration you set to expire. You can to the roles page and manually deactivate each role. But what if you don’t have to? What if you leave early and you don’t want to manually do it? Fortunately, there is a way to do it through PowerShell. Let me show you how….
Deactivate Entra PIM Roles with PowerShell: Prerequisites
Before you do this, you will need the following:
- You need to be licensed to at least MS Entra ID P2 or Enterprise Mobility + Security (EMS) E5 license.
- You need to have the MgGraph Module installed in PowerShell
- Your PIM Role administrator must assign you your PIM Roles (i.e Teams Administrator, Exchange Administrator etc.)
What the Script Does
Simply put, the script will iterate through a Role list you have created as a CSV file and deactivate each role. As your job changes you modify the CSV file for what roles have been added or taken away from your position. It is quicker than deactivating your PIM than doing it manually. It is all automatic. You can leave work knowing they have been deactivated!
Deactivate Entra PIM Roles with PowerShell: The Script
Make sure your roles.csv file is in the same directory as the script. Here is an example of its format:

The script looks like this:
$CSVPath = ".\roles.csv"
Connect-MgGraph -NoWelcome
$context = Get-MgContext
$currentUser = (Get-MgUser -UserId $context.Account).Id
##Try import CSV file
try {
$Roles = import-csv $CSVPath -ErrorAction stop
}
catch {
throw "Error importing CSV: $($_.Exception.Message)"
break
}
# Get all available roles
$myRoles = Get-MgRoleManagementDirectoryRoleEligibilitySchedule -ExpandProperty RoleDefinition -All -Filter "principalId eq '$currentuser'"
foreach ($Role in $Roles) {
$CurRole = $Role.role
#Get Role to deactivate
$myRole = $myroles | Where-Object {$_.RoleDefinition.DisplayName -eq $CurRole}
write Deactivating $CurRole
#Setup parameters for activation
$params = @{
Action = "selfDeactivate"
PrincipalId = $myRole.PrincipalId
RoleDefinitionId = $myRole.RoleDefinitionId
DirectoryScopeId = $myRole.DirectoryScopeId
}
# Deactivate the role
New-MgRoleManagementDirectoryRoleAssignmentScheduleRequest -BodyParameter $params
}
Write All Done!
Disconnect-MgGraph For a continued explanation of this go here. I hope this quick tip can speed up your day. I know it did mine!!
Activate Entra PIM Roles with PowerShell

So, you would like to Activate Entra PIM Roles with PowerShell? Doing it through the admin portal is a manual procedure and it takes forever!! I couldn’t agree more. I couldn’t take it anymore so I thought to myself, “You should be able to do this in PowerShell”. You can do a lot of things, so why not do this?
Read on and I will show you who to create and run this script that can start your day off quicker. But first, what are Entra PIM Roles?
Entra PIM Roles Explanation
As Microsoft states, Privileged Identity Management (PIM) is a service in Microsoft Entra ID that enables you to manage, control, and monitor access to important resources in your organization. These resources include resources in Microsoft Entra ID, Azure, and other Microsoft Online Services such as Microsoft 365 or Microsoft Intune. The following video explains important PIM concepts and features.
Simply put, if your organization is big enough and takes security seriously, it won’t give Global Admin access to just anyone. It is like giving a kid keys to the candy store! Instead, it is better to be more granular with your organization’s admin access. If you need only user admin, exchange admin and team’s admin access, then that’s all you should have. However, if you have several more roles, going to the roles page and manually activating them can be time consuming. Fortunately, there is a way to do it through PowerShell. Let me show you how….
Activate Entra PIM Roles with PowerShell: Prerequisites
Before you do this, you will need the following:
- You need to be licensed to at least MS Entra ID P2 or Enterprise Mobility + Security (EMS) E5 license.
- You need to have the MgGraph Module installed in PowerShell
- Your PIM Role administrator must assign you your PIM Roles (i.e Teams Administrator, Exchange Administrator etc.)
What the Script Does
Simply put, the script will iterate through a Role list you have created as a CSV file and activate each role you need to be activated for your workday. As your job changes you modify the CSV file for what roles have been added or taken away from your position. It will take about as long to active your PIM as it did before but there is absolutely no manual clicking involved. It is all automatic, leaving you to start other tasks during your day while it is running in the background.
Activate Entra PIM Roles with PowerShell: The Script
Make sure your roles.csv file is in the same directory as the script. Here is an example of its format:

The script looks like this:
$CSVPath = ".\roles.csv"
Connect-MgGraph -NoWelcome
$context = Get-MgContext
$currentUser = (Get-MgUser -UserId $context.Account).Id
##Try import CSV file
try {
$Roles = import-csv $CSVPath -ErrorAction stop
}
catch {
throw "Error importing CSV: $($_.Exception.Message)"
break
}
# Get all available roles
$myRoles = Get-MgRoleManagementDirectoryRoleEligibilitySchedule -ExpandProperty RoleDefinition -All -Filter "principalId eq '$currentuser'"
foreach ($Role in $Roles) {
$CurRole = $Role.role
#Get Role
$myRole = $myroles | Where-Object {$_.RoleDefinition.DisplayName -eq $CurRole}
write Activating $CurRole
#Setup parameters for activation
$params = @{
Action = "selfActivate"
PrincipalId = $myRole.PrincipalId
RoleDefinitionId = $myRole.RoleDefinitionId
DirectoryScopeId = $myRole.DirectoryScopeId
Justification = "Needed for work"
ScheduleInfo = @{
StartDateTime = Get-Date
Expiration = @{
Type = "AfterDuration"
Duration = "PT8H"
}
}
}
# Activate the role
New-MgRoleManagementDirectoryRoleAssignmentScheduleRequest -BodyParameter $params
}
Write All Done!
Disconnect-MgGraph For a continued explanation of this go here. I hope this quick tip can speed up your day. I know it did mine a bit…
Unfortunately, there are a few roles that you get an error and will have to manually activate. For me, it is my company’s firewall that is the issue. The only info about it I dug up here. The issue has been around for over a year and is still not fixed. The roles I had issue activating through the script are as follows:
User Administrator
Exchange Administrator
Share Point Administrator
Security Administrator
Intune Administrator
Conditional Access Administrator
But when I went through an internet connection without out a restrictive firewall I was able to active all roles through PowerShell
If you want to learn how to Deactivate the roles with PowerShell, Go here.
VB.Net and M365

It is funny but VB.Net and M365 go together like peanut butter and jelly. VB has been around for years, and I always seem to find a use for it when dealing with Microsoft products. I remember using it fresh out of school. I have worked with it so much I am going to add a dedicated section to the blog about it, I hope it will help you automate or improve some of your M365 Administration. First a little history…
VB used for Automation
Those were the days of pagers (I know I am dating myself here). But I developed a program where you could automatically page you when someone called your land line (If you know what a land line is, then you are old like me).

VB developed for the Internet
With VB6 and onward there was the ability to program for the internet. I thought Internet control was cool. So, I wrote a program that would check POP/IMAP email and let you know if you had mail. It seems basic now but back in the day it was an advantage. You didn’t have to load your large email program to see if you had mail. Just a little VB program saving tons of memory on your computer for other tasks. It doesn’t mean much now but it did back then, Fast forward a little later….Sorry No Screenshots!
VB becomes VB.Net and Databases
With the advent of visual studio MS changed the name to VB.net and became able to handle a lot of different things. I started using it as a front end for database. It makes a great front end to Access and SQL DB’s. I was able to incorporate this like data entry, reports, exports, print, security and auditing of a system to name a few.
VB.Net and M365 Administration
Now the part you all have been waiting for. Thanks for going down memory lane with me. What a trip. I can’t believe how much I have used VB over the years. It is no surprise that I would use it for M365 Automation. From creating JSON and CSV to ICS to files to be imported my M365 Apps to automating PowerShell scripts and MSGraph, it is quite versatile.
Stay Tuned for VB.Net and M365
So, adding it as a dedicated section to the blog like the sections I have made it a no brainer. Stay tuned for information on using VB.Net and M365!
Configuring Location Services in Intune

You have set up an enrollment profile that uses Location Services in Intune for your Android devices. Great. It can be very useful when you need to locate a lost or stolen device in Microsoft Intune Admin.
Here’s the thing though, if you do not allow the Intune app permission to access the devices’ location all the time, it won’t work.
Follow these steps.
Steps to Configure Location Services in Intune
- Swipe down from the top of the phone screen and tap settings in the top right of the screen.
- Search for location:

3. Once in Location, tap “App Permissions”:

4. Once in App permissions, scroll down until you see the Intune app. Since it is among the apps not allowed to access location data, it will be further down the screen. When you find it, tap it.:

5. In the Intune App permissions screen, make sure “Allow all the time” is chosen. Then tap the back arrow.

Locations services are now configured for your phone. It would be foolish to choose “allow only while using this app” or “Ask Every time”. Nobody knows when or how they are going to have their phone lost or stolen LOL.
One Consideration when Configuring Location Services
When I configured a usage policy for a group of mobiles, I had another group of mobiles excluded from this policy. They had to be configured slightly differently. The problem was location services were not enabled in their policy. Never assume it will always work. I had to go into the second policy and enabled it.
Follow these steps to Location Services on Intune and you will be good to go!
One Way to Stop Auto-Archiving Outlook

So, you are stuck in the old ways of Outlook, and you feel the need to Auto-Archive your emails when you think that your storage is being all user up. What if I could show you how to Stop Auto-Archiving Outlook.
If you are using Outlook through an M365 subscription you have a minimum of 50GB Storage for your email. Depending on your license you could have up to 100 GB storage. I don’t know about you but that is a lot.
Back in the day 2-10 GB storage was considered a lot, so you would have to use Auto Archive a lot. I would try not to use it at all if you can. It really is a pain as it creates another storage location for your mail, and I like to keep it all in one place.
How to Stop Auto-Archiving Outlook
Go to the control panel and search for mail:

When you click mail, go to your account settings:

Double click your account, it will take you to your cache settings:

In this example I am keeping 3 years of mail cached. Anything older will be in the cloud, accessible by going to https://outlook.office.com/mail/ . This will reduce the size of the mail being stored on your computer.
Benefits of Not Auto-Archiving Outlook
The benefits are twofold:
- It reduces the storage space on your computer. This is helpful if you are using a device with limited storage space (Surface Tablet or any computer with a smaller hard disk)
- You only have one storage location for all your mail. If you need to retrieve mail from outside your cached timeframe all you must do is go to the cloud.
What you can see here is that users forget that Outlook is always updating and Auto-Archive is an ooutdated concept. Storage is no longer the precious commodity it once was!
The Evolution of Information Rights Management

A user brought this functionality to my attention. Unfortunately, the Evolution of Information Rights Management put me onto a wild goose chance. Sometimes, trying to keep up with Microsoft’s changes are a bit dizzying to say the least.
I try not to be too editorial on this blog. I just want to give Quick M365 Tips so you can admin your M365 tenant so you can go along your merry way. Sometimes, it is more difficult than it has to be. This is where the frustration lies.
What I will give you is an account of where I started and where I ended up…..
The Problem
A user of mine has several spreadsheets that contain macros and queried data. Most users who need these sheets need to be only able to view and filter the data. Currently, the only way it can be done is manually locking and unlocking the spreadsheets, thereby reducing its functionality. This is very cumbersome.
He was looking for a solution that will allow the users to have the limited functionality without the need to manually lock and unlock the sheets. Enter IRM (Information Rights Management).
What IRM Was
IRM used to be quite simple. You Configure it using SharePoint, install an IRM client on your computer and viola, start protecting Office documents (in this case, excel) and limit certain functions of the document depending on the user that is accessing it.
But something happened and IRM changed… a lot. Now you must configure a whole bunch of things and remember acronyms like AARM, RMS and AIP. Do not forget Azure, SharePoint and Exchange Online. This article tries to explain it.
What IRM is Now (Evolution of Information Rights Management)
It seems the Evolution of Information Rights Management is now mostly a cloud service. It cannot be used until you activate the Rights Management Service. So you will need to go to Azure Admin first and then SharePoint Admin. Also, do not forget to install the AIP Service in PowerShell.
You are going to get a lot of errors trying to get all of this set up. There will be several calls made to Microsoft support. They might be able to help you, maybe not. It really depends on when you installed it and configured your tenant and who you get when you call MS support!
If you are an old pro with O365/M365 Admin you will probably look at this article and think “It is really not that hard” but you have evolved with the technology over the years.
It you are trying to get IRL installed and configure on a relatively new tenant then you are going to have to do some research. The problem is, you must piece it together from several loosely related articles on MS’s site or various other forums and blogs, etc.
And there is a very good chance you will not get it right the first time. Microsoft, you must do better than this. If you aren’t going to slow down whatever changes you make at least make better documentation!
Oh, and just so you know, I still haven’t got IRL fully setup. Results pending…
