Showing posts with label Exchange 2016. Show all posts
Showing posts with label Exchange 2016. Show all posts

25 May 2023

Simply list all Shared Mailboxes from On-premises and in Exchange Online with PowerShell

Hey there, fellow tech enthusiasts!
Today, I have some lines to drop about how to simply list all your shared mailboxes.
In this case I wanted to get all email addresses.

Step 1: Connect to your environment

Connect to you on-premises Exchange environment.

Step 2: Retrieve On-Premises Shared Mailboxes

Let's start by fetching all the remaining shared mailboxes from your on-premises environment. Open up your PowerShell console and execute the following:

$OnPremSharedMailboxes = Get-Mailbox -RecipientTypeDetails SharedMailbox -ResultSize unlimited | Select-Object PrimarySmtpAddress

Step 3: Retrieve the remote shared mailboxes

Run the following commands

Get all mailboxes
  $RemoteMailboxes = Get-RemoteMailbox -ResultSize unlimited

Get all remotesharedmailboxes and filter only the smtpaddress
  $RemoteMailboxes | Where-Object { $_.RecipientTypeDetails -eq "RemoteSharedMailbox" } | select PrimarySmtpAddress

Step 5: Consolidate the Results

It's time to bring everything together and create a unified list of shared mailboxes. Run the following commands to consolidate the on-premises and Exchange Online results:

  $AllSharedMailboxes = $OnPremSharedMailboxes + $ExchangeOnlineSharedMailboxes

With this command, you'll combine the arrays of on-premises and Exchange Online shared mailbox addresses into a single array called '$AllSharedMailboxes'. You can make other choices in your filter to generate a different list.

Step 6: Display the Results

  $AllSharedMailboxes

And just like that, your PowerShell console will present you with a complete list of shared mailbox addresses, combining the best of both on-premises en Exchange Online.

27 May 2020

How to create a shared mailbox from hybrid Exchange directly in Exchange Online but also visible in on-premises Active directory

How to create a shared mailbox from hybrid Exchange directly in Exchange Online but also visible in on-premises Active directory
Now that's a mouthful, but a question that's asked often.
And that's not strange at all, because after moving most of your mailboxes to Exchange Online, the inevitable new shared mailbox request will pop up.

Only to find out that if created in the old way you need to manually move it Exchange Online.

And if you create shared mailbox in the ECP you will soon find that you problably made the wrong choice and the new mailbox is not visible in your on-premises Active Directory.

There's where this script comes to the recue.
It creates a remote shared mailbox, a distribution group for full access and send-as rights, adds that group to the mailbox, hides the distribution group because we only use it for access rights.
Then login to Exchange Online, disable POP, IMAP, Activesync and OWA, and resets the proxy settings if neccesary.

This can also be used to create usermailboxes, just tweak it to your needs.
It's also on my Github: https://github.com/brenkster/New-RemoteSharedMailbox

param ($Alias,$DisplayName)

#	Show countdown timer
           
Function Start-Countdown
{
    Param(
        [Int32]$Seconds = 600,
        [string]$Message = "Waiting for 10 minutes"
    )
    ForEach ($Count in (1..$Seconds))
    {   Write-Progress -Id 1 -Activity $Message -Status "Waiting for $Seconds seconds, `
$($Seconds - $Count) left" -PercentComplete (($Count / $Seconds) * 100)
        Start-Sleep -Seconds 1
    }
    Write-Progress -Id 1 -Activity $Message -Status "Completed" -PercentComplete 100 -Completed
}



#	Load Exchange Powershell module
#add-pssnapin Microsoft.Exchange.Management.PowerShell.E2010
add-pssnapin Microsoft.Exchange.Management.PowerShell.SnapIn
#	Load Active Directory Powershell module
import-module activedirectory
            
# Setup variables
$DomainController="servername.domain.lan"
$OU='domain.lan/Groups/Mail/Shared Mailboxes'
$OU2='domain.lan/Groups/Mail/Shared Mailbox Groups'
$UPNdomain = "@domain.nl"
$UPNRemoteDomain = "@domain.onmicrosoft.com"



if ($Alias)
{
    if ($Alias.Contains('@')) { $Alias = $Alias.Substring(0,$Alias.IndexOf('@')) }
    $AliasMailbox = Get-Mailbox $Alias -ErrorAction SilentlyContinue
    $AliasMailUser = Get-MailUser $Alias -ErrorAction SilentlyContinue
    if ($AliasMailbox -or $AliasMailUser)
    {
        Write-Host "The Alias specified already exists" -ForegroundColor red
        $Alias = $null
    }
}
while (!$Alias)
{
    $Alias = Read-Host -Prompt "Alias (max 20 caracters)"
    if ($Alias)
    {
        if ($Alias.Contains('@')) { $Alias = $Alias.Substring(0,$Alias.IndexOf('@')) }
        $AliasMailbox = Get-Mailbox $Alias -ErrorAction SilentlyContinue
        $AliasMailUser = Get-MailUser $Alias -ErrorAction SilentlyContinue
        if ($AliasMailbox -or $AliasMailUser)
        {
            Write-Host "The Alias specified already exists" -ForegroundColor red
            $Alias = $null
        }
    }
}

if ($DisplayName)
{
    $DisplayNameMailbox = Get-Mailbox $DisplayName -ErrorAction SilentlyContinue
    $DisplayNameMailUser = Get-MailUser $DisplayName -ErrorAction SilentlyContinue
    if ($DisplayNameMailbox -or $DisplayNameMailUser)
    {
        Write-Host "The Display Name specified already exists" -ForegroundColor red
        $DisplayName = $null
    }
}
while (!$DisplayName)
{
    $DisplayName = Read-Host -Prompt "Display Name (As many caracters as you like)"
    if ($DisplayName)
    {
        $DisplayNameMailbox = Get-Mailbox $DisplayName -ErrorAction SilentlyContinue
        $DisplayNameMailUser = Get-MailUser $DisplayName -ErrorAction SilentlyContinue
        if ($DisplayNameMailbox -or $DisplayNameMailUser)
        {
            Write-Host "The Display Name specified already exists" -ForegroundColor red
            $DisplayName = $null
        }
    }
}

# Setup more variables
$Alias=$Alias.ToLower()
$UPN=$Alias + $UPNDomain
$UPNRemoteRoutingAddress = $Alias + $UPNRemoteDomain

Sleep 10
# Create the SharedMailbox
Write-Host "Creating Shared Mailbox" -ForegroundColor green            
New-RemoteMailbox -RemoteRoutingAddress "$UPNRemoteRoutingAddress" -Shared -UserPrincipalName "$UPN" `
-OnPremisesOrganizationalUnit $OU -Alias $alias -Name $alias -DisplayName $displayname `
-PrimarySmtpAddress $UPN -SamAccountName $alias -DomainController $domaincontroller            
Write-Host "Created Shared Mailbox" -ForegroundColor green            
            
Sleep 10            
# Set the description for the SharedMailbox            
Write-Host "Set Description" -ForegroundColor green            
Set-ADUser $Alias -Description "Shared Mailbox t.b.v. $Displayname"            
Write-Host "Description set" -ForegroundColor green            
            
Sleep 10            
# Create the distributiongroup for security use            
Write-Host "Creating Office365 Distributiongroup" -ForegroundColor green            
New-DistributionGroup  -DisplayName "SM.$alias" -Type Security -Alias "SM.$alias" -Name "SM.$alias" -Organizationalunit $OU2            
Write-Host "Office365 Distributiongroup created" -ForegroundColor green            
            
Sleep 10            
# Hide distributiongroup            
Write-Host "Set Office365 distributiongroup hidden" -ForegroundColor green            
Set-DistributionGroup -Identity "SM.$alias" -HiddenFromAddressListsEnabled:$true            
Write-Host "Office365 distributiongroup set to hidden" -ForegroundColor green            
            
Sleep 30            
# Sync AADConnect and wait for the account to show up online            
Write-Host "Starting Adsynccycle now" -ForegroundColor red            
Invoke-Command -ComputerName servername.domain.lan -Port 5986 -UseSSL -ScriptBlock { Start-ADSyncSyncCycle -PolicyType Delta }            
Write-Host "Adsynccycle has run" -ForegroundColor green            
            
Write-Host "Waiting for AzureAD sync" -ForegroundColor green            
#Start-Countdown -Seconds 600 -Message "Waiting for 10 minutes"            
            
$Time = 600            
$i = 0            
Do {            
    $i++            
    Write-Progress -Activity 'Waiting for 10 minutes' -Status 'Status' -PercentComplete (($i/$Time)*100) -SecondsRemaining ($Time-$i)            
    Start-Sleep 1            
} Until ($i -eq $Time)            
            
# Set the PowerShell session to use the proxy            
netsh winhttp set proxy proxy.domain.lan:8080            
Write-Host "Proxy Set" -ForegroundColor green            
            
# Connect to ExchangeOnline PowerShell            
Connect-ExchangeOnline -ShowProgress $true            
Write-Host "Connected to ExchangeOnline" -ForegroundColor green            
            
# Disable Mailbox features            
Write-Host "Disabeling OWA, POP, IMAP, ActiveSync" -ForegroundColor green            
Set-CASMailbox -Identity $Alias -imapenabled $false -owaenabled $false `
-OWAforDevicesEnabled $false -popEnabled $false -ActiveSyncEnabled $false -PopUseProtocolDefaults $false -ImapUseProtocolDefaults $false            
Write-Host "OWA, POP, IMAP, ActiveSync disabled" -ForegroundColor green            
            
Sleep 10            
# Add the distributiongroup to the sharedmailbox with Full Access            
Write-Host "Setting Mailbox Full Access Permissions" -ForegroundColor green            
Add-MailboxPermission –Identity: $Alias –AccessRights:FullAccess –user:"SM.$Alias"
Write-Host "Full Access Permissions set" -ForegroundColor green

Sleep 10

# Add the distributiongroup to the sharedmailbox with Send-as
Write-Host "Setting Mailbox Send-as Permissions" -ForegroundColor green
Add-ADPermission –Identity "$Alias" –user "SM.$Alias" –ExtendedRights 'Send-as' -DomainController $DomainController            
Write-Host "Send-as Permissions set" -ForegroundColor green            
            
Sleep 10            
            
# Reset proxy to direct access            
netsh winhttp reset proxy            
Write-Host "Proxy Set to default" -ForegroundColor green            
Write-Host "Script Finished" -ForegroundColor green            
Write-Host "Close this window " -ForegroundColor Red            

15 October 2019

How to set default retention policy for all existing and new users in Exchange Online - Set default retention policy for On-Premises users

In Exchange Online you can set a retention policy as the default for everyone within the tenant.
In Exchange On-Premises this isn't possible, therefore you have to set them every once in a while.

For Exchange Online:

To view all your retention policies:
Get-RetentionPolicy

The set the one you want as the default:
Set-RetentionPolicy "Company - Default Policy" -IsDefault:$true

For Exchange On-Premises:

To view all your retention policies:
Get-RetentionPolicy

To set the one you want as default for all existing users:
Get-Mailbox -Resultsize Unlimited | Set-Mailbox -RetentionPolicy "Company - Default Policy"

To check what the current retention policy is:
Get-Mailbox -ResultSize Unlimited | Select Retentionpolicy,Name

02 July 2019

EdgeSync failed to decrypt the credential for Edge Transport - Eventid 1033 MSExchange EdgeSync - Renewed certificate lately?

EdgeSync failed to decrypt the credential for Edge Transport server sr-xxxx.domain.lan using the private key of the default Exchange certificate with exception Invalid provider type specified.
. The certificate's thumbprint is XXXXXXXXXXXXXXXXXXXX52F0283F5F8BDDD13058 and its subject is CN=autodiscover.domain.nl, OU=ICT, O=Company., L=City, S=State, C=NL.
Use either Enable-ExchangeCertificate or New-ExchangeCertificate to set the proper Exchange default certificate and re-subscribe the Edge Transport server sr-XXXX.domain.lan again.

Well...here we go again.
At least I'm not bored at work.

Quick recap, this was after renewing our old Exchange certificate that had the SMTP service assigned to it. The old certificate was an old SHA1 type, this is very important to keep in mind as it comes back later in the solution.

In my quest to solve this as quickly as possible I came across some other strange things, which I will list later on.
But first let me sum up all the steps needed to resolve this:

  1. On the Edge server request a new Exchange certificate:
    New-ExchangeCertificate
  2. Generate a new EdgeSubscription file:
    New-EdgeSubscription -FileName C:\Temp\Servername.xml
  3. Copy the EdgeSubscription file to an Exchange 2013 or 2016 or 2019 server (I'm assuming you installed multirole)
  4. Remove the current EdgeSubscription(s):
    Get-EdgeSubscription | select Name
    Remove-EdgeSubscription -Identity YourEdgesubscriptionName
  5. Find the current self signed Exchange Certificate with the servername as Subject. This certificate is created at installation. This certificate needs to get the SMTP service reassigned to it. Copy the thumbprint, we need it in the second command:
    Get-ExchangeCertificate | fl
    Enable-ExchangeCertificate -Thumbprint XXXXXXXXXXXXXXXXXX334EFEA37EFC7E5813 -DoNotRequireSsl -Services:SMTP
  6. Double check your existing subscription has been deleted:
    Get-EdgeSubscription
  7. Create a new Edge Subscription with the file copied in step 3:
    New-EdgeSubscription -FileData ([byte[]]$(Get-Content -Path "C:\Temp\Servername.xml" -Encoding Byte -ReadCount 0)) -Site YourSiteName
  8. Then start your newly created subscription:
    Start-EdgeSynchronization
  9. Test the EdgeSynchronization:
    Test-EdgeSynchronization
  10. The last step is to sync everything:
    Start-EdgeSynchronization -ForceUpdateCookie -ForceFullSync
The problem I had was the certificate that got renewed initialy was a SHA1 certificate.
The one it got replaced with was a SHA256 certificate. When trying to renew the Edge Subscription the following error popped up:
A special Rpc error occurs on server sr-xxxxx Invalid provider type specified
This the SHA1 part I mentioned earlier. The Edge Subscription only works with SHA1 certificates.
Strange and not very secure, eventho it is meant for internal Exchange server traffic only.
Hence the default certificate created at installation of Exchange itself is needed, which is SHA1.

Solved.



27 December 2018

Save disk space - Compact - NTFS compress your folders

In the ever ongoing search for disk space on Exchange servers it is possible to compress certain folders with NTFS compression.
You could do this through the file explorer and click yourself silly, or you can turn to PowerShell.
Actually it's just an old command prompt program, but we'll use it in PowerShell.

Now first things first:

DO NOT USE THIS ON YOUR MAILBOXDATABASES!!!

This may seem like a no brainer but I don't want to hear that someone used it because I didn't warn them.
The only thing you can safely use this for is on log files, temp files, etl files and blg files.
If you've read my previous blog post about cleaning up certain logging folders used by exchange (which can be found here:
https://vanbrenk.blogspot.com/2018/02/clean-iis-log-files-blg-and-etl-files.html)

Here are the directories you can compress safely:
As always with scripts/code found on the internet, test it yourself on a test environment.

.\compact.exe /C /S /A /I "C:\Program Files\Microsoft\Exchange Server\V15\Logging\*"                        
.\compact.exe /C /S /A /I "C:\Program Files\Microsoft\Exchange Server\V15\Bin\Search\Ceres\Diagnostics\ETLTraces\*"                        
.\compact.exe /C /S /A /I "C:\Program Files\Microsoft\Exchange Server\V15\Bin\Search\Ceres\Diagnostics\Logs\*"                        
.\compact.exe /C /S /A /I "C:\Program Files\Microsoft\Exchange Server\V15\FIP-FS\Data\ProgramLogArchive\*"                        
.\compact.exe /C /S /A /I "C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\Logs\FrontEnd\Connectivity\*"                        
.\compact.exe /C /S /A /I "C:\Windows\System32\LogFiles\HTTPERR\*"

27 November 2018

PowerShell One liners (continuous work in progress)

If you know a nice one liner that should be on here drop me a line.

Install and configure Active Directory Certificate Authority Web Enrollment:
Import-Module ServerManager
Add-WindowsFeature Adcs-Web-Enrollment
Install-AdcsWebEnrollment
Get the top30 largest mailboxes per mailboxdatabase:
Get-Mailbox -Database databasename | Get-MailboxStatistics | Sort-Object totalitemsize -Descending| Select-Object DisplayName,TotalItemSize -First 30
Get all members in a dynamic distribution group:
$dyn =  Get-DynamicDistributionGroup "distributiongroupname"
Get-Recipient -RecipientPreviewFilter $dyn.recipientfilter -OrganizationalUnit $dyn.recipientcontainer
Get emailaddresspolicy setting for all users and set to True:
Get-Mailbox -ResultSize Unlimited | Where {$_.EmailAddressPolicyEnabled -eq $False} | Set-Mailbox -EmailAddressPolicyEnabled $true
Install HTMLOutView module:
Install-Module PSWriteHTML -Force
Install the latest PowerShellCore version:
Invoke-Expression "& { $(Invoke-RestMethod https://aka.ms/install-powershell.ps1) } -UseMSI"
Install the latest PowerShellGet version:
Install-Module PowerShellGet -Force
Find the number of users that connect through OWA:
"C:\Program Files (x86)\Log Parser 2.2\logparser.exe" "SELECT cs-username, Count(*) AS OWAHits from \\sr-xxxxx\d$\IISLogs\W3SVC1\u_ex*.log
 WHERE cs-uri-stem LIKE '/OWA/' AND cs-username IS NOT NULL GROUP BY cs-username ORDER BY OWAHits Desc" -rtp:-1
Find all soft deleted mailboxes
Get-MailboxDatabase | Get-MailboxStatistics | Where { $_.DisconnectReason -eq "SoftDeleted" } | Format-Table DisplayName,Database,DisconnectDate
Permanently delete soft deleted mailboxes
Remove-StoreMailbox -Database MBX02 -Identity "John Doe" -MailboxState SoftDeleted
Delete all soft deleted mailboxes per database
Get-MailboxStatistics -Database MBX02 | where {$_.DisconnectReason -eq "SoftDeleted"} | ForEach
 {Remove-StoreMailbox -Database $_.Database -Identity $_.MailboxGuid -MailboxState SoftDeleted}
Update the Offline Addressbook and the Global Addressbook
Get-OfflineAddressBook | Update-OfflineAddressBook
Get-GlobalAddressList | Update-GlobalAddressList
Update Windows Defender manually:
"%programfiles%\windows defender\mpcmdrun.exe" -signatureupdate -http
Search for IMAP enabled mailboxes:
Get-CASMailbox -ResultSize unlimited | where {$_.ImapEnabled -eq $true} | FL name | out-file C:\temp\imapenabled.txt
Enable Remote Desktop locally:
Set-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' -Name fDenyTSConnections -Value 1
Or including the firewall rule:
(Get-WmiObject Win32_TerminalServiceSetting -Namespace root\cimv2\TerminalServices).SetAllowTsConnections(1,1) | Out-Null
(Get-WmiObject -Class "Win32_TSGeneralSetting" -Namespace root\cimv2\TerminalServices 
-Filter "TerminalName='RDP-tcp'").SetUserAuthenticationRequired(0) | Out-Null
Get-NetFirewallRule -DisplayName "Remote Desktop*" | Set-NetFirewallRule -enabled true
Add a user to blocked senders
Set-MailboxJunkEmailConfiguration -Identity "UserName" –BlockedSendersandDomains @{Add="somebody@domain.com"}
Check if set correctly
Get-MailboxJunkEmailConfiguration -Identity "UserName" | FL BlockedSendersandDomains
To Remove a user from blocked senders
Set-MailboxJunkEmailConfiguration -Identity "UserName" –BlockedSendersandDomains @{Remove="somebody@domain.com"}
Delete the file "desktop.ini" from 2 directories deep:
get-childitem -path \\domain.lan\sharename\users\home\*\* -force -filter "desktop.ini" | foreach ($_) {remove-item $_.fullname -force 
-verbose 4>> c:\temp\desktopiniresults.txt}
Set UPN to match Mail Address for Office365 use:
Get-User -OrganizationalUnit "domain.com/OUName" -ResultSize unlimited | Where { -Not [string]::IsNullOrEmpty($_.WindowsEmailAddress) } | 
ForEach { Set-User -Identity $_.Guid.ToString() -UserPrincipalName $_.WindowsEmailAddress.ToString() }
Allow Windows 10 PC in workgroup to manage Hyper-v server:
winrm quickconfig -force
winrm set winrm/config/client ‘@{TrustedHosts=”Name of the Server”}’
Enable protocol logging for IMAP
Set-ImapSettings -Server "CAS01" -ProtocolLogEnabled $true
Disable protocol logging for IMAP
Set-ImapSettings -Server "CAS01" -ProtocolLogEnabled $false
Recreate the Sharedwebconfig.config files for Exchange 2013:
cd %ExchangeInstallPath%\bin
DependentAssemblyGenerator.exe -exchangePath "%ExchangeInstallPath%bin" -exchangePath "%ExchangeInstallPath%ClientAccess" 
-configFile "%ExchangeInstallPath%ClientAccess\SharedWebConfig.config"
DependentAssemblyGenerator.exe -exchangePath "%ExchangeInstallPath%bin" -exchangePath "%ExchangeInstallPath%FrontEnd\HttpProxy" 
-configFile "%ExchangeInstallPath%FrontEnd\HttpProxy\SharedWebConfig.config"
Get the list of network profiles on the system.
Get-NetConnectionProfile
Change the network interface to private, use the network interface index number from the previous command.
Set-NetConnectionProfile -InterfaceIndex 10 -NetworkCategory Private
Get Exchange build number:
Get-ExchangeServer | Format-List Name, Edition, AdminDisplayVersion
Get Exchange Schema version:
"Exchange Schema Version = " + ([ADSI]("LDAP://CN=ms-Exch-Schema-Version-Pt," + ([ADSI]"LDAP://RootDSE").schemaNamingContext)).rangeUpper
Set Default Addressbook Policy and Retention Policy for all mailboxes at once:
Get-Mailbox -ResultSize unlimited | Set-mailbox -AddressBookPolicy "Your AddressBookPolicy" -RetentionPolicy "Your - Default Policy"
Quickly add the Exchange PowerShell module to a regular PowerShell console:
Add-PSSnapin *exchange*
Add multiple aliasses at once:
Set-Mailbox "UserName" -EmailAddresses @{add="UserName01@domain.com","UserName02@domain.com","UserName03@domain.com","UserName04@domain.com",
"UserName05@domain.com","UserName06@domain.com","UserName07@domain.com","UserName08@domain.com","UserName09@domain.com","UserName10@domain.com",
"UserName11@domain.com","UserName12@domain.com","UserName13@domain.com","UserName14@domain.com","UserName15@domain.com","UserName16@domain.com",
"UserName17@domain.com","UserName18@domain.com","UserName19@domain.com","UserName20@domain.com"}
List all mailboxes that have a forwarding address
Get-mailbox -Resultsize Unlimited | select DisplayName,ForwardingAddress | where {$_.ForwardingAddress -ne $Null}
Send Output to Clipboard with PowerShell
Get-EventLog application -Newest 1 | clip
Find specific Help articles with Powershell
Get-Help about_
press tab to cycle through the matches
Find white space (Available new mailbox space) in all databases
Get-MailboxDatabase -Status | sort name | select name,@{Name='DB Size (Gb)';
Expression={$_.DatabaseSize.ToGb()}},@{Name='Available New Mbx Space Gb)';
Expression={$_.AvailableNewMailboxSpace.ToGb()}}
Create Powershell profile
New-Item -path $profile -type file –force
Edit the newly created profile in the following location
C:\Users\Username\Documents\WindowsPowerShell
Load all Powershell available modules at once:
Get-Module -ListAvailable | Import-Module
Turn off shutdown tracker for Windows server
New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Reliability"
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Reliability" -Name ShutdownReasonOn -Value 0
Combine multiple files into one;
Get-ChildItem -filter "c:\temp\*.html" | % { Get-Content $_ -ReadCount 0 | Add-Content "c:\temp\combined_files.html" }
Or:
Get-Content -path c:\temp\eventlogs\*.html | Add-Content -Path C:\temp\Eventlogs\combined.html
Get users with imap enabled:
Get-CASMailbox -ResultSize unlimited | Where-Object {$_.imapenabled -eq "true"} | fl name,imapenabled
Get empty AD groups and email the output;
$body=Get-ADGroup -Filter * -Properties Members | where {-not $_.members} | select Name
Send-MailMessage -smtpserver smtp.domain.lan -subject
 "Empty groups" -to "user1@domain.com,user2@domain.com" -from "user@domain.com" -Body ( $Body | out-string )
Set send on behalf of rights;
Set-Mailbox UserMailbox -GrantSendOnBehalfTo UserWhoSends
View who has which permissions on a user mailbox;

Get-MailboxFolderPermission -Identity "alias:\postvak in" | fl 
(for Dutch)

Get-MailboxFolderPermission -Identity "alias:\inbox" | fl 
(for English)

View who has which permissions on a user calendar;

Get-MailboxFolderPermission -Identity alias:\agenda | fl 
(for Dutch)
Get-MailboxFolderPermission -Identity alias:\calendar | fl 
(for English)

Remove user rights on a mailbox/folder for an other user:
Remove-MailboxFolderPermission -Identity username1:\agenda -User username2
Add user rights on a mailbox/folder for an other user:
Add-MailboxFolderPermission -Identity username1:\agenda -AccessRights Publishingeditor -User username2
MAPI encryption enabled or disabled; (for Outlook 2003 clients)

Get-RpcClientAccess | fl encryp*,server
View blocked ActiveSync devices, in "Blocked" state for longer than a month;

Get-ActiveSync Device | Where {$_.DeviceAccessState -eq "blocked"} | Select DeviceModel | ft -auto
Delete "Blocked" activesync devices, in "Blocked" state for longer than a month;

Get-ActiveSync Device | Where {$_.DeviceAccessState -eq "Quarantined" -and $_.FirstSyncTime
 -lt (Get-Date).AddMonths(-1)} | Remove-ActiveSyncDevice

Delete all ActiveSync devices with DeviceAccessState "Blocked";

Get-ActiveSyncDevice | Where {$_.DeviceAccessState -eq "Blocked"} | 
Remove-ActiveSyncDevice

To retrieve all Exchange-related events:


Get-EventLog Application | Where { $_.Source -Ilike “*Exchange*” } 

07 November 2018

Unable to remove shared mailbox from Outlook profile

As a postmaster or Exchange admin you periodically need to open other users mailboxes to move stuff, restore stuff, add or adjust stuff and so on.

After a while you have a long list of attached mailboxes that you don't need anymore.
And sometimes those mailboxes won't disconnect properly.

How do you get rid of them when the mailboxes are still listed in your folder panel though the mailbox but not listed in Account Settings/Change/More settings/Advanced?

You can view the following attribute in ADUC:

Search for the (shared) mailbox you want to remove, right-click the (shared) mailbox, in the Attribute Editor, double click the msExchDelegateListLink attribute, check if your account is listed there. You can remove your account from the msExchDelegateListLink attribute to clear Automapping.
Restart Outlook and check if the shared mailbox is removed.

One way to prevent AutoMapping:
Add-MailboxPermission "shared-mailbox" -User "alias" -AccessRights FullAccess –AutoMapping $False
If the mailbox is still showing in Outlook you have to turn to PowerShell:
Remove-MailboxPermission -Identity shared-mailbox -User user-alias -AccessRights FullAccess -InheritanceType All

19 October 2018

Exchange 2013 and 2016 - Create Edge subscription

I keep forgetting this:

This is for Exchange 2010, 2013 and 2016 and probaly 2019 but I didn't check this.

Create a new subscription file on the Edge server:
New-EdgeSubscription -FileName C:\Temp\Servername-Edge.xml

Copy the file to a mailbox server and import using this command:
New-EdgeSubscription -FileData ([byte[]]$(Get-Content -Path "C:\Temp\Servername-Edge.xml" -Encoding Byte -ReadCount 0)) -Site "Sitename"

451 4.4.0 Primary target IP address responded with: 454 4.7.0 Temporary authentication failure - Exchange Edge

The error doesn't suggest anything that could point you to a solution.
In my case it wasn't the KB article found here.

Turned out to be a certificate Service assignment that got lost.

The error in the Eventviewer was:

EventID: 12023
Level: Warning
Task Category: Transportservice
Source: MSExchangeFrontEndTransport
Microsoft Exchange could not load the certificate with thumbprint of BAE49XX5021785XX4433FXXA78XX434CXXBD4EXX from the personal store on the local computer. This certificate was configured for authentication with other Exchange servers. Mail flow to other Exchange servers could be affected by this error. If the certificate with this thumbprint still exists in the personal store, run Enable-ExchangeCertificate BAE49XX5021785XX4433FXXA78XX434CXXBD4EXX -Services SMTP to resolve the issue. If the certificate does not exist in the personal store, restore it from backup by using the Import-ExchangeCertificate cmdlet, or create a new certificate for the FQDN or the server enabled for SMTP by running the following command: New-ExchangeCertificate -DomainName serverfqdn -Services SMTP. Meanwhile, the certificate with thumbprint XX728XXD9AD2D55XXE9F9D4BEXX8949AE4DXXCBA is being used.

When running the command:
Get-ExchangeCertificate

Thumbprint                                Services   Subject
----------                                --------   -------
499A246DF957FDF438CD9C7BF5DB070E326B0AF9  ...W...    CN=sr-XXXXX.domain.lan, O=Trend Micro ScanMail for Microsoft Ex...
4272892D9AD2D557DE9F9D4BEB98949AE4D8CCBA  .......    CN=sr-XXXXX.domain.lan
065470FCE311211810679A92A4A2F67708E29398  .......    CN=SkypeforBusiness-OWA
DA9D8609DED5198F1AEEE96E3CCE33ED7323DA5E  IP.WS..    CN=service0.domain.lan
ECB0F4D6FE4BCA7B6DAB79C96F491222F845B3B9  ....S..    CN=service1.domain.nl, O=domain N.V., L=City, S=State, C=NL
E40C46317EE13A419C3B41334EFEA37EFC7E5813  ....S..    CN=sr-XXXXX
2B55508050B8C4269D4DA3EE5C97B346AEAFDF7C  .......    CN=WMSvc-SR-XXXXX
93EEEB92883AB769FD22226B8B78DAB4C60EABD0  ....S..    CN=Microsoft Exchange Server Auth Certificate


Enable-ExchangeCertificate DA9D8609DED5198F1AEEE96E3CCE33ED7323DA5E -Services SMTP

Confirm
Overwrite the existing default SMTP certificate?

Current certificate: 'BAE49EF5021785CA4433F25A7800434CA4BD4E6E' (expires 21-11-2017 13:51:22)
Replace it with certificate: 'DA9D8609DED5198F1AEEE96E3CCE33ED7323DA5E' (expires 26-9-2020 14:12:44)
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [?] Help (default is "Y"): y
A special Rpc error occurs on server SR-XXXXX: The internal transport certificate for the local server was damaged or m
issing in Active Directory. The problem has been fixed. However, if you have existing Edge Subscriptions, you must subs
cribe all Edge Transport servers again by using the New-EdgeSubscription cmdlet in the Shell.
    + CategoryInfo          : ObjectNotFound: (:) [Enable-ExchangeCertificate], InvalidOperationException
    + FullyQualifiedErrorId : [Server=SR-XXXXX,RequestId=30ee7bbb-899d-4cb0-b4e2-8d7862775a41,TimeStamp=19-10-2018 10:
   06:00] [FailureCategory=Cmdlet-InvalidOperationException] FD2ADDFB,Microsoft.Exchange.Management.SystemConfigurati
  onTasks.EnableExchangeCertificate
    + PSComputerName        : sr-XXXXX.domain.lan

As soon as I ran this command mail flow started again.

17 August 2018

Get notified by email about pending Windows Updates

This all started life by Boe Prox, he created the basic script.
You can find it on his site Learn PowerShell
I added things from comments other blogs and stuff from my own.

Schedule this to run once a week or once a month to receive an email about the number of updates that are pending on the servers that you specify.

You could schedule it for all Exchange servers, or all Skype servers, or the product group you want to see or are responsible for.

Edit the logpath, the product and the email settings and you're good to go.
And remove the spaces around style (Blogger formatting thingy).

The email will look like this:

























Here's the script:


Function Get-PendingUpdate {             
    <#    
      .SYNOPSIS   
        Retrieves the updates waiting to be installed from WSUS   
      .DESCRIPTION   
        Retrieves the updates waiting to be installed from WSUS  
      .PARAMETER Computername 
        Computer or computers to find updates for.   
      .EXAMPLE   
       Get-PendingUpdate
    
       Description 
       ----------- 
       Retrieves the updates that are available to install on the local system 
      .NOTES 
      Author: Boe Prox                                           
                                        
    #>             
                  
    #Requires -version 3.0               
    [CmdletBinding(             
        DefaultParameterSetName = 'computer'             
        )]             
    param(             
        [Parameter(ValueFromPipeline = $True)]             
            [string[]]$Computername = $env:COMPUTERNAME            
        )                 
    Process {             
        ForEach ($computer in $Computername) {             
            If (Test-Connection -ComputerName $computer -Count 1 -Quiet) {             
                Try {             
                #Create Session COM object             
                    Write-Verbose "Creating COM object for WSUS Session"             
                    $updatesession =  [activator]::CreateInstance([type]::GetTypeFromProgID("Microsoft.Update.Session",$computer))             
                    }             
                Catch {             
                    Write-Warning "$($Error[0])"             
                    Break             
                    }             
             
                #Configure Session COM Object             
                Write-Verbose "Creating COM object for WSUS update Search"             
                $updatesearcher = $updatesession.CreateUpdateSearcher()             
             
                #Configure Searcher object to look for Updates awaiting installation             
                Write-Verbose "Searching for WSUS updates on client"             
                $searchresult = $updatesearcher.Search("IsInstalled=0")                 
                         
                #Verify if Updates need installed             
                Write-Verbose "Verifing that updates are available to install"             
                If ($searchresult.Updates.Count -gt 0) {             
                    #Updates are waiting to be installed             
                    Write-Verbose "Found $($searchresult.Updates.Count) update\s!"             
                    #Cache the count to make the For loop run faster             
                    $count = $searchresult.Updates.Count             
                             
                    #Begin iterating through Updates available for installation             
                    Write-Verbose "Iterating through list of updates"             
                    For ($i=0; $i -lt $Count; $i++) {             
                        #Create object holding update             
                        $Update = $searchresult.Updates.Item($i)            
                        [pscustomobject]@{            
                            Computername = $Computer            
                            Title = $Update.Title            
                            KB = $($Update.KBArticleIDs)            
                            SecurityBulletin = $($Update.SecurityBulletinIDs)            
                            MsrcSeverity = $Update.MsrcSeverity            
                            IsDownloaded = $Update.IsDownloaded            
                            Url = $($Update.MoreInfoUrls)            
                            Categories = ($Update.Categories | Select-Object -ExpandProperty Name)            
                            BundledUpdates = @($Update.BundledUpdates)|ForEach{            
                               [pscustomobject]@{            
                                    Title = $_.Title            
                                    DownloadUrl = @($_.DownloadContents).DownloadUrl            
                                }            
                            }            
                        }             
                    }            
                }             
                Else {             
                    #Nothing to install at this time             
                    Write-Verbose "No updates to install."             
                }            
            }             
            Else {             
                #Nothing to install at this time             
                Write-Warning "$($c): Offline"             
            }              
        }            
    }              
}            
            
$output = Get-Pendingupdate -Verbose -ComputerName sr-xxxx,sr-xxxx,sr-xxxx,sr-xxxx,sr-xxxx,sr-xxxx,sr-xxxx | Group-Object ComputerName | Foreach-Object {            
 $_ | Select-Object @{Name='ComputerName';Expr={$_.Name}},            
  @{Name='TotalUpdates';Expr={$_.Count}},            
  @{Name='Critical'; Expr={$_.Group| where MsrcSeverity -eq 'Critical'  | measure | select -expand Count}},            
  @{Name='Important';Expr={$_.Group| where MsrcSeverity -eq 'Important' | measure | select -expand Count}},            
  @{Name='Moderate'; Expr={$_.Group| where MsrcSeverity -eq 'Moderate'  | measure | select -expand Count}},            
  @{Name='NonRated'; Expr={$_.Group| where MsrcSeverity -eq $null       | measure | select -expand Count}}            
} | Select-Object Computername, Totalupdates, Critical, Important, Moderate, Nonrated            
            
# Change variables to your environment            
$Date = Get-Date -Format dd-MM-yyyy              
$logPath = "C:\_Scripts\"            
$product = "Exchange"            
            
# Build table for html files, remove spaces around style            
$style = "< style >BODY{font-family: Arial; font-size: 10pt;}"                                    
$style = $style + "TABLE{border: 1px solid black; border-collapse: collapse;}"                                    
$style = $style + "TH{border: 1px solid black; background: #dddddd; padding: 5px; }"                                    
$style = $style + "TD{border: 1px solid black; padding: 5px; }"                                    
$style = $style + "</ style >"                                    
# End HTML Output file style               
            
#SMTP options for sending the report email                        
$smtpServer = "smtp.domain.lan"                        
$smtpFrom = "Get-PendingWindowsUpdates@domain.nl"                        
$smtpTo = "recipient@domain.nl"                        
$messageSubject = "Windowsupdates available for $product"            
                        
#$body = $output | ConvertTo-Html -head $style -body "Get Windows Updates"            
$output | ConvertTo-HTML -head $style -body "
Windows Updates for $product Servers
" | Out-File "$logPath\output-$date.html"                
            
Send-Mailmessage -To $smtpto -From $smtpfrom -SmtpServer $smtpserver -Subject $messagesubject -Body (Get-Content $logpath\output-$date.html | Out-String) -BodyasHtml            
            
# Remove all html files to prevent filling the disk                        
Remove-Item "$logPath\output-$date.html"

03 August 2018

Block .NET 4.7.2 on Exchange 2013 and 2016

As you probably know the exchange teams has decided to skip .net 4.7.2 support for all exchange versions.
Here's a simple way to block .net 4.7.2 from auto installing on your remote exchange servers

From an elevated command prompt run:

REG ADD "\\SERVERNAME\HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\WU" /v "BlockNetFramework472" /t REG_DWORD /d "00000001"

Or if you want to create your own reg file:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\WU] "BlockNetFramework472"=dword:00000001

30 July 2018

Exchange Server testing - Things to test

This a copy from the article Steve Goodman wrote for Techtarget.com in case it gets lost, deleted or whatever. Which would be a shame because of the relevant info it contains.

Set up Exchange for basic tests

You should test the Exchange 2016 infrastructure at a high level to verify its status. At this point, it's unlikely you have migrated the client access role across, so you might need to reconfigure the local host files on your test clients to run these trials.
AreaTest activity
User accountsCreate test mailboxes in each data center on Exchange 2016.
User accountsCreate test mailboxes in each DAG on Exchange 2010.
ClientConfigure host file records to simulate connectivity to Exchange 2016 load balancers.
OWA 2016Test Outlook on the web/Outlook Web App (OWA) login functionality for an Exchange 2016 user in each data center.
OWA 2016Test reading, opening and replying to emails for an Exchange 2016 user in each data center.
OWA 2016Test creating, updating and modifying a calendar item for an Exchange 2016 user in each data center.
OWA 2016Test creating, updating and modifying a contact item for an Exchange 2016 user in each data center.
OWA 2016Test disabling user access to OWA for security purposes.
EmailTest mail flow between Exchange 2016 users in each data center.
EmailTest mail flow to an Exchange 2016 user in each data center from Exchange 2010 in each data center.
EmailTest mail flow to an Exchange 2016 user in each data center from an external source.
EmailTest mail flow from an Exchange 2016 user to an Exchange 2010 user.
EmailTest external out-of-office settings of an Exchange 2016 user from an external source.
FederationTest availability of an Exchange 2016 mailbox from an external partner.
FederationTest availability of an external partner's mailbox from Exchange 2016.
Exchange generalTest mailbox move functionality from Exchange 2010 to Exchange 2016 in each DAG.
Exchange generalTest mailbox move functionality from Exchange 2016 to Exchange 2010 in each DAG.

Testing each database availability group

After you complete these basic checks, you should run tests with the following PowerShell cmdlets against each DAG to check mailbox services.
AreaTest activity
Service healthUse Test-ServiceHealth to verify services are running.
Service healthUse Get-HealthReport to check if each server is healthy.
Mail flowUse Test-Mailflow to test the mail flow against each server.
Mail flowUse Test-SmtpConnectivity to test connectivity to each receive connector.
MailboxUse Test-ReplicationHealth to validate the DAG continuous replication status.
MailboxUse Get-MailboxDatabaseCopyStatus to view the health of the database copies within the DAG.
MailboxUse Test-MapiConnectivity to verify MAPI and LDAP work with a user's login.
MailboxUse Test-AssistantHealth to check that the Mailbox Assistants service is running and healthy against each server.
Client accessUse the Microsoft Connectivity Analyzer to execute the Outlook connectivity tests.
Client accessUse Test-WebServicesConnectivity to test client connectivity to Exchange Web Services virtual directories against each server.
Client accessUse Test-ActiveSyncConnectivity to simulate a full synchronization with a mobile device.
Client accessUse a browser to log on to the Exchange Admin Center to verify functionality of all Exchange 2016 servers.
Client accessUse Test-MRSHealth to verify that the Mailbox Replication service is running and that it responds to a remote procedure call ping check.
High availabilityValidate that the passive copy of databases in the same data center comes online automatically after a failure of a database.
High availabilityValidate that the services that are running in the secondary data center continue to operate without any interruption after failing all the servers within the DAG in the primary data center.
High availabilityManually remove a disk from a passive database to test if auto reseed works as expected. Reverse the process to return the disks to the original state.
High availabilityPerform a cold start of the DAG to validate that the DAG will start correctly if a major outage occurs.
Load balancerDisable all load balanced servers for each server in turn within the same data center. Validate client access and mail flow for mailboxes hosted on failed servers.
Load balancerDisable all load balanced services within the first data center. Validate client access and mail flow for mailboxes hosted on the failed data center.
Load balancerDisable all load balanced services within the secondary data center. Validate client access and mail flow for mailboxes hosted on the failed data center.
BackupsUse Get-MailboxDatabase to validate the right setting for circular logging: disabled if using backup software or enabled if there is no backup software installed.
BackupsPerform a full backup of each mailbox database.
BackupsPerform an incremental backup of each mailbox database.
BackupsRestore a full database to a temporary location and recover a mailbox.
BackupsRestore a full database to the original location and mount it.
Unified messagingTest leaving a voicemail to an Exchange 2016 mailbox.
Unified messagingTest receiving a voicemail in an Exchange 2016 mailbox via the Outlook client.
Unified messagingTest receiving a voicemail in an Exchange 2016 mailbox via Play on Phone.
Unified messagingTest access to Outlook Voice Access in Exchange 2016.
Unified messagingTest instant messaging sign-in to Exchange 2016.
Unified messagingTest Skype for Business meeting scheduling in OWA.

Check client connectivity

In the final stage of Exchange Server testing, you should examine client connectivity. If the Exchange system passes all the previous tests, then basic connectivity is most likely fine. It's important to run a full set of tests using the builds of the clients the end users will use.
Your checklist might vary from the one below to include the different Outlook versions and mobile devices to test.
AreaTest Activity
Outlook 2016Test before/after migration experience.
Outlook 2016Test Autodiscover in Exchange 2016.
Outlook 2016Test cached mode access via Exchange 2016 in each data center.
Outlook 2016Test offline address book download functionality via Exchange 2016 in each data center.
Outlook 2016Test Exchange Web Services -- free, busy, out of office -- functionality via Exchange 2016 in each data center.
Outlook 2016Test Outlook Anywhere functionality via Exchange 2016 in each data center.
Outlook 2016Test mail send/receive/synchronization via Exchange 2016 in each data center.
Outlook 2016Test open additional mailbox functionality via Exchange 2016 in each data center.
Outlook 2016Test open additional on-premises mailbox functionality via Exchange 2016 in each data center.
Mobile deviceTest Autodiscover functionality in Exchange 2016 in each DAG.
Mobile deviceTest ActiveSync synchronization in Exchange 2016 in each DAG.
Skype for Business clientTest Exchange access after migration to Exchange 2016.
As you test, record the results for reference purposes. For example, you may wish to:
  • collect screenshots or screen recordings as you test;
  • work with a colleague to help oversee the testing process and sign off on the checklist; and
  • there may be other areas to resolve, so add a column to add notes for any remediation actions before retesting the environment.