Add members to Azure AD/Microsoft 365 Groups in bulk

We can bulk update Microsoft 365/Azure AD Groups using PowerShell. We just need a list of users UPN or primary SMTP Addresses.

Get the group’s object ID.

You can copy the group’s object ID from Azure AD console from properties tab or use below command.

#Connect Azure AD
Connect-AzureAD
(Get-AzureADGroup -SearchString Test-Group).ObjectId

If you want to add a mailbox to the group, use below command, it will add User@domain.com to Test-Group

#Connect Azure AD and Exchange online if not connected already. 
Connect-AzureAD
Connect-ExchangeOnline -UserPrincipalName <Your O365 Admin ID>

Add-AzureADGroupMember -ObjectId <Group's Object ID> -RefObjectId (Get-Mailbox User@domain.com).ExternalDirectoryObjectId

If you want to add a set of Azure AD users, for example all users start with TestUser, below command will add all the users start with TestUser to TestGroup

Get-AzureADUser -SearchString TestUser | foreach {Add-AzureADGroupMember -ObjectId <Gorup's ObjectID> -RefObjectId $_.ObjectID}

If you have a list of users, create a file Userlist.txt [One UPN or email per line] and save in C:\Temp folder. Below command will add all the users mentioned in Userlist.txt to the Test-Group

#Connect Azure AD and Exchange online if not connected already.
Connect-AzureAD
Connect-ExchangeOnline -UserPrincipalName <Your Microsoft 365 Admin ID>
Get-Content C:\Temp\UserList.txt | foreach {Add-AzureADGroupMember -ObjectId <Group's Object ID> -RefObjectId (Get-Mailbox $_).ExternalDirectoryObjectId}

Add Microsoft 365 licenses in bulk

We can add bulk license to Microsoft 365 users using Microsoft 365 PowerShell, though it is always good to create a Dynamic group for license assignment. But, we can have situation when we want to add licenses to some users who already have other license assigned and doesn’t have any common attribute to create and add new license.

For Example, you have new “Phone System” license and want to add that license to selected users for testing or for UAT.

If user doesn’t have any license and we are adding license for first time, then we will have to set location first.

Step 1 –

Get list of users and save in a file “UserList.txt”, one UPN per line and place in C:\Temp folder on your computer.

The below command will set location on all users mentioned in “UserList.txt” to US.

Connect-MsolService

#When prompted enter your Microsoft 365 Admins accounts UPN and password.

Get-Content C:\Temp\UserList.txt | foreach {Get-MsolUser -UserPrincipalName $_ | Set-MsolUser -UsageLocation US}

If users are being synced from On-prem AD, you can set msExchUsageLocation on on-prem AD users and wait for the Sync to complete.

Run below command on your On-Prem AD server.

Get-Content C:\Temp\UserList.txt | foreach {Get-ADUser -Filter {UserPrincipalName -eq $_ | Set-ADUser -Add @{msExchUsageLocation = "US" }

Step 2 –

Get the SkuPartNumber of the license you want to add to users.

Connect-AzureAD

#When prompted enter your Microsoft 365 Admins accounts UPN and password.

Get-AzureADSubscribedSku | Select Sku*, ConsumedUnits

Note down the license Sku number to use in next command. For example for “Phone System” SkuPartNumber is MCOEV.

Step 3 –

The below command will add “PhoneSystem” license to all users and there would not be any change in existing license.

Get-Content C:\Temp\UserList.txt  | foreach { Write-Host "Processing $_";  Set-MsolUserLicense -UserPrincipalName $_  -AddLicenses "YourTenantname:MCOEV" }

Get Distribution Group Owner lists and email

User below PowerShell one liner we can list group owners and their email ID in Microsoft 365.

To get for one group.

Get-DistributionGroup TestGroup@Domain.com | select PrimarySmtpAddress, @{n= "ManagedBy"; e={$_.ManagedBy | foreach {(Get-Mailbox $_).PrimarySMTPAddress}}}

To get for all groups

Get-DistributionGroup | select PrimarySmtpAddress, @{n= "ManagedBy"; e={$_.ManagedBy | foreach {(Get-Mailbox $_).PrimarySMTPAddress}}}

We can also filter groups and get the list. Below command will list all the groups starting with DL-IT

Get-DistributionGroup DL-IT* | select PrimarySmtpAddress, @{n= "ManagedBy"; e={$_.ManagedBy | foreach {(Get-Mailbox $_).PrimarySMTPAddress}}}

Remove Reoccuring meeting invite

There are situations when an organizer wants to cancel a meeting invite, but not able to delete and invite re-appears in attendees calendars, and we may need to remove the invite from backend.

It becomes extremely important to examine the results before deleting them to make sure you have targeted the correct meeting invite or email.

If you want to search all the mailboxes for specific meeting, use below command. This command will give you estimated results only and will not delete anything from the mailboxes.

Get-Mailbox | Search-Mailbox -SearchQuery "kind:meetings AND Subject:'Bi Weekly IT Review Meeting' AND From:User@domain.com" -EstimateResultOnly

If you want to search a set of mailboxes, you can copy the PrimarySMTP addresses in a list and use the command like below.

The below command will export all the specified meeting invite from all mentioned mailboxes to a target folder in a target mailbox.

This command will also not delete anything from mailbox, once results are exported you can review the results and make sure you are targeting the correct meeting invite or email.

Get-Content C:\Temp\List.txt | Get-Mailbox | Search-Mailbox -SearchQuery "kind:meetings AND Subject:'Bi Weekly IT Review Meeting' AND From:User@domain.com" -TargetMailbox ExportResult@Domian.com -TargetFolder "ExportResult"

Once you are sure that correct meeting invite or email will be deleted, you can run the delete command to remove the meeting.

Search-Mailbox -SearchQuery '(subject:"Bi Weekly IT Review Meeting") AND (kind:meetings) and from:User@domain.com' -DeleteContent -Loglevel Full  -TargetMailbox ExportResult@Domian.com -TargetFolder "ExportResult"

On the target mailbox, you can download the logs to see all the actions.

Your flow needs attention…

When you have a flow running with your account and have MFA enabled, you often receive an error email about your flow connection, which states “YOUR FLOW NEEDS ATTENTION….”

This notification was sent as the flow had failure runs due to invalid flow connection.

We can fix this by just reauthenticating the flow connection but if this comes too frequent then it may require some back end fix.

  • Get rid of “Remember Multi-factor authentication”
    • If you are using “Remember Multi-factor authentication” option from
    • AAD Console –> Users –> All Users –> Multi-Factor Authentication -> Service Settings.
  • We should move away from this setting and configure the same using Conditional Access policy.
  • This setting overrides the default behavior for modern authentication clients (like Microsoft Outlook) who only prompt every 90 days, by default.
  • We can configure the same setting using Conditional Access Policies –
  • AAD Console -> Security -> Conditional Access -> Access Controls -> Sessions –> Sign-in Frequency.
  • Exclude selected users from MFA
    • The one solution is to exclude the user from MFA, but this may not be possible in all cases, especially when we have MFA enabled for all the accounts.
    • If you apply MFA using conditional access policies, then excluding some users is simple, just create one group and then all such users to that group and exclude the group in the policy.
    • Open Azure AD console and select Security and click Conditional Access.
    • If you already have a policy, open the policy and in Assignment section -> Users and groups – Exclude – Add the group.
  • Exclude Flow location IPs from MFA.
    • The another solutions can be to exclude the Flow location services IP from MFA.
    • For this create a “New Network” location, like “IP range for Flow in APAC”.
    • Azure AD Console -> Security -> Named Locations.
  • Add IPs of Flow of your tenant location, you can find IPs using below link.

https://docs.microsoft.com/en-us/power-automate/limits-and-config

  • Now in the Conditional Access Policy, Assignments -> Conditions  -> Locations – Exclude the Name location you have created.

allow or suppress the auto replies when sending emails to groups.

Recently one of our users reported an issue that when he sent an email to a large distribution list, he did not receive any Auto Reply, OOF from any user. Though, there were many users had Auto Reply configured at that time.

There are few group properties which can cause this –

ReportToManagerEnabled –  The ReportToManagerEnabled parameter specifies whether delivery status notifications (also known as DSNs, non-delivery reports, NDRs, or bounce messages) are sent to the owners of the group (defined by the ManagedBy property). Valid values are

  • $true: Delivery status notifications are sent to the owners of the group.
  • $false: Delivery status notifications aren’t sent to the owners of the group. This is the default value.

ReportToOriginatorEnabled –  The ReportToOriginatorEnabled parameter specifies whether delivery status notifications (also known as DSNs, non-delivery reports, NDRs, or bounce messages) are sent to senders who send messages to this group. Valid values are:

  • $true: Delivery status notifications are sent to the message senders. This is the default value.
  • $false: Delivery status notifications aren’t sent to the message senders.

The ReportToManagerEnabled and ReportToOriginatorEnabled parameters affect the return path for messages sent to the group. Some email servers reject messages that don’t have a return path. Therefore, you should set one parameter to $false and one to $true, but not both to $false or both to $true.

SendOofMessageToOriginatorEnabled –  The SendOofMessageToOriginatorEnabled parameter specifies how to handle out of office (OOF) messages for members of the group. Valid values are:

  • $true: When messages are sent to the group, OOF messages for any of the group members are sent to the message sender.
  • $false: When messages are sent to the group, OOF messages for any of the group members aren’t sent to the message sender. This is the default value.

Based on the above values, Exchange Online adds a header “X-Auto-Response-Suppress” to message to suppress or allow the Auto Replies or OOF.

Like in my example – SendOofMessageToOriginatorEnabled was False and I can see header “X-Auto-Response-Suppress” with value “DR, OOF, AutoReply” in the message properties.

We can adjust these values to allow or suppress the Auto Replies, OOF from groups.

Microsoft Teams Storage Location

Microsoft Teams chats are persistent, this means the chat will always be available. Though, we can retain or delete the chat data by creating retention policies from Security & Compliance Center for Teams chat and channel messages.

But, where does the chat data and files shared in chat get stored ?

Teams stores chat data in a primary storage and secondary storage for compliance. Below is the brief details about Teams storage locations.

DataPrimary StorageSecondary Storage
MessagesThe Chat service stores its messages in Azure Cosmos DB.Compliance records for messages are stored in group and personal Exchange Online mailboxes [Under a hidden folder in mailbox]
ImagesMedia service (Azure) using blob storage.Any images referenced in messages are also copied in compliance records and stored in Exchange Online.
FilesPersonal files are in users’ OneDrive for Business accounts. Files shared in channels are in the team’s SharePoint document library. 
VoicemailPersonal Exchange mailboxes. 
RecordingsMeeting recordings are captured in a media service in Azure (blob storage).Within 24 hours, the recordings are ingested by Stream and available as video/audio files there.
CalendarsGroup and personal Exchange mailboxes. 
ContactsPersonal Exchange mailboxes. 
 

Get Multi-factor authentication details for Microsoft 365 Users.

Multi-factor authentication is a process where a user is prompted during the sign-in process for an additional form of identification, such as to enter a code on their cellphone or to provide a fingerprint scan.

In Azure AD, user can register for MFA using below link. We can also use conditional access policy to force users to register for MFA, before we implement MFA to avoid any inconvenience.

https://aka.ms/mfasetup

Using the below scripts, we can get details of MFA registered users and methods they are using for MFA.

The script takes three parameters.

-UPN – You can pass any single user’s UPN and will get details of MFA for user.

-UserList – You can pass a list of users as a txt file. One UPN per line.

-All – You can pass All switch if you want to get details of all users in your Microsoft 365 environment.

EXAMPLE 1 – This example will give MFA details of supplied UPN “User@domain.com”

Get-MFAUserDetails -UPN User@domain.com

EXAMPLE 2 – This example will give MFA details of users supplied in “MFAUPN.txt” file, one UPN per line, without any header.

Get-MFAUserDetails -UserList C:\temp\MFAUPN.txt

EXAMPLE 3 – This example will give MFA details of ALL Users enabled for MFA in your Microsoft 365 environment.

Get-MFAUserDetails -All

Download the script from here.

<#
.Synopsis
  Script to get details of MFA enabled users.

.DESCRIPTION
 This script can be used to get details of MFA enabled users

. NOTES
   Author Subodh Uniyal <TechCognizance@outlook.com>

. Disclaimer: Author holds no responsibility to damages caused due to incorrect use of this script.
  It is recommended that you run this script in  your lab before using in production.

.EXAMPLE
This example will give MFA details of supplied UPN "User@domain.com"

   Get-MFAUserDetails -UPN User@domain.com

.EXAMPLE
This example will give MFA details of users supplied in "MFAUPN.txt" file, one UPN per line, without any header. 

 Get-MFAUserDetails -UserList C:\temp\MFAUPN.txt

 .EXAMPLE
This example will give MFA details of ALL Users enabled for MFA in your Microsoft 365 environment. 

 Get-MFAUserDetails -All

#>

    Param
    (
        
        [Parameter(ValueFromPipeline=$true)]
                   [String[]]$UserList,
                   [String]$UPN,
                   [String[]]$All
    )

#Connect to MSOL
Write-Host "Checking Current MsolService Session"
try {
    Get-MsolDomain -ErrorAction Stop | Out-Null
} catch {
Write-Host "No current session detected. Please supply credentials to connect to Microsoft Online Service"
Connect-MsolService
}

$Result = @()
$Results = @()
$reportPath = ".\"
$ReportName = "MFAReport_$(get-date -format dd-MM-yyyy_hh-mm-ss).CSV"
$MFAReport = $reportPath + $reportName


Function Get-MFAUserDetails
{
$Result = @()
$Results = @()
Foreach ($User in $List)
{

Write-Host "Processing user "$User.DisplayName""
    $StrongAuthenticationRequirements = $User | Select-Object -ExpandProperty StrongAuthenticationRequirements
    $StrongAuthenticationUserDetails = $User | Select-Object -ExpandProperty StrongAuthenticationUserDetails
    $StrongAuthenticationMethods = $User | Select-Object -ExpandProperty StrongAuthenticationMethods

$Result = [PSCustomObject]@{
DisplayName = $User.DisplayName
UPN = $User.UserPrincipalName
IsLicensed = $User.IsLicensed
RememberDevicesNotIssuedBefore = $StrongAuthenticationRequirements.RememberDevicesNotIssuedBefore
StrongAuthenticationUserDetailsPhoneNumber = $StrongAuthenticationUserDetails.PhoneNumber
StrongAuthenticationUserDetailsEmail = $StrongAuthenticationUserDetails.Email
DefaultStrongAuthenticationMethodType = ($StrongAuthenticationMethods | Where {$_.IsDefault -eq $True}).MethodType
}
$Results +=$Result
}
$Results | ft
$Results | Export-Csv $MFAReport -NoTypeInformation
$Location = (Get-Location).path + "\" + "$ReportName"
Write-Host "Results are saved in File $Location" -ForegroundColor Yellow
}

 IF ($UserList) 
    {
    $List = Get-Content -Path $UserList | foreach {Get-MsolUser -UserPrincipalName $_}
    Write-Host "Processing with UserList provided, there are "($List).count" users to process."
    
    Get-MFAUserDetails($List)
    }
     ElseIf ($UPN)
       {
       $List = Get-MsolUser -UserPrincipalName $UPN
       Get-MFAUserDetails($List)
       }
        ElseIF ($All)
         {
         $List = Get-Msoluser -all | Where-Object {$_.StrongAuthenticationMethods -like "*"}
         Write-Host "Processing with all users enabled for MFA"
          Get-MFAUserDetails($List)
         }
           Else
           {
           Write-Host "Please input at least one parameter, check help for details."
           }

Recover Deleted Emails in Microsoft 365

In The Previous article we have learnt, how we can find the cause of deleted emails from users’ mailbox.

In similar way, we can also recover selected deleted emails back to the user’s mailbox original folder, without any import of export process.

Search items for particular time frame :-

Get-RecoverableItems -Identity User@domain.com -SourceFolder RecoverableItems -FilterStartTime "12/04/2020 00:00:00" -FilterEndTime "12/05/2020 23:00:00"

To restore these items, we can simply pipe this to Restore RecoverableItems and save results. Once the command is complete, you can examine file RestoreLogs.csv for items restored.

Get-RecoverableItems -Identity User@domain.com -SourceFolder RecoverableItems -FilterStartTime "12/04/2020 00:00:00" -FilterEndTime "12/05/2020 23:00:00"  | Restore-RecoverableItems | Export-Csv C:\Temp\RestoreLogs.csv -Append -NoType

If we want to restore only actual email items, we can use FilterItemType

Get-RecoverableItems -Identity User@domain.com -SourceFolder RecoverableItems -FilterStartTime "12/04/2020 00:00:00" -FilterEndTime "12/05/2020 23:00:00"  -FilterItemType Ipm.Note | Restore-RecoverableItems | Export-Csv C:\Temp\RestoreLogs.csv -Append -NoType

In case we want to restore emails deleted from “Sent Items” only, we can use this.

Get-RecoverableItems -Identity User@domain.com -FilterItemType Ipm.Note -SourceFolder RecoverableItems | where {$_.LastParentPath -eq "Sent Items"} | Restore-RecoverableItems | Export-Csv C:\Temp\RestoreLogfile.csv -Append -NoType

You cannot search folders in the archive mailbox. To search for deleted items across all locations, don’t pass a SourceFolder parameter to Get-RecoverableItems. For example, this search finds all deleted items for the specified mailbox:

$Items = Get-RecoverableItems -Identity User@domain.com

We can then filter the $items variable and restore the selected emails.

Location of Deleted Items –

  • DeletedItems: The Deleted Items folder in the user’s mailbox.
  • RecoverableItems: The Deletions sub-folder in the Recoverable Items folder of the user’s mailbox. This is where items go after they are removed from the Deleted Items folder. Exchange’s Single Item Recovery (SIR) feature holds items deleted from the Deleted Items folder in the Deletions folder until the deleted items retention period defined for the mailbox expires (typically 14 days with a maximum of 30 days). SIR exists to ensure that users have a reasonable chance of recovering deleted items if they make a mistake.
  • PurgedItems: The Purges sub-folder in the Recoverable Items folder. Deleted items kept due to a retention policy are stored here until the retention period set in the policy expires.

Searching for Specific Types of Deleted Items:-

• IPM.Note: A standard email message item.

• IPM.Appointment: A calendar meeting or appointment.

• IPM.Task: A task.

• IPM.Contact: A contact.

• IPM.File: A file stored in the mailbox. These include files created by Office 365 as the result of some processing.

Search Mailbox Audit Logs in Office 365

There may be situations when end user(s) report missing emails from their mailbox. It may be possible that they have inadvertently deleted the emails, but want to know the cause.

If we have mailbox auditing enabled, we can find out the cause. The auditing should be enabled by default in Microsoft 365.

The first step is to track the emails and see, if there were emails delivered/Sent to/from user’s mailbox. We can use tracking logs for this.

To check the emails received –

Get-MessageTrace -RecipientAddress User@domain.com -StartDate 12/10/2020 -EndDate 12/18/2020 -Status Delivered

To check the email received –

Get-MessageTrace -RecipientAddress User@domain.com -StartDate 12/10/2020 -EndDate 12/18/2020

If we got the emails in result and these emails are not in user’s mailbox, then we need to investigate further.

Check if the mailbox Audit logs are enabled for user.

Get-Mailbox User@domain.com | ft PrimarySMTPAddress, AuditEnabled

Get the Mailbox Folder Statics to check if there are emails in Deleted items.

Get-MailboxFolderStatistics "User@domian.com" -FolderScope RecoverableItems -IncludeOldestAndNewestItems | Format-List Name,FolderAndSubfolderSize

If auditing is enabled, we can check the mailbox audit logs and find the delete cause.

Search the Audit Logs for the user:-

Search-MailboxAuditLog User@domain.com -ShowDetails -StartDate 12/10/2020 -EndDate 12/18/2020 -ResultSize 250000 | Export-Csv -Path C:\Temp\Mailbox_Audit_Logs.csv -Notype

Open the audit logs in excel and then filter the Operation column to filter for deleted events.

Here, we can see that the mailbox owner have deleted the emails from “Sent Items” folder.

We can also run the below script to find the Audit Logs –

https://docs.microsoft.com/en-us/office365/troubleshoot/audit-logs/mailbox-audit-logs

In next article, we will learn how to restore deleted emails back to user’s mailbox.

« Older Entries Recent Entries »