Azure AD

Please do NOT disable Security Defaults!

If you aren’t licensed for and using Conditional Access policies, please do not disable the security defaults feature just because something isn’t working (e.g. scan to email). Microsoft introduced the defaults for a very good reason – they realised that tenants without Azure AD Premium P1 licensing and correctly configured CA policies were wide open to Phishing and Password Spray attacks, via connections to Exchange Online using basic authentication protocols such as POP, IMAP and SMTP.

Connections using basic authentication do not support and therefore bypass MFA. If you disable this setting you are effectively turning off many security features.

Let’s find a solution to these problems and leave our tenant protected ‘by default’.

Here’s how to allow certain things (e.g. Teams meeting room devices and printers) while leaving our tenant secure:

1. Add any external IPs of company locations to Trusted IPs under MFA settings. In most cases you would do this for all company owned office locations.

2. Set Password Reset Registration to No so that new users are not prompted to register.

3. If you need to send SMTP email through Exchange Online (e.g. from a printer), create an account with exchange license to use for sending.

4. Load Cloud Shell from top of the Azure Portal. Connect to Exchange:


5. Create an EOL Authentication Policy:

New-AuthenticationPolicy -Name “Allow Basic Auth SMTP” -AllowBasicAuthSmtp

6. Assign the policy to the user:

Get-User | Set-User -AuthenticationPolicy “Allow Basic Auth SMTP”

7. Optionally, force the policy to apply within 30 minutes:

Set-User -STSRefreshTokensValidFrom $([System.DateTime]::UtcNow)

Now your users and devices will be able to connect without MFA requirement from trusted offices, and you can set up Scan to Email functions to use the account you created.

Righto – time have a cup of tea and reward yourself for not chopping off a leg to fix an itch! 🤣🤣


Runbook: Sync Shared Mailbox accounts with an Azure AD Group

Hey! I hope you are well.. 🤘 🙂 🤘. This script was a result of the following ponderings:

  • How to monitor and manage the deletion of Blocked (Disabled) and Guest accounts in Azure AD.
  • I have a Dynamic group for ‘Blocked (Disabled) users’, but members include valid Shared Mailbox accounts.
  • What about Guest users… should I just leave them? 🤣

Noooo, I shouldn’t… paying monthly subscriptions it’s important to stay on top of user account maintenance. There are some reports and sorting you can do, and Power BI, Graph etc, but I wanted to script something!! In my usual non-perfect PowerShell way of course, but hey it gets the job done.

The Guest users are easy to group using a Azure AD Dynamic Security group with this Rule Syntax:

(user.userType -eq "Guest") and (user.accountEnabled -eq true)

Sweet! My Blocked (or Disabled) users group is Dynamic as well, using this syntax:

(user.accountEnabled -ne true) and (user.surname -ne "Shared_Mailbox")

I’ve only just added (user.surname -ne “Shared_Mailbox”) – the script sets that attribute when it adds an account to the Shared Mailbox group, so that the accounts are excluded from the Dynamic Bocked Users group. Cool now I can actually review the blocked users knowing my Shared Mailbox accounts are safe!

I could also use Conditional Access policies to increase the security of those accounts!

Here is the script… you need to have an Automation account with credential set up (this can be a Synced AD or cloud account that has ‘Exchange Recipient’ and ‘Group Administrator’ roles assigned. Make sure you have imported the AzureAD and ExchangeOnlineManagement modules into the Automation account, and have created the Azure AD Group (set the group to ‘Assigned’ membership rather than ‘Dynamic’). From Azure AD navigate to Groups, search for your group and click on it. You will be able to copy the Object ID from here:

Enter that for $sharedmailboxgroupid and the ‘Name’ of your Automation account credential as $runbookcredentialname. That’s it – give it a good testing and whack eem into production mate!

(NB – following all relevant change control precedures of course!)

See below the code for how the output looks in the Runbook logs… great for troubleshooting!

See ya! 🍺

# use TLS 1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

# Sync shared mailbox accounts with an Azure AD group - Simon Burbery - November 2021
# Update variables with the name of your runbook credential and the Azure AD Object ID displayed on the groups overview page in the AAD portal.

# set variables
$runbookcredentialname = 'svc_runbookcredential'
$sharedmailboxgroupid = '12345678-abcd-4321-0987-665544332211'

# get credential for connections
Try { 
    $CredAzure = Get-AutomationPSCredential -Name $runbookcredentialname
        Catch {
            Write-Error "Failed to get credential!"
Write-Output "Get automation credential - Success"

# connect Azure AD
Try {
    Connect-AzureAD -Credential $CredAzure | Out-Null
        Catch {    
            Write-Error "Failed to connect to Azure AD!"
Write-Output "Connect to Azure AD - Success"

# connect EOL
Try {
    Connect-ExchangeOnline -Credential $CredAzure
        Catch {    
            Write-Error "Failed to connect to EOL!"
Write-Output "Connect to EOL - Success"

# get group name
$groupname = (Get-AzureADGroup -ObjectId $sharedmailboxgroupid).DisplayName

# get all shared mailboxes and group members
Write-Output "Enumerating Shared Mailbox accounts and $groupname membership..."
Try {
    $sharedmailboxaccounts = Get-Mailbox -ResultSize Unlimited | Where-Object { $_.RecipientTypeDetails -eq 'SharedMailbox' } | select ExternalDirectoryObjectID,UserPrincipalName
    $currentgroupmembers = Get-AzureADGroupMember -All $true -ObjectId $sharedmailboxgroupid | select ObjectID,UserPrincipalName
        Catch {    
            Write-Error "Failed to enumerate Shared Mailbox accounts or $groupname membership!"
Write-Output "Enumerate Shared Mailbox accounts and $groupname membership - Success"

# remove any members that are no longer shared mailboxes
Write-Output "Verify $groupname membership..."
Try {
    foreach ( $groupmember in $currentgroupmembers ) {
        $groupmemberid = $groupmember.ObjectID
        $groupmemberupn = $groupmember.UserPrincipalName
        $checkmember = ( $sharedmailboxaccounts.ExternalDirectoryObjectId -contains $groupmemberid )
            If ( $checkmember -ne 'True' ) {
                Write-Output "Shared Mailbox not found - removing $groupmemberupn from $groupname..."
                Remove-AzureADGroupMember -ObjectId $sharedmailboxgroupid -MemberId $groupmemberid
                Set-AzureADUser -ObjectId $groupmemberid -Surname 'Disabled User'
                Else {
                    Write-Output "Shared Mailbox found - skipping $groupmemberupn"
        Catch {    
            Write-Error "Error while removing accounts from group!"
Write-Output "Verify $groupname membership - Success"

# add new shared mailbox accounts to Azure AD group
Write-Output "Checking for new Shared Mailboxes..."
Try {
    foreach ( $sharedmailboxaccount in $sharedmailboxaccounts ) {
        $sharedmailboxaccountid = $sharedmailboxaccount.ExternalDirectoryObjectId
        $sharedmailboxaccountupn = $sharedmailboxaccount.UserPrincipalName
        $checkmembersm = ( $currentgroupmembers.ObjectID -contains $sharedmailboxaccountid )
            If ( $checkmembersm -ne 'True' ) {
                Write-Output "New Shared Mailbox - adding $sharedmailboxaccountupn to $groupname..."
                Add-AzureADGroupMember -ObjectId $sharedmailboxgroupid -RefObjectId $sharedmailboxaccountid
                Set-AzureADUser -ObjectId $sharedmailboxaccountid -Surname 'Shared_Mailbox'
                else {
                    Write-Output "Skipping Shared Mailbox $sharedmailboxaccountupn"
        Catch {    
            Write-Error "Error while adding accounts to group!"
Write-Output "Check for new Shared Mailboxes - Success"

# clean up
Disconnect-ExchangeOnline -Confirm:$false
Disconnect-AzureAD -Confirm:$false

# end

And here is what I really like about using Runbooks – the output from the script is available to go back and look at when failures occur etc. Nice!


Azure AD – export groups and members to CSV

UPDATE Feb ’23 – David made me do it – well, he didn’t make me at all really, but I did it anyway 🙂. Check out this new post which uses AzAD and AzureAD cmdlets to get the groups and members email, UPN and ObjectID (catering for different member types and groups with no members):

Azure AD – export groups and members #2

UPDATE June ’22 – for on-premises AD check out Active Directory – export groups and members (with email addresses).

# export azure ad groups and members to csv (also output empty groups with 'No Members' value) 
# assumes existing connection to Azure AD using Connect-AzureAD (or use a runbook)

$allgroups = Get-AzureADGroup -All $true | select ObjectId,DisplayName

$result = foreach ( $group in $allgroups ) {

    $hash = @{GroupName=$group.DisplayName;Member=''}
    $groupid = $group.ObjectId
    if ( $members = Get-AzureADGroupMember -ObjectId $groupid ) {
            foreach ( $member in $members ) {

                $hash.Member = $member.DisplayName
                New-Object psObject -Property $hash
        $displayname = "No Members"
        $hash.Member = $displayname
        New-Object psObject -Property $hash

$result | Export-Csv -Path C:\temp\AzureADGroups.csv -NoTypeInformation

# End

PowerShell get azure ad group members export to csv

export azure ad group members to csv PowerShell

PowerShell export azure ad user group membership to csv