Friday, August 14, 2015

Script: Pool Objects

After my issue this week with attempting to decommission a Lync 2013 pool due to a lingering object, I decided to write a simple script that takes the pool name that you are attempting to remove, and runs though all possible objects and outputs any missed that are still attached to the pool.

Download OrphanedObjects.ps1

I have tested and confirmed that this works on Lync 2013 and S4B. It does NOT work on Lync 2010, but if I get enough interest in a 2010 version I could modify it to work.

Searching for Orphaned Objects

Recently when decommissioning a Lync 2013 pool, I ran into an issue where it said that by publishing the topology and removing the pool I would orphan existing users, endpoints, or devices:

Consult your Skype for Business Server documentation to learn how to move or disable objects still homed on the pool. To find those objects, execute the following cmdLets: Get-CsUser, Get-CsExUmContact, Get-CsCommonAreaPhone, Get-CsAnalogDevice, Get-CsRgsWorkflow, Get-CsDialInConferencingAccessNumber, Get-CsAudioTestServiceApplication, Get-CsTrustedApplicationEndpoint, Get-CsPersistentChatEndpoint.

Now I knew there were more objects that could trigger the orphaned objects failure, so I also ran Get-CsConferenceDirectory, Get-CsTrustedApplication, Get-CsTrustedApplicationComputer, Get-CsTrustedApplicationEndpoint, Get-CsTrustedApplicationPool. The only result I got was aCsAudioTestServiceApplication: 

I attempted to remove this however there is no remove cmdlet, and there is no option to set it's registrar pool with Set-CsAudioTestServiceApplication. 

I confirmed with one of my co-workers that it was not possible to remove this, however it should not be an object that prevents the removal of a pool. So what was causing the hold? 

I decided to go into the weeds and dumped all user objects from AD to a CSV via CDSVE ( csvde -f test.csv -r objectClass=user ), and then searched for all users who had the msRTCSIP-PrimaryHomeServer value that matched the Lync 2013 server. Low and behold there was a Lync Room System object that was still homed to the Lync 2013 pool. After moving this object to the new S4B pool, I was then able to publish the topology.

Thursday, August 13, 2015

Lync 2013 to S4B CMS Migration Replication Issues

I recently moved the CMS from Lync 2013 to a new S4B pool for a project I was working on. I followed the normal procedure and re-ran bootstapper on all the nodes that make up the new pool hosting the CMS, as well as the old Lync 2013 pool to remove the CMS role. I verified that the S4B Master Replicator Agent and File Transfer Agent services were running on all four of the new S4B nodes. I rebooted all four of the new S4B servers individually and once complete I attempted to view the CMS replication status however it reported nothing was updating. All entries show UpToDate False, and all of them except the node I ran the Move-csManagmentServer cmdlet from and the edge servers show laststatusreport from around the time I performed the move:

I verified that I was able to download the Topology; I could see that FE01 was the ActiveMaster of the CMS:

I ran a trace using ManagmentCore scenario and saw a couple errors regarding the XDS-Replica folder:

This line stuck out particularly:

Query changes operation failed. Exception [System.UnauthorizedAccessException: Access to the path '\\\xds-replica\xds-master\xds-master\working\replication\tmp\0c112834-9f3e-49bc-ba01-fb0e4227e56e' is denied.

At this point I attempted to recreate the XDS-Replica folder by following Ken’s blog ) however this didn’t seem to solve it. At this point I knew it had to be something to do with permissions/authentication. I checked and verified that all the servers in the new S4B pool were members of the RTCUniversalConfigReplicator group. 

I finally enabled CAPI2 logs and saw that there was an expired certificate being passed. So I re-ran the deployment wizard, checked and sure enough the OAuth certificate had expired. Renewing the certificate and restarting each server propagated the OAuth certificate and replication began to work. 

Saturday, August 1, 2015

Exchange 2013 Cmdlets

I get asked often for various cmdlets on how to perform actions in Exchange PowerShell. So I have decided to post them on the blog. I will be updating this every time I find myself using a new one often so be sure to bookmark this page.

Export Mailbox folder sizes:
Get-Mailbox -Identity User | Get-MailboxFolderStatistics | Select Identity,FolderPath,FolderSize,ItemsInFolder | Sort-Object ItemsInFolder | fl | out-File C:\Temp\User-Mbox.txt

Export UPN of all users with an archive:
Get-Mailbox -Archive | Select-Object UserPrincipalName | Export-Csv -Path C:\Temp\AssignLicenseOptions.csv

Retrieve Message Tracking logs, sort by TotalBytes and save to txt file:
 Get-MessageTrackingLog -EventId SEND -Start "9/1/2011 06:00:00" -End "9/1/2011 20:00:00" -resultsize unlimited | Sort-Object TotalBytes -Descending | FT Sender, MessageSubject, TotalBytes > C:\test.txt

 Retrieve send-as permissions on a mailbox that is not the account tied to the mailbox:
 Get-Mailbox -Identity "ltemple" | Get-ADPermission | where { ($_.ExtendedRights -like "*Send-As*") -and -not ($_.User -like "NT AUTHORITY\SELF") } | select Identity, User, ExtendedRights, IsInherited | FT -Wrap

 Formatted List with Mailbox Size in Megabytes (MB) to CSV File:
Get-Mailbox | Get-MailboxStatistics | Select-Object DisplayName,{$_.TotalItemSize.Value.ToMB()} | export-csv -path C:\<FILENAME>.csv

Alternatively, you can specify the MBD also:
Get-MailboxDatabase -Identity "MailboxDatabase" | Get-Mailbox | Get-MailboxStatistics | Select-Object DisplayName,{$_.TotalItemSize.Value.ToMB()} | export-csv -path C:\Temp\MailboxSize.csv

Get Archive Size:
Get-Mailbox | Get-MailboxStatistics -Archive | Select-Object DisplayName,{$_.TotalItemSize.Value.ToMB()},Database | export-csv -path C:\Filename.csv

Formatted List with Mailbox Size in Megabytes (MB) Output to Screen:
Get-Mailbox | Get-MailboxStatistics | Format-Table DisplayName,{$_.TotalItemSize.Value.ToMB()} -auto

Get whitespace in DB's:
Get-MailboxDatabase -Status | Select Servername, Name, AvailableNewMailboxSpace
Get-MailboxServer | Get-MailboxDatabase | Select Server, StorageGroupName, Name, @{Name="Size";expression={"{0:N2}" -f ((get-mailboxstatistics -database $_.Identity | Measure-Object -Property TotalItemSize,TotalDeletedItemSize -Sum |Select-Object Sum |Measure-Object -Property Sum -Sum).Sum.ToString() /1mb)}}

Get Mailbox Move Statistics: 
Get-MoveRequest | Get-MoveRequestStatistics | ft displayname,*stat*,perc*,totalmailboxsize

Only in Progress Moves:
Get-MoveRequest –MoveStatus InProgress | Get-MoveRequestStatistics | ft displayname,*stat*,perc*,totalmailboxsize

For a constant update:
while (1 -eq 1) { Get-MoveRequest | Get-MoveRequestStatistics; Start-Sleep -Seconds 2; Clear-Host; }

Empty Dumpster for User: 
search-mailbox -identity "MBauer" -searchdumpsterOnly -DeleteContent

Dump Message Tracking to GridView:
get-messagetrackinglog -Start "7/14/2015 8:00:00 AM" -End "7/14/2015 4:30:00 PM" -ResultSize Unlimited | select timestamp, ClientIp, ClientHostname, ServerIp, ServerHostname, ConnectorId, Source, EventId, InternalMessageId, Sender, {$_.Recipients}, {$_.RecipientStatus},MessageSubject, SourceContext, MessageId, TotalBytes, RecipientCount, RelatedRecipientAddress, Reference, ReturnPath, MessageInfo | Out-GridView

Dump Message Tracking Results to CSV: 
get-messagetrackinglog -MessageSubject "Test Message to Verify Tracking Details" -Start "1/26/2014 9:00:00 PM" -End "1/26/2014 9:30:00 PM" | select timestamp, ClientIp, ClientHostname, ServerIp, ServerHostname, SourceContext, ConnectorId, Source, EventId, InternalMessageId, MessageId, {$_.Recipients}, {$_.RecipientStatus}, TotalBytes, RecipientCount, RelatedRecipientAddress, Reference, MessageSubject, Sender, ReturnPath, MessageInfo | Export-Csv "c:\Temp\Track-results.csv"

Get Message Tracking for whole domain: 
get-messagetrackinglog -Server "Exchange-server-name" -Start "7/1/2010 11:34:00 AM" -End "8/10/2010 9:44:00 AM" -resultsize unlimited |where {$_.Sender -like "*"}
Get Unified Messeging Extensions: 
Get-UMMailbox |ft name,extensions,UMDialPlan,UMMailboxPolicy
Get List of SMTP Addresses and export to CSV:
Get-Mailbox -ResultSize Unlimited |Select-Object DisplayName,PrimarySmtpAddress, @{Name=“EmailAddresses”;Expression={$_.EmailAddresses |Where-Object {$_.PrefixString -ceq “smtp”} | ForEach-Object {$_.SmtpAddress}}} | Export-CSV "C:\Temp\List-SMTP-Address.csv"

Get List ActiveSync Devices:
$Mailboxes = Get-Mailbox
$Mailboxes | ForEach {Get-MobileDeviceStatistics -Mailbox:$_.Identity} | Select Identity,DeviceType,DeviceUserAgent,DeviceID,LastSyncAttemptTime,LastSuccessSync | Export-CSV -Path C:\temp\ActiveSyncDevices.csv

Get-ActiveSyncDevice -ResultSize Unlimited | Select-Object UserDisplayName, Name, DeviceUserAgent | Export-Csv C:\Temp\ActiveSyncDevices.csv

Get-Mailbox | ForEach {Get-ActiveSyncDeviceStatistics -Mailbox:$_.Identity} | ft DisplayName, Devicetype, DeviceUserAgent, LastSuccessSync

Get ActiveSync information for specific user:
Get-ActiveSyncDeviceStatistics -Mailbox blye | ft DeviceType, DeviceUserAgent, LastSuccessSync

Grant Rights to a user for a public folder and all sub-folders:
Get-PublicFolder –Identity “\IT” –Recurse | Add-PublicFolderClientPermission –User pponzeka –AccessRights PublishingEditor

See if anyone has sent or received email from a specific domain: 
Get-MessageTrackingLog -resultsize unlimited |where-object {$_.Recipients -like “*,*,*,*” -AND $_.EventId -eq “Send”} |ft -auto >>C:\Temp\MailDomainSearch.txt

Batch move mailbox archives from text file:
$Mailboxes = Get-Content .\TextFile.txt
For ($Start = 0; $Start -lt $Mailboxes.length; $Start++) {New-MoveRequest –Identity `$Mailboxes[$Start] -ArchiveOnly -ArchiveTargetDatabase 'MailboxDatabaseName'}

Remove messages from queue with specific subject:
Remove-Message -Filter {Subject -eq "ENTERSUBJECT HERE"} -WithNDR $false
Export User's Contacts Folder:
New-MailboxExportRequest -Mailbox user1 -IncludeFolders “#Contacts#” -ExcludeDumpster -filepath \\server\share\user1.pst

Dump Mail Relay to txt file:
(Get-ReceiveConnector "exchange2007\allowedrelay").RemoteIPRanges | select Lowerbound,Upperbound,RangeFormat | sort-object Lowerbound| export-csv c:\rc.txt –NoTypeInformation

Move messages with a specific subject:
Get-Mailbox -Identity “MBauer” | Search-Mailbox -SearchQuery subject:”Alert: Host Parent Partition CPU Utilization high Resolution state: ”,from:”” -TargetMailbox “MBauer” -TargetFolder “SCOM\CPU”

Update OAB:
Update-OfflineAddressBook “default offline address book”

Update DB Catalog Only (Re-Seed Content Index State_):
Update-MailboxDatabaseCopy "Mailboxes M-O\MailboxServer" -CatalogOnly

Set Domain on OWA/ECP Login Page:
Set-OwaVirtualDirectory ServerName\"owa (Default Web Site)" -LogonFormat Username -DefaultDomain TaskRepository

DL Auditing:
Get-DistributionGroup | Select-Object PrimarySMTPAddress | Sort-ObjectPrimarySMTPAddress | Export-CSV DL-ALL.csv -notype
Get-MessageTrackingLog -Server EXCH1 -EventId Expand -ResultSize Unlimited |Sort-Object RelatedRecipientAddress | Group-Object RelatedRecipientAddress |Sort-Object Name | Select-Object @{label="PrimarySmtpAddress";expression={$_.Name}}, Count | Export-CSV DL-Active.csv -notype
$file1 = Import-CSV -Path "DL-ALL.csv"
$file2 = Import-CSV -Path "DL-Active.csv"
Compare-Object $file1 $file2 -Property PrimarySmtpAddress -SyncWindow 500 |Sort-Object PrimarySmtpAddress | Select-Object -Property PrimarySmtpAddress |Export-Csv DL-Inactive.csv -NoType

Tracking Log Explorer:
Get-MessageTrackingLog -ResultSize Unlimited -Start "June 18 2015” | Out-GridView

Disable Calendar Repair:
Set-Mailbox -Identity username -Calendar Repair Disabled $True

Get all unhealthy Exchange resources:
$test = get-healthreport -server CATDALEXCH01 | where {$_.alertvalue -ne “healthy”}
foreach ($line in $test) {$line.entries | where {$_.alertvalue -ne “healthy”} | ft -auto}

Remove Automatic Mapping of Mailbox:
Remove-MailboxPermission -Identity celine -User ashley -AccessRights FullAccess

Add-MailboxPermission -Identity celine -User ashley -AccessRights FullAccess -AutoMapping:$false