Outlook’s Autodiscover Redirect Limit – Hybrid Autodiscover Breaking for Exchange Online Mailboxes / Remote On-Premises Mailboxes

Scenario:  After introducing additional Exchange On-Premises servers, we noticed that Autodiscover stopped working for our Exchange Online Hybrid mailboxes that were on computers internal to the domain. Using the ‘Test E-mail AutoConfiguration’ feature in Outlook, it would fail after the SCP/DNS autodiscover lookup.

Reason:  Outlook has an Autodiscover Redirect limit of 8-10 responses.  Being we had more than 10 servers in our Exchange Infrastructure, once it hit the response limit from each SCP lookup for each server, it would then fail all redirects thereafter. You do not need 100 SCP Failures if its going to fail after the first 10 servers.

Solution:  You can reduce the number of SCP lookup’s by performing any of the following.

  1. Set the AutoDiscoverSiteScope value on each Exchange server so it only serves requests for each  AD Site via the following command:  Set-ClientAccessServer ExSrv1 -autodiscoversitescope NewYork   (Or a combination NewYork,Baltimore,Tampa)
  2. Set the AutoDiscoverSiteScope to $null so it does not participate in SCP lookups via the following command: Set-ClientAccessServer ExSrv1 -AutodiscoverSiteScope $null
  3. Set the AutoDiscoverServiceInternalURI to $null so it does not participate in SCP lookups via the following command: Set-ClientAccessServer ExSrv1 -AutodiscoverServiceInternalURI $null

 

Determine the effective management roles assigned to an Exchange Administrator

Scenario: You want to view/verify the management roles assigned to Exchange Administrators.

Scriptlet:

To view a list of management roles for every Exchange Administrator, run:

Get-ManagementRoleAssignment -GetEffectiveUsers

If you are looking for a specific user, run:

Get-ManagementRoleAssignment -GetEffectiveUsers | Where { $_.EffectiveUserName -like “steveadm1” }

Send on Behalf not working Externally

Scenario:  When ‘Sending on Behalf’ to an internal recipient, the sender displays correctly/as expected:

Steven on behalf of TestService123

BUT, when “Sending on Behalf” to an external recipient, the result of the sender line displaying correctly is unpredictable.  It may only show the email address of the sender you sent on behalf of and may not include your delegated email address.

TestService123 (TestService123@domain.com)

Reason:  The “Send on Behalf” feature is an Exchange feature.  Unless your external mail system knows how to process the headers ‘From’ and ‘Sender’ lines, it may only show it coming from the ‘From’ address, the email address you sent on behalf from and not include your delegated email address.

Integrating Exchange 2016 Outlook on the Web with Skype/Lync

Scenario:  You want to Integrate Exchange 2016 Outlook on the Web with Skype/Lync.  Run the following:

Script:

Gather the following Variables:

#Variables
$Name = "Skype IM Override"  #Must be Unique
$Pool = "skype.domain.com" #Skype/Lync name
$Cert = "575DEE6AB4F7842A0032FEC45CE5021769A997DE" #Thumbprint of cert with IIS settings assigned to it 
$Override = "Configure IM"

Run the following to configure the integration and check your work:

#Add the Setting Override
New-SettingOverride -Name $Name  -Component OwaServer -Section IMSettings -Parameters @("IMServerName=$Pool","IMCertificateThumbprint=$cert") -Reason $override


#Refresh the IM Settings on the Exchange server 
Get-ExchangeDiagnosticInfo -Server esgmtwex16-1 -Process Microsoft.Exchange.Directory.TopologyService -Component VariantConfiguration -Argument Refresh

#Make sure OWAVirtualDirectory is set properly
get-clientaccessserve Exch* | Get-owavirtualdirectory | Set-owavirtualdirectory -instantmessagingtype OCS


#View the Diagnostic Info to confirm the correct settings
[xml]$diag=Get-ExchangeDiagnosticInfo -Server <ServerName> -Process MSExchangeMailboxAssistants -Component VariantConfiguration -Argument "Config,Component=OwaServer"; $diag.Diagnostics.Components.VariantConfiguration.Configuration.OwaServer.IMSettings

#Restart the WebAppPool Restart-WebAppPool MSExchangeOWAAppPool 

 

 

 

PowerShell Script: Combine the Alias and Mailbox Size into a variable and export to a file.

Scenario:  You want a script that will combine the Alias from the get-mailbox command and the Mailbox Size (TotalItemSize and TotalDeletedItemSize) from the get-mailboxstatistics command into a single array variable.  You want to export the Results to a .csv file as well.

Script:

$db = get-mailboxdatabase 
$final = @()
$file = "C:tempAlias_Size.csv"

$db | Select -expandproperty Name | Sort | %{
    Write-Host "DBName:  $_ " -ForegroundColor Cyan
    $mbx = Get-mailbox -database $_ -resultsize unlimited
    $mbx | Select -expandpropert alias | Sort | %{
        Write-Host ".......... Pulling Stats for $_" -ForegroundColor Yellow
        $alias = $_
        $size = Get-mailboxstatistics $alias | Select TotalItemSize,TotalDeletedItemSize
        $TIS = $size.TotalItemSize
        $TDIS  = $size.TotalDeletedItemSize
        $ServerObj = New-Object PSObject
        $ServerObj | Add-member NoteProperty -Name "Alias" -Value $alias
        $ServerObj | Add-Member NoteProperty -name "TIS" -Value $TIS
        $ServerObj | Add-Member NoteProperty -name "TDIS" -Value $TDIS
        $final += $ServerObj
    }
}
$final | Export-csv $file -append

 

Scheduled Task doesn’t allow powershell script to convert/save XLSX file to a CSV file

Scenario:  You have a PowerShell script running that will convert an .XLSX document into a .CSV Document.  When you run it natively in PowerShell, the script works fine and the document converts.  When you attempt to run it in Task Scheduler, it doesn’t convert and  create the new document.

Solution:  Create a Desktop folder in the following two directories and run the scheduled task again.:

  1. C:WindowsSysWOW64configsystemprofile
    C:WindowsSystem32configsystemprofile

EWS Script: Recover Email Items out of the Purges and Deletions based on a timeframe for when the email items were deleted.

Scenario:  You want to recover email items that were deleted from the mailbox and were moved into the backend Purges and Deletions Recoverable folders. You want to recover only items that were deleted  between a timeframe. You also want to place these deleted items into a single folder available in the mailbox.

Scriptlets:

Declare your Variables:

#Variables
    $cred = Get-credential  #credentials will fullaccess to access the mailbox
    $mailboxname = "Jane@Domain.Com"  #The Mailbox you wish to perform the query and restore on
    $EWS_DLL = "C:Program FilesMicrosoftExchange ServerV15BinMicrosoft.Exchange.WebServices.dll"
    $EWS_URL = "https://mail.domain.com/ews/exchange.asmx"
    [datetime]$StartDate  = "6/5/2017" #Used for the LastModifiedTime
    [datetime]$EndDate = "6/19/2017" #Used for the LastModifiedTime
    $RestoreFolder = "Recovered Items"  #The folder thats is already created in the mailbox to restore to

Configure the EWS connection properties

#Configure connection to EWS
    Import-Module -Name $EWS_DLL
    $service = new-object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.Exchangeversion]::exchange2013)
    $service.Url = new-object System.Uri($EWS_URL)
    $service.UseDefaultCredentials = $false
    $service.Credentials = $cred.GetNetworkCredential()

Attach to the following folders:  Purges, Deletions, and Recoverable Items (created in mailbox to restore content to)

#Attach to Purges
    $folderidpurges = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::RecoverableItemspurges,$MailboxName)
    $purgesFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderidpurges)
#Attach to Deletions
    $folderidDeletions = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::RecoverableItemsDeletions,$MailboxName)
    $DeletionsFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderidDeletions)
#Attach to Recovered Items Folder
    $PathToSearch = $restorefolder  
    $folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$mailboxname)   
    $tfTargetFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)  
    $fldArray = $PathToSearch.Split("") 
    for ($lint = 1; $lint -lt $fldArray.Length; $lint++) { 
        $fldArray[$lint] 
        #Perform search based on the displayname of each folder level 
        $fvFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1) 
        $SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,$fldArray[$lint]) 
        $findFolderResults = $service.FindFolders($tfTargetFolder.Id,$SfSearchFilter,$fvFolderView) 
        if ($findFolderResults.TotalCount -gt 0){ 
            foreach($folder in $findFolderResults.Folders){ 
                $tfTargetFolder = $folder                
            } 
        } 
        else{ 
            "Error Folder Not Found"  
            $tfTargetFolder = $null  
            break  
        }     

Create your Search Filter and perform the search

#Create and Apply an Search Filter
    $sfCollection = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::And);
    $Sfgt = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsGreaterThan([Microsoft.Exchange.WebServices.Data.ItemSchema]::LastModifiedTime, $StartDate)
    $Sflt = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsLessThan([Microsoft.Exchange.WebServices.Data.ItemSchema]::LastModifiedTime, $EndDate)
    $sfCollection.add($Sfgt)
    $sfCollection.add($Sflt)
    $view = new-object Microsoft.Exchange.WebServices.Data.ItemView(2000000)
    $miMailItems = $DeletionsFolder.FindItems($sfCollection,$view)

Lastly,  Move your email Items into the Recovery folder

    ###Moves your Email Items
    $MiMailItems | %{
        "Moving: "+ $_.LastModifiedTime +": " + $_.Subject.ToString()
        [VOID]$_.Move($tftargetfolder.id)
    }

 

 

 

 

 

 

 

 

 

Powershell: List all mailbox folder delegate access for a mailbox

Scenario: You want to determine all mailbox folder delegate access for a mailbox. You wish to not include the anonymous and default permissions.

Script:

$mbx = "steve"
$permissions = @()
$Folders = Get-MailboxFolderStatistics $mbx | % {$_.folderpath} | % {$_.replace(“/”,””)}
$list = ForEach ($F in $Folders)
   {
    $FolderKey = $mbx + ":" + $F
    $Permissions += Get-MailboxFolderPermission -identity $FolderKey -ErrorAction SilentlyContinue | Where-Object {$_.User -notlike “Default” -and $_.User -notlike “Anonymous” -and $_.AccessRights -notlike “None”}
   }
$permissions

Remove a user/group permission on an AD Object via PowerShell

Scenario: You want to remove a users permission to an AD Object via PowerShell. This is the equivalent of opening Active Directory Users and Computers, finding your AD object (user, computer, ect), and removing the users permission from the Security Tab.

Scriptlets:  We are going to remove the user   domainjdoe from the AD Computer  test_computer.

 

#Set these variables
$DistinguishedName = "CN=test_computer,OU=Test,OU=Domain,DC=com"
$user = "domainjdoe"

#Collect the current ACL
$Acl = Get-Acl $DistinguishedName 

#Loop each access permission in the ACL
foreach ($access in $acl.Access) {
        if ($access.IdentityReference.Value -eq $user) {
        $acl.RemoveAccessRule($access)
        } 
}

#Set the ACL Back to the AD Object
set-acl $DistinguishedName  -AclObject $acl

 

Lets say you wanted to do this for every ADObject in a specific OU, run the following

#Set these Variables
$strOU = "CN=test,DC=Domain,DC=Com"
$Obj = get-adobject -searchbase $strOU -properties DistinguishedName,DisplayName -Filter * | Select DisplayName,DistinguishedName
$Obj = $obj | Sort DisplayName
$user = "domainjdoe"
$counter = 0

#Set AD as the location to find the user objects.
Set-Location ad:

#Loop it
$Obj | Select -first 5 | %{
#Increase counter
$Counter++
#Display Output
"
$counter / $($obj.count): Removing $User from $_.DisplayName - $_.DistinguishedName
-----------------------------------------------------------------------
"
#Get the current ACL for the AD Object
$DN = $_.DistinguishedName
$Acl = Get-Acl $DN 

#Loop each Access Level in the ACL And Remove for the User
foreach ($access in $acl.Access) {
       if ($access.IdentityReference.Value -eq $user) {$acl.RemoveAccessRule($access)} }

#Setting the Modified ACL back to the AD Object
set-acl $DN  -AclObject $acl

#reset variables
$ACL = $Null
$DN = $null
}

 

 

Script: Set NTFS Permissions on a Folder via PowerShell

Scenario: You want to set NTFS permissions on a folder via Powershell. You want the permissions to inherit down to SubFolders and files as well.

Scriptlet:

$folder = "\FileSvr1filestest12345"
$user = "DomainJdoe"

# Get the ACL for an existing folder
$existingAcl = Get-Acl -Path $folder

# Set the permissions that you want to apply to the folder
$permissions = $user, 'Read,Modify', 'ContainerInherit,ObjectInherit', 'None', 'Allow'

# Create a new FileSystemAccessRule object
$rule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList $permissions

# Modify the existing ACL to include the new rule
$existingAcl.SetAccessRule($rule)

# Apply the modified access rule to the folder
$existingAcl | Set-Acl -Path $folder