Category Archives: Azure

Understanding User Hard Matching and Soft Matching in Azure AD Connect

Azure AD Connect

Dealing with hybrid identity can be scary and complex but in this post I will assist you in understanding how objects and their attributes are synchronized between on-premises Active Directory and Azure AD.

So lets look at provisioning an object from on-premises Active Directory to Azure AD, Azure AD performs additional actions one time, when compared to updating a user object. These two actions are named:

  • Hard Matching
  • Soft Matching

The actions are performed in the above sequence; Hard matching is attempted, before soft matching is attempted. If there’s no match, a new user object is created in Azure AD to correspond to the user object in the on-premises Active Directory environment.

Hard Matching

To definitively match an on-premises Active Directory user object to an Azure AD user object, Azure AD Connect looks at the sourceanchor attribute.

During normal synchronization cycles, this attribute is already used to provide the end-to-end connection between the on-premises Active Directory user object and the Azure AD user object through Azure AD Connect’s connector spaces and metaverse, so it’s an ideal way to match.

The Azure Active Directory Connect wizard, used to configure Azure AD Connect installations provides options to choose the sourceanchor attribute:

Azure AD Connect's Uniquely identifying your users page (click for original screenshot)

The above screenshot is a screenshot of a recent versions of Azure AD Connect. It provides the default option to Let Azure AD manage the source anchor and a list of available attributes to use as an alternative through the Choose a specific attribute option.

Let Azure AD manage the source anchor

When the Let Azure AD manage the source anchor option is selected, Azure AD Connect checks if there is a previous (older) Azure AD Connect installation connected to the Azure AD tenant. If there is, it will default to the objectGUID attribute . If this is the first Azure AD Connect installations, or all other Azure AD Connect installations have already been migrated to use mS-DS-ConsistencyGUID as the sourceanchor attribute for user objects, the mS-DS-ConsistencyGUID attribute is automatically selected as the sourceanchor attribute.

Choose a specific attribute

When you Choose a specific attribute it is important to choose an attribute that:

  1. Does not exceed 60 characters in length
  2. Does not contain special characters like \ ! # $ % & * + / = ?  and ^
  3. Is globally unique throughout your organization
  4. Is either a string, integer or binary

A good sourceanchor attribute is not based on a person’s name, because this may change, however some organizations still choose to do so, based on the mail attribute…

Note:
The sourceanchor attribute chosen is stored in the configuration of the Azure AD tenant.

Note:
When upgrading or changing settings, Azure AD Connect reports that the Azure AD Connect installation for this tenant still uses objectGUID to synchronize user objects.

Hard matching occurs, based on the following data:

Azure AD Connect Hard Matching Table

Here is a script I created for generating the ImmutableID from on-premises AD and Azure AD:

<#Information
 
    Author: thewatchernode
    Contact: author@blogabout.cloud
    Published: 4th October 2020

    .DESCRIPTION
    Tool to assist with application delivery

    Version Changes            
    : 0.1 Initial Script Build
    : 1.0 Initial Build Release
    : 1.1 Additional fields in AD Output

    .EXAMPLE
    .\set-immutableid.ps1

    Description
    -----------
    Runs script with default values. It 


    .INPUTS
    None. You cannot pipe objects to this script.
#>

#region Shortname

$DarkCyan = 'DarkCyan'
$DarkRed = 'DarkRed'
$Green = 'Green'
$Red = 'Red'
$Yellow = 'Yellow'
$White = 'White'

$AzureAD = 'AzureAD'
$ImportExcel = 'ImportExcel'
$Quit = 'Q'

#endregion

#region Banner
[string] $Root = @'
 
  ┌─────────────────────────────────────────────────────────────┐
            Gather ImmutableID in Bulk using PowerShell              
           
                Follow me @thewatchernode on Twitter 
                
                
   This script gathers the ImmutableID from Active Directory
   and Azure Active Directory.                   
  └─────────────────────────────────────────────────────────────┘
   
  1)  Connect to Azure AD                             -->

  2)  Get ImmutableID for AD                          -->
  3)  Get ImmutableID for AAD
  
  5)  Merge AD and AAD outputs (Coming Soon)          --> 
  6)  Set ImmutableID using Option 4 (Coming Soon)    --> 
     
  Q) Quit

  Select an option.. [1-99]?
'@
[string] $ADRoot = @'
 
  ┌─────────────────────────────────────────────────────────────┐
            Gather ImmutableID in Bulk using PowerShell              
           
                Follow me @thewatchernode on Twitter 
                
                
   This section will gather the UserPrincipleName,SamAccountName,
   DisplayName,CanonicalName, EmailAddress and ImmutableID for 
   all AD Users..                   
  └─────────────────────────────────────────────────────────────┘
'@
[string] $AADRoot = @'
 
  ┌─────────────────────────────────────────────────────────────┐
            Gather ImmutableID in Bulk using PowerShell              
           
                Follow me @thewatchernode on Twitter 
                
                
   This section will gather the UserPrincipleName,ObjectID and
   ImmutableID for all Azure AD Users.                 
  └─────────────────────────────────────────────────────────────┘
'@
[string] $ImportExcel1 = @'
 
  ┌─────────────────────────────────────────────────────────────┐
            Gather ImmutableID in Bulk using PowerShell              
           
                Follow me @thewatchernode on Twitter 
                
                
   Please Note: The worksheet in both files MUST be called 
                Sheet1                
  └─────────────────────────────────────────────────────────────┘
'@
#endregion Banner
#region Menu Prompt
function Get-Root    {
  # Menu Prompt
  
    Do {
      $MenuOption = Read-Host -Prompt $Root
      Clear-Host
      switch ($MenuOption){
        1 { # Connect to AzureAD
             Get-AADConnect
          } 
        2 { # Get ImmutableID for AD
             Get-ADImmutableID
          }
        3 { # Get ImmutableID for AAD
             Get-ADDImmutableID
          }
        4 { # Rename exports
            
          }
        5 { # Merge XLSX Sheets and convert to CSV file
            Get-ImportExcel
            Get-MergeFiles
          }
        6 { # Set ImmutableID in Azure
            Set-ImmutableID
          }

        $Quit {return} 
      }
    
    }  until ($Root -eq {$Quit})
  }
#endregion Menu Prompt
#region Functions

Function Test-IsAdmin {
  <#
      .SYNOPSIS
      Describe purpose of "Test-IsAdmin" in 1-2 sentences.

      .DESCRIPTION
      Add a more complete description of what the function does.

      .EXAMPLE
      Test-IsAdmin
      Describe what this call does

      .NOTES
      Place additional notes here.

      .LINK
      URLs to related sites
      The first link is opened by Get-Help -Online Test-IsAdmin

      .INPUTS
      List of input types that are accepted by this function.

      .OUTPUTS
      List of output types produced by this function.
  #>

  ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')

}
if (!(Test-IsAdmin)){
  throw 'Please Note: You are trying to run this script without administative priviliges. In order to run this script you will required PowerShell running in Administrator Mode'
}
else {
  Write-Verbose -Message 'Are you running as an Administator' -verbose
}
Function Get-AzureADPSVersion {
  # Azure PowerShell Version
  
  $ModuleVersion = Get-InstalledModule -Name $AzureAD -ErrorAction SilentlyContinue | Select-Object -Property name,version
  Write-Host 'Your client machine is running the following version of AzureAD Module' -ForegroundColor $White -BackgroundColor $DarkCyan
  $moduleversion
}
Function Get-AzureAD {
  
 [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls1
  $ModuleCheck = Get-InstalledModule -name $AzureAD -ErrorAction SilentlyContinue   

  if ($ModuleCheck) {
    Write-Host 'Info: Detected an installation of the AzureAD Module' -ForegroundColor $White -BackgroundColor $DarkCyan
    $Module = Get-Module -Name $AzureAD -ListAvailable
    # Identify modules with multiple versions installed
    $g = $module | Group-Object -Property name -NoElement | Where-Object count -gt 1
    # Check Module from PSGallery
    Write-Host 'Checking AzureAD module from the PSGallery' -ForegroundColor $White -BackgroundColor $DarkCyan
    $gallery = $module | Where-Object {$_.repositorysourcelocation}

    Write-Host 'Comparing installed version against online version of AzureAD module' -ForegroundColor $White -BackgroundColor $DarkCyan
    foreach ($module in $gallery) {

      #find the current version in the gallery
      Try {
        $online = Find-Module -Name $module.name -Repository PSGallery -ErrorAction Stop
      }
      Catch {
        Write-Warning -Message ('Module {0} was not found in the PSGallery' -f $module.name)
      }

      #compare versions
      if ($online.version -gt $module.version) {
        $UpdateAvailable = 'Version removed'
        Write-Host -BackgroundColor $DarkRed -ForegroundColor $White "Warning: Legacy Version of AzureAD Module detected. Starting removing process"
        Uninstall-Module -Name $AzureAD -RequiredVersion $module.version 
        Write-Host -BackgroundColor $DarkRed -ForegroundColor $White "Info: Legacy Version of AzureAD Module now removed"
        Install-Module -Name $AzureAD -RequiredVersion $online.Version -Force
      }
      else {
        $UpdateAvailable = 'No update required'
      }

      #write a custom object to the pipeline
      [pscustomobject]@{
        Name = $module.name
        MultipleVersions = ($g.name -contains $module.name)
        InstalledVersion = $module.version
        OnlineVersion = $online.version
        Update = $UpdateAvailable
        Path = $module.modulebase
      }
 
    } 
    # Microsoft Azure PowerShell Version
    Get-AzureADPSVersion
   
  }
  else
  {
    Write-Host 'Error: Failed to detect an installation of the AzureAD Module' -ForegroundColor $Red
    Write-Host 'Info: Attempting an installation of the AzureAD Module' -ForegroundColor $Green
    Install-Module -Name $AzureAD
   
    # Microsoft Azure PowerShell Check
    Get-AzureADPSVersion

  }
 }
Function Get-AADConnect {

Get-AzureADPSVersion
Get-AzureAD

Write-Host "INFO: Connecting to Azure Active Directory, prompting for relevant administrative credentials" -ForegroundColor $White -BackgroundColor $DarkCyan
Connect-AzureAD

}
Function Get-ADImmutableID {
$ADRoot
$reportoutput=@()
$users = Get-ADUser -Filter * -Properties *
$users | Foreach-Object {

    $user = $_
    $immutableid = [System.Convert]::ToBase64String($user.ObjectGUID.tobytearray())
    $userid = $user | select @{Name='Access Rights';Expression={[string]::join(', ', $immutableid)}}

    $report = New-Object -TypeName PSObject
    $report | Add-Member -MemberType NoteProperty -Name 'UserPrincipalName' -Value $user.UserPrincipalName
    $report | Add-Member -MemberType NoteProperty -Name 'SamAccountName' -Value $user.samaccountname
    $report | Add-Member -MemberType NoteProperty -Name 'DisplayName' -Value $user.displayname
    $report | Add-Member -MemberType NoteProperty -Name 'CanonicalName' -Value $user.canonicalname
    $report | Add-Member -MemberType NoteProperty -Name 'EmailAddress' -Value $user.emailaddress
    $report | Add-Member -MemberType NoteProperty -Name 'ImmutableID' -Value $immutableid
    $reportoutput += $report
}
 # Report
$reportoutput | Export-Csv -Path $env:USERPROFILE\desktop\ImmutableID4AD.csv -NoTypeInformation -Encoding UTF8
Write-Host "INFO: File exported to $env:USERPROFILE\desktop\ImmutableID4AD.csv" -ForegroundColor $Green
 }
Function Get-ADDImmutableID {
$AADRoot
$reportoutput=@()
$users = Get-AzureADUser | Select UserPrincipalName, SamAccountName, DisplayName, EmailAddress,ImmutableID
$users | Foreach-Object {

    $user = $_
    #$immutableid = "[System.Convert]::ToBase64String($user.ObjectGUID.tobytearray())"
    $userid = $user | select @{Name='Access Rights';Expression={[string]::join(', ', $immutableid)}}

    $report = New-Object -TypeName PSObject
    $report | Add-Member -MemberType NoteProperty -Name 'UserPrincipalName' -Value $user.UserPrincipalName
    $report | Add-Member -MemberType NoteProperty -Name 'SamAccountName' -Value $user.samaccountname
    $report | Add-Member -MemberType NoteProperty -Name 'DisplayName' -Value $user.displayname
    $report | Add-Member -MemberType NoteProperty -Name 'EmailAddress' -Value $user.emailaddress
    $report | Add-Member -MemberType NoteProperty -Name 'ImmutableID' -Value $user.immutableid
    $reportoutput += $report
}
 # Report
$reportoutput | Export-Csv -Path $env:USERPROFILE\desktop\ImmutableID4AAD.csv -NoTypeInformation -Encoding UTF8 
Write-Host "INFO: File exported to $env:USERPROFILE\desktop\ImmutableID4AAD.csv" -ForegroundColor $Green
}
Function Get-RenameCSVtoXLSX {

    $proj_files = get-childitem -path "$env:userprofile\Desktop\*.csv"
    ForEach ($file in $proj_files) {
    $file | Rename-Item -NewName { $_.name -Replace '\.csv$','.xls' }
    }
    Write-Host "INFO: Files named to .xls for investigation using Excel" -ForegroundColor $Green
}
Function Get-RenameXLSXtoCSV {

    $proj_files = get-childitem -path "$env:userprofile\Desktop\*.xlsx"
    ForEach ($file in $proj_files) {
    $filenew = $file.Name + ".csv"
    Rename-Item $file $filenew
    }
}
Function Get-ImportExcelPSVersion {
  # MSOL PowerShell Version
  
  $ModuleVersion = Get-InstalledModule -Name $ImportExcel | Select-Object -Property name,version
  Write-Host 'Your client machine is running the following version of ImportExcel Module' -ForegroundColor $White -BackgroundColor $DarkCyan
  $moduleversion
}
Function Get-ImportExcel {
  $ModuleCheck = Get-InstalledModule -name $ImportExcel -ErrorAction SilentlyContinue   

  if ($ModuleCheck) {
    Write-Host 'Info: Detected an installation of the ImportExcel Module' -ForegroundColor $Green
    $Module = Get-Module -Name $ImportExcel -ListAvailable
    # Identify modules with multiple versions installed
    $g = $module | Group-Object -Property name -NoElement | Where-Object count -gt 1
    # Check Module from PSGallery
    Write-Host 'Checking ImportExcel module from the PSGallery' -ForegroundColor $White -BackgroundColor $DarkCyan
    $gallery = $module | Where-Object {$_.repositorysourcelocation}

    Write-Host 'Comparing installed version against online version of ImportExcel module' -ForegroundColor $White -BackgroundColor $DarkCyan
    foreach ($module in $gallery) {

      #find the current version in the gallery
      Try {
        $online = Find-Module -Name $module.name -Repository PSGallery -ErrorAction Stop
      }
      Catch {
        Write-Warning -Message ('Module {0} was not found in the PSGallery' -f $module.name)
      }

      #compare versions
      if ($online.version -gt $module.version) {
        $UpdateAvailable = 'Version removed'
        Write-Host -BackgroundColor $DarkRed -ForegroundColor $White "Warning: Legacy Version of ImportExcel Module detected. Starting removing process"
        Uninstall-Module -Name $ImportExcel -RequiredVersion $module.version 
        Write-Host -BackgroundColor $DarkRed -ForegroundColor $White "Info: Legacy Version of ImportExcel Analyzer Module now removed"
        Install-Module -Name $ImportExcel -RequiredVersion $online.Version -Force
      }
      else {
        $UpdateAvailable = 'No update required'
      }

      #write a custom object to the pipeline
      [pscustomobject]@{
        Name = $module.name
        MultipleVersions = ($g.name -contains $module.name)
        InstalledVersion = $module.version
        OnlineVersion = $online.version
        Update = $UpdateAvailable
        Path = $module.modulebase
      }
 
    } 
    # ImportExcel
    Get-ImportExcelPSVersion
   
  }
  else
  {
    Write-Host 'Error: Failed to detect an installation of the ImportExcel Module' -ForegroundColor $Red
    Write-Host 'Info: Attempting an installation of the ImportExcel Module' -ForegroundColor $Green
    Install-Module -Name $ImportExcel
   
    # ImportExcel
    Get-ImportExcelPSVersion

  }
}
Function Get-MergeFiles {

   #$ref = Read-Host -Prompt 'Specify your Reference File for example (c:\ref.xlsx)'
   #Write-Host -Foreground $Cyan ('You have specified {0}' -f $ref)
   #$dif = Read-Host -Prompt 'Specify your Difference File for example (c:\dif.xlsx)'
   #Write-Host -Foreground $Cyan ('You have specified {0}' -f $dif)
   
   Write-Host 'Specify your 1st source reference file ' -ForegroundColor $White -BackgroundColor $DarkCyan
   $Ref = Get-Filename
   Write-Host 'Specify your 2nd source reference file ' -ForegroundColor $White -BackgroundColor $DarkCyan 
   $Dif = Get-FileName

   $out = Read-Host -Prompt 'Specify your output file for example (c:\output.xlsx)'
   Write-Host ('You have specified {0}' -f $out) -ForegroundColor $White -BackgroundColor $DarkCyan 

   Merge-Worksheet -Referencefile $ref -Differencefile $dif -OutputFile $out -WorksheetName Sheet1 -Startrow 1 -OutputSheetName Sheet1 -NoHeader
}
Function Set-ImmutableID {
 
#endregion 
 
Start-Transcript $env:USERPROFILE\desktop\PilotUser.csv
 
foreach($user in $csv1){
 
    Set-AzureADUser -ObjectID $user.ObjectId -ImmutableID $user.ImmutableID
    Write-Host $user.PrimarySMTPAddress,"with ObjectID"$user.ObjectId," has been set with ImmutableID",$user.ImmutableID
}
Stop-Transcript

Write-Host "Starting AD Sync Cycle Delta"
Start-ADSyncSyncCycle -PolicyType Delta

}
Function Get-FileName
{
    
  [CmdletBinding()]
  param
  (
    [Object]$initialDirectory
  )
Add-Type -AssemblyName System.windows.forms | Out-Null
    
    $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
    $OpenFileDialog.initialDirectory = $initialDirectory
    $OpenFileDialog.filter = 'XLS (*.xls*)| *.xls*'
    $OpenFileDialog.ShowDialog() | Out-Null
    $OpenFileDialog.filename
}

#endregion

#endregion


#region Code Launch
clear-host
#Requires -Version 5.0
Write-Output "I'm version 5.0 or above"
$PSVersionTable
Test-IsAdmin
Write-host 'Version information - You are running script version 1.2' -ForegroundColor White -BackgroundColor DarkGray
Get-Root
#endregion Code Launch

When hard matching provides a match, soft matching is not attempted. However, the non-matching rules still apply.

Soft Matching

When hard matching doesn’t provide a match, soft matching is attempted. Soft matching is little more straight-forward than hard matching as it’s based on the following data:

Azure AD Connect Soft Matching Table

Through soft matching, an on-premises Active Directory user object is matched to an Azure AD user object, when:

  1. The userPrincipalName attributes match
  2. The userPrincipalName attribute for the on-premises user object matches with the e-mail address denoted with SMTP: in the proxyAddresses attribute of the Azure AD user object
  3. The primary SMTP address (denoted with SMTP: in the proxyAddresses attribute) matches the userPrincipalName of the Azure AD user object

When soft matching provides a match, hard matching is established at the first synchronization cycle by setting the immutableID attribute for the Azure AD user object, based on the sourceanchor configuration. This is done for disaster recovery purposes: When the (only) Azure AD Connect installation fails, a replacement Azure AD Connect installation can pick up synchronization for end users by accurate hard matching.

Powershell – Changing the UPN in Office 365 for Bulk Users

It is very normal for IT admins to change people’s names, or domain names in Active Directory because they have got married or completed a deed poll, or purchase a new primary domain name. In doing this, the user’s sign-in address so should also reflect the name change. In this post, I am going to look at how to change the User Principal Name for a single user and multiple users.

Setting Up Your PowerShell Environment

First of all you will require the relevant PowerShell Module to connect to Office 365, this can be obtained by running the following cmdlet;

Install-Module MSOnline

Install-Module AzureAD

It might depend on your environment, ask you to install the NuGet provider, as well as installing a module from the PSGallery. Answer Y to both these questions if you encounter them.

Changing a UserPrincipalName for a single user

Using Connect-MSOL Module

Set-MsolUserPrincipalName -UserPrincipalName andrew.moran@contoso.onmicrosoft.com -NewUserPrincipalName andrew.moran@contoso.com

Using AzureAD Module

Set-AzureADUser -ObjectID andrew.moran@contoso.onmicrosoft.com -UserPrincipalName andrew.moran@contoso.com


Changing UserPrincipalName in bulk for users

The following script requires the following in order to be run;

– AzureAD PowerShell Module
– Source CSV file with the following DisplayName,UserPrincipalName,MailNickName

#region Variables
 
 $Filepath = Get-FileName -initialdirectory "$env:USERNAME\downloads"

 #endregion

 #region Functions
 Function Get-FileName {  
  [CmdletBinding()]
  param
  (
    [Object]$initialdirectory
  )
  Add-Type -AssemblyName System.windows.forms | Out-Null
    
    $OpenFileDialog = New-Object -TypeName System.Windows.Forms.OpenFileDialog
    $OpenFileDialog.initialDirectory = $initialdirectory
    $OpenFileDialog.filter = "All files (*.*)| *.*"
    $OpenFileDialog.ShowDialog() | Out-Null
    $OpenFileDialog.filename
}
 #endregion 

Connect-AzureAD

# Start-Time
$start = [system.datetime]::Now

# Output will be added to C:\temp folder. Open the Add-SMTP-Address.log with a text editor. For example, Notepad.
Start-Transcript -Path C:\temp\ChangingUPN.log -Append

# Get all mailboxes
$Mailboxes = Import-Csv -Path $FilePath 

# Loop through each mailbox
foreach ($Mailbox in $Mailboxes) {
	
        # Change @contoso.com to the domain that you want to add
        $Address = "$($Mailbox.MailNickName)@ipfin.co.uk"

        # Remove the -WhatIf parameter after you tested and are sure to add the secondary email addresses
        Set-AzureADUser -ObjectId $Mailbox.MailNickName -UserPrincipalName $Address

        # Write output
        Write-Host "Setting $($Address) to $($Mailbox.UserPrincipalName)" -ForegroundColor Green
    }

    # End-Time
$end = [system.datetime]::Now

$resulttime = $end - $start
Write-Host ('Execution : {0}Days:{1}Hr:{2}Min:{3}Sec' -f $resultTime.Days, $resultTime.Hours, $resultTime.Minutes, $resultTime.Seconds) 

Regards
The Author – Blogabout.Cloud

Microsoft Teams: Admins can use cloud shell from Teams admin center

Microsoft has now launched Cloud Shell within the Microsoft Teams Admin Center (13th January 2022), to enable Admins the ability to run PowerShell scripts direct from the browser.

The ability to use Cloud Shell for Microsoft Teams has been available for a while now but this small addition to the Admin Center resolves the need to have more than one window open to administer your Teams environment.

So if you are a Microsoft Teams Admin, go and check it out today.

Regards
The Author

N?? ??? ??? ???? ?????? ?????? ????????? ??????? ?? ???????? ????? ?? ??????? :



AAD Connect can synchronize the same users, groups, and contacts from a single Active Directory to multiple Azure AD tenants. These tenants can be in different Azure environments, such as the Azure China environment or the Azure Government environment, but they could also be in the same Azure environment, such as two tenants that are both in Azure Commercial.

Important points to consider :

The same Source Anchor can be used for a single object in separate tenants (but not for multiple objects in the same tenant)
You will need to deploy an AADConnect server for every Azure AD tenant you want to synchronize to – one AADConnect server cannot synchronize to more than one Azure AD tenant.
It is supported to have different sync scopes and different sync rules for different tenants.

Only one Azure AD tenant sync can be configured to write back to Active Directory for the same object. This includes device and group writeback as well as Hybrid Exchange configurations – these features can only be configured in one tenant. The only exception here is Password Writeback – see below.

It is supported to configure Password Hash Sync from Active Directory to multiple Azure AD tenants for the same user object. If Password Hash Sync is enabled for a tenant, then Password Writeback may be enabled as well, and this can be done on multiple tenants: if the password is changed on one tenant, then password writeback will update it in Active Directory, and Password Hash Sync will update the password in the other tenants.

It is not supported to use the same custom domain name in more than one Azure AD tenant, with one exception: it is supported to use a custom domain name in the Azure Commercial environment and use that same domain name in the Azure GCCH environment. Note that the custom domain name MUST exist in Commercial before it can be verified in the GCCH environment.

It is not supported to configure hybrid experiences such as Seamless SSO and Hybrid Azure AD Join on more than one tenant. Doing so would overwrite the configuration of the other tenant and would make it unusable.

You can synchronize device objects to more than one tenant but only one tenant can be configured to trust a device.
Each Azure AD Connect instance should be running on a domain-joined machine.

Visit https://docs.microsoft.com/en-us/azure/active-directory/hybrid/plan-connect-topologies for more details

Enabling Self Service Password Reset

Hello Reader,

In this post, I will show you how to configure Self Service Password Reset for your organization. This option is designed to reduce the workload on Service Desks and provide a solution whereby end-users are able to reset this account password.

Launch Azure Portal https://portal.azure.com

Go to Azure Active Directory

Select Users

Select Password Reset

For users to authenticate to reset their password you need to define the best security method for your organization. Click on Authentication methods and select whether you want 1 or 2 methods to be required to reset. Then select the options you want available as shown below

We now need to ensure that users are required to register for Password Reset –> Select Registration and select Yes. You can also get users to reconfirm their authentication information after a set amount of days.

Some additional steps you can make are;

Notifications
Notify a user when their password has been reset
Notify all admins when another admin resets their password

Customization
Provide a link to your helpdesk for the end-user if they are having difficulties.

On-premises integration
If you are using Password Writeback with Azure Active Directory Connect. You can able users to write back their password to an on-premises directory and allows users to unlock accounts without resetting their password.

Regards
The Author – Blogabout.Cloud

Deploying BGInfo using Azure Blob Storage and Microsoft Endpoint Manager

Delivering your corporate applications can be a nightmare if you dont have a enterprise delivery solution like System Center or 3rd party mechanism.

So let’s see how Azure Blob Storage and Microsoft Intune can address this issue by using a storage location and PowerShell script.

Azure Storage Account

One of the requirements for this solution is an Azure Storage Account within your Azure subscription, this account will be used for storing the applications which you would like to roll out to your Windows 10 desktops that are managed using Microsoft Intune.

Storage Account

Specify the required settings within the Basic tab for creating a Storage Account.

Basic Properties

Using the default settings as shown below

Advanced Properties

Click Review and Create
Click Create

Configuring Storage Account with required Applications

Click Container
Specify the Name
Select Conditioner (anonymous read access for containers and blobs) under Public Access Level

Blob – Container

Select your container
Select Upload
Select the files you want to upload
Modify the block size if it’s less than the size of the files you are uploading
Select Upload

Once the files are upload they all have a unique url which is used to identify the file as shown below. This will be required later for the PowerShell script.

The PowerShell Script!!!

This script has been made available on GitHub, you will need to modify the following;

$bginfo64 and $layout to reference your Azure Blob Storage for each file

Download Script

https://github.com/TheWatcherNode/blogaboutcloud/blob/master/Get-BGInfo.ps1

Publish script via Microsoft Endpoint Manager

Launch Microsoft Endpoint Manager https://endpoint.microsoft.com

Browse to Devices –> Scripts –> Click Add –> Select Windows 10

Provide a name and description (optional).. Press Next

Provide your script and select Run script in 64 bit PowerShell Host. Press Next

Press next on Scope Tag, unless you utilize them within your environment

Select the group(s) you wish to target.. Press Next

Press Add to complete

Once the script has applied to the required workstations, at the next reboot the BGInfo will be presented on the desktop wallpaper

Regards
The Author – Blogabout.Cloud

Invoke-WebRequest: The response content cannot be parsed when adapting a local powershell script for Azure Automation.

I have been recently adapting a PowerShell script that collects the Microsoft 365 Service Health using the Office 365 Service Communication API publishing the information into ServiceNow to log a support ticket to make the helpdesk aware of a potential issue in the service. The script was working is no issues when run manually out of PowerShell ISE but failed when ran by Azure Automation.

The focus of this post is to show two specific steps for adapting a locally executed PowerShell script for an Azure Automation runbook. In addition to standard work in Automation to add credentials/connections and create parameters where needed, the following changes were required to adapt a local script for my runbook.

1) Add -UseBasicParsing to Web Requests

The Management API script is filled with various requests using Invoke-WebRequest. Initially, I received numerous errors stating that the Internet Explorer engine is not available.

Invoke-WebRequest : The response content cannot be parsed because the Internet Explorer engine is not available, or 
Internet Explorer's first-launch configuration is not complete. Specify the UseBasicParsing parameter and try again. 
At line:46 char:9
+ $subs = Invoke-WebRequest -Headers $headerParams -Uri "https://manage ...
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotImplemented: (:) [Invoke-WebRequest], NotSupportedException
    + FullyQualifiedErrorId : WebCmdletIEDomNotSupportedException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

To overcome this, I needed to add the parameter UseBasicParsing.

Original: $oauth = Invoke-RestMethod -Method Post -Uri $loginURL/$tenantdomain/oauth2/token?api-version=1.0 -Body $body
Adapted: $oauth = Invoke-RestMethod -Method Post -Uri $loginURL/$tenantdomain/oauth2/token?api-version=1.0 -Body $body –UseBasicParsing

2) Alter $ProgressPreference

PowerShell shows progress using $ProgressPreference, which defaults to “continue”. For Automation, I needed to change the option to “silently continue” and suppress progress bars. Locally, the progress can be convenient to see, but it was causing issues with

Automation.Adapted: $ProgressPreference = “silentlyContinue”

Regards
The Author – Blogabout.Cloud

Publishing your Microsoft 365 Service Messages to a SharePoint List.

I have been recently working on a solution for a customer whereby they required Microsoft 365 Service Messages to be logged as an incident using a Service Desk tool and publish to Microsoft Teams Channel.

Microsoft Teams – This was the simple bit as a nice person in the community built a PowerShell script available on GitHub.

Now for the publishing the information to SharePoint Online, this has caused quite a few headaches from banging my head on my desk but it now working. The reason for publishing to SharePoint List is so that it can be used / reference as a data source to pulling the information into ServiceNow.

You can bit with Power Automate however the information required and provided is lacking so the route I am going to demostrate provide a lot more details. Just like the Microsoft Teams post

I have been recently working on a solution for a customer whereby they required Microsoft 365 Service Messages to be logged as an incident using a Service Desk tool and publish to Microsoft Teams Channel.

Microsoft Teams – This was the simple bit as a nice person in the community built a PowerShell script available on GitHub.

https://github.com/einast/PS_M365_scripts/blob/master/AzureAutomation/AzO365ServiceHealth.ps1

Now for publishing the information to SharePoint Online, this has caused quite a few headaches from banging my head on my desk but it now works. The reason for publishing to SharePoint List is so that it can be used / reference as a data source to pulling the information into ServiceNow.

You can bit with Power Automate however the information required and provided is lacking so the route I am going to demonstrate provide a lot more details. Just like the Microsoft Teams post

Create the SharePoint Site

The first thing to do is create a SharePoint Site that can be used as the location for the data. This can be simply done by connecting to SharePoint via PowerShell and running the following command.

You will need the following PowerShell Module
https://www.powershellgallery.com/packages/PnP.PowerShell

$URL =  "blogabout.sharepoint.com"
$Admin = "thewatcher@blogabout.cloud"

$cred = (Get-Credential)

Connect-AzureAD -Credential $cred

Connect-PnPOnline -UseWebLogin -Url $url
New-PnPTenantSite -Url $URL -Title "Health Status" -Owner "$Admin" -TimeZone "2"     

We now need to gather some information which will be used later in this blog. Using the same PowerShell console run the following cmdlet

Get-PnPList

Make a note of the Id for Health Status as we will need this later on.

We now need to configure the SharePoint List with required columns for receiving the data.

The column used are;

– Title
– Status
– Severity
– Service
– Classification
– Uri
– Message
– Reference

Modifying your Azure Automation Account for SharePoint Lists

In this post, I will make an assumption that you have already implemented pushing messages to Microsoft Teams. If you havent, please visit this article.

First of you all, we need to add PnP.PowerShell module to this Azure Automation account. Go to Modules under Shared Resources and Browse Gallery.

Search for PnP.PowerShell and click on the module

Click Import

The import may take a few minutes to complete and once complete we can go on to the next step.

Select Credentials from Shared Resources

Important Notice

Some of the next steps I have already completed in my environment.

Click “Add a credential” and provide;

-Name – This is important for the PowerShell script to call the credentials
– Username
– Password

You can also provide a description if required.

It is time to now modify the Runbook with the required PowerShell cmdlets for publishing to SharePoint Lists.

Select your run book to edit the script

Under User-Defined Variables I have added the following;

Please note: I am not using Automated Variables in this script but they could be used if you add the variable and use Get-AutomatedVariable -Name

$SpListName = "Health-Status"
$ListName = "Add the Health Status ID you gathered earlier"
$SpSiteUrl = "https://blogabout.sharepoint.com/sites/$SpListName"

https://pnp.github.io/powershell/articles/authentication.html

$myCred = Get-AutomationPSCredential -Name 'MyCredential'
$userName = $myCred.UserName
$securePassword = $myCred.Password
$password = $myCred.GetNetworkCredential().Password

$SpCreds = New-Object System.Management.Automation.PSCredential ($userName,$securePassword)

As you can see from above I am using Get-AutomationPSCredential -Name MyCredential to call stored credentials.

Under the following PowerShell cmdlet in the script

# If any new posts, add to Teams
Invoke-RestMethod -uri $uri -Method Post -body $Payload -ContentType 'application/json; charset=utf-8'

I have added;

# Add news post to Sharepoint Site
Connect-PnPOnline $SpSiteUrl -Credentials $SpCreds
$list =  Get-PnPList -identity $ListName

Add-PnPListItem -List $list -Values @{"Title"=$inc.Title;"Reference"=$inc.Id;"Status"=$inc.Status;"Severity"=$inc.Severity;
                "Service"=$inc.WorkloadDisplayName;"Classification"=$inc.Classification;"Uri"=$inc.PostIncidentDocumentUrl;"Message"=$Message}

This will now allow you to publish the data to SharePoint as shown below,

I am using Power Automate to trigger the Runbook for more detail visit the below post.

Hopefully this post will help you publish the data to SharePoint.

Regards
The Author – Blogabout.Cloud

Pushing Microsoft Health Status to Microsoft Teams using Azure Automation.

In this post I am going to demostrate how to push Microsoft Health Status to Microsoft Teams using Azure Automation

In this post it is expected you have configured Office 365 Service Communications API. If not, visit this blog post https://www.blogabout.cloud/2020/10/1884/

Configuring Microsoft Teams for Incoming Webhook

Launch Microsoft Teams right click a channel within a Team and select Connectors. Search for Incoming Webhook and click Add

Click Add

Specify the name of the Webhook and click Create

Make a note of the URL below as you will need this for the PowerShell script later and click Done

Create Automation Account

Launch your Azure Portal and search for Automation Account to create a new account.

Specify;
– Name
– Subscription
– Resource Group
– Location

No should be selected for “Create Azure Run As Account” as this is will enable the Account to run against Office 365.
Click Create

Once the resources has successful created, go into the resource

First of all we need to create a number of variables, these variables will use within the PowerShell script we are going to add to the Runbook.

You will need to add the following variables

– AzO365ServiceHealthApplicationID = Client ID
– AzO365ServiceHealthApplicationKey = Client Secrey
– AzO365TeamsURI = This is the url we created earlier in the post
– AzO365TenantDomain = Tenant ID

If you unsure how to obtain these values, please visit this blog post for more details

We now need to launch Runbooks

Select Create a runbook
– Provide a name
– Runbook Type should equal PowerShell
– Description (is optional)

Press Create

I am now going to use the following PowerShell script available from GitHub within my Runbook. https://github.com/einast/PS_M365_scripts. This script has been created for the sole purpose of extracting the Microsoft 365 Service Health using the Office Service Communication API, which I demonstrated how to configure in another post.

Under the “User Defined Variables” you will need to edit;

Mintues to reflect the same time as your schedule.

Pushover notifications and Services to monitor as shown below

# ------------------------------------- USER DEFINED VARIABLES -------------------------------------

$ApplicationID = Get-AutomationVariable -Name 'AzO365ServiceHealthApplicationID'
$ApplicationKey = Get-AutomationVariable -Name 'AzO365ServiceHealthApplicationKey'
$TenantDomain = Get-AutomationVariable -Name 'AzO365TenantDomain'
$URI = Get-AutomationVariable -Name 'AzO365TeamsURI'
$Minutes = '1440'

# Pushover notifications in case Teams is down.
# Due to limitations and readability, the script will only send the title of the incident/advisory to Pushover. 
# COMMENT OUT THIS SECTION IF YOU DON'T WANT TO USE PUSHOVER!

#$Pushover = 'yes' # Comment out if you don't want to use Pushover. I use 'yes' for readability.
#$PushoverToken = Get-AutomationVariable -Name 'AzO365PushoverToken' # Your API token. Comment out if you don't want to use Pushover
#$PushoverUser = Get-AutomationVariable -Name 'AzO365PushoverUser' # User/Group token. Comment out if you don't want to use Pushover
#$PushoverURI = 'https://api.pushover.net/1/messages.json' # DO NOT CHANGE! Default Pushover URI. Comment out if you don't want to use Pushover

# Service(s) to monitor
# Leave the one(s) you DON'T want to check empty (with '' ), add a value in the ones you WANT to check (I added 'yes' for readability)

$ExchangeOnline = 'yes'
$MicrosoftForms = 'yes'
$MicrosoftIntune = 'yes'
$MicrosoftKaizala = 'yes'
$SkypeforBusiness = 'yes'
$MicrosoftDefenderATP = 'yes'
$MicrosoftFlow = 'yes'
$FlowinMicrosoft365 = 'yes'
$MicrosoftTeams = 'yes'
$MobileDeviceManagementforOffice365 = 'yes'
$OfficeClientApplications = 'yes'
$Officefortheweb = 'yes'
$OneDriveforBusiness = 'yes'
$IdentityService = 'yes'
$Office365Portal = 'yes'
$OfficeSubscription = 'yes'
$Planner = 'yes'
$PowerApps = 'yes'
$PowerAppsinMicrosoft365 = 'yes'
$PowerBI = 'yes'
$AzureInformationProtection = 'yes'
$SharePointOnline = 'yes'
$MicrosoftStaffHub = 'yes'
$YammerEnterprise = 'yes'

# Classification(s) to monitor
# Leave the one(s) you DON'T want to check empty (with '' ), add a value in the ones you WANT to check (I added 'yes' for readability)

$Incident = 'yes'
$Advisory = 'yes'

# ------------------------------------- END OF USER DEFINED VARIABLES -------------------------------------

You will now need to define a schedule within the Runbook for it to execute.

Once you have done all of the above, you will receive a nice notification in Microsoft Teams about the Service Health Status

In my next article I will show how simple it is too trigger this Runbook using Power Automate.

Regards
The Author – Blogabout.Cloud

Sign up for the new Microsoft Security Public Webinars

Microsoft have just released the following dates for all their new Security webinars. Go and check them out.

Below please find the full list. Details and registration are at https://aka.ms/SecurityWebinars.

JAN 27 – Azure Purview | Introduction to Azure Purview

FEB 3 – Azure Confidential Computing | Confidential computing nodes on Azure Kubernetes Service

FEB 4 – Azure Sentinel | Accelerate Your Azure Sentinel Deployment with the All-in-One Accelerator

FEB 16 – Microsoft Security Public Community | The Billion-Dollar Central Bank Heist

FEB 18 – Azure Sentinel | Best practices for converting detection rules

Interested in some bite-sized security tips? Check out our short videos at https://aka.ms/SecurityCommunityVideos.

To stay informed about future webinars and other events, join our Security Community at https://aka.ms/SecurityCommunity.

Regards
The Author – Blogabout.Cloud