Tag: SfB


How to delete those annoying duplicate Lync contacts


sfb-contact-duplicatesIf you’ve been using Lync (now Skype for Business) for a while, you’ll likely find that you have a number of duplicate contacts in your mailbox, due to a legacy behavior of the client auto-creating contacts everytime the client version changed. If you’ve been running Lync for quite a while, these duplicates may run north of 10,000 objects, which will start to mess with your Outlook performance and do horrible things to your mobile phone address book.

The underlying cause of these duplicates being created has long been fixed via CUs, but the contacts remain. The trouble with removing these if you’re on Exchange Online is the contacts have special permissions and can’t be deleted by the end user.

I needed to solve this problem recently, and found plenty of suggested fixes but they were either way too manual (fine for a few duplicates but not thousands) or they just didn’t work at all.

Being a fan of all things PowerShell, I dug out some cmdlets and figured out how to do it via PowerShell, which I’ll spell out below. I’ve included a full copy of the script at the end of this post if you just want to steal the whole thing. Go for it.

To do this you’ll need a PowerShell window (no special modules required) and an account with the global admin role. (To be honest I’ve been lazy here, there will be a combination of role permissions lower than global admin that enables this, I just haven’t figured out what they are yet)

First up, the basics. Get yourself connected to Exchange Online via PowerShell.

$cred = get-credential  
$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://ps.outlook.com/powershell/" -Credential $cred -Authentication Basic -AllowRedirection Import-PSSession $session -AllowClobber

Next create a mailbox to store the log output. I’d recommend using a shared mailbox so it doesn’t consume a license. Be sure to grant yourself access to the mailbox so you can review outputs.

New-Mailbox -Name LyncContactRemoval -Shared 
Get-Mailbox LyncContactRemoval | Add-MailboxPermission -User  -AccessRights FullAccess -InheritanceType All

Now to find the offending contacts. We use a mailbox search query to iterate over all mailbox items that contains the unique string that each duplicate contains. The output of the search is saved to the shared mailbox you created above to allow you to verify the query returns only those items you want.

I’ve included a few sample queries to get you started.
1. Query against a single mailbox
2. Query multiple mailboxes
3. Query all mailboxes

Note that these commands will include the dumpster by default. If you want to exlude the mailbox dumpster from the query, add “-SearchDumpster:$false” to the end of the command. There is also a return item limit of 10,000 items per mailbox.

# Example 1 - For a single mailbox  
Get-Mailbox  | Search-Mailbox -SearchQuery 'all:"This contact was updated from Microsoft Lync" OR all:"This contact was added from Microsoft Lync"' -TargetMailbox LyncContactRemoval -TargetFolder Cleanup -LogLevel Full -LogOnly

#   Example 2 - For a couple of mailboxes  
Get-Mailbox | ?{$_.alias -eq '' -OR $_.alias -eq ';' -or $_.alias -eq ''} | Search-Mailbox -SearchQuery 'all:"This contact was updated from Microsoft Lync" OR all:"This contact was added from Microsoft Lync"' -TargetMailbox LyncContactRemoval -TargetFolder Cleanup -LogLevel Full -LogOnly

#   Example 3 - For all mailboxes  
Get-Mailbox | Search-Mailbox -SearchQuery 'all:"This contact was updated from Microsoft Lync" OR all:"This contact was added from Microsoft Lync"' -TargetMailbox LyncContactRemoval -TargetFolder Cleanup -LogLevel Full -LogOnly

Open the shared mailbox via Outlook and locate the TargetFolder path you used. This will include a mail object with the results of the query you ran, and a CSV file attachment containing the precise results down to a per-contact level. Validate your results are as you expect, and then either proceed with deletion, or refine your query and re-run the search.

Once you’re happy with your query and the output you’re getting, time to hit delete. This is basically just a case of using the -DeleteContent flag on the same query command you used above.

If you use the ‘all mailboxes’ query this may take a very long time. In my case I had about 150 mailboxes with 350,000 duplicate contacts. The deletion process took 7 hours to complete.

Due to the query return item limit, this will only delete 10,000 items from each mailbox at a time. Pending on how many duplicates your users have you may need to run this more than once (I did).

#  For a single mailbox  
Get-Mailbox  | Search-Mailbox -SearchQuery 'all:"This contact was updated from Microsoft Lync" OR all:"This contact was added from Microsoft Lync"' -DeleteContent

# For a couple of mailboxes (this filter uses the mailbox alias value, but you could filter on any field that makes sense to you)
Get-Mailbox | ?{$_.alias -eq '' -OR $_.alias -eq '' -or $_.alias -eq ''} | Search-Mailbox -SearchQuery 'all:"This contact was updated from Microsoft Lync" OR all:"This contact was added from Microsoft Lync"' -DeleteContent

# For all mailboxes - note this could take a loooong time.
Get-Mailbox | Search-Mailbox -SearchQuery 'all:"This contact was updated from Microsoft Lync" OR all:"This contact was added from Microsoft Lync"' -DeleteContent -SearchDumpster:$false

That’s it – job done.

Technically you should remove the shared mailbox you created at the beginning to clean this up. I left mine in place for a while as a lazy log of what was removed, but deleted it once I was happy nobody was upset by the sudden removal of 350,000 completely useless contact objects. That and of course close your session like a good ‘sheller should.

Get-Mailbox LyncContactRemoval | Remove-Mailbox
Remove-PSSession $session

If you’re looking for the entire script, you’ll find it below.  Hope it’s useful.

JB

 

# Get yourself connected to Exchange Online 
$cred = get-credential  
$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://ps.outlook.com/powershell/" -Credential $cred -Authentication Basic -AllowRedirection Import-PSSession $session -AllowClobber

# First create a destination mailbox for query reports. Use a shared mailbox so it doesn't consume a license 
New-Mailbox -Name LyncContactRemoval -Shared 
# Grant yourself access to the mailbox so you can open via Outlook 
Get-Mailbox LyncContactRemoval | Add-MailboxPermission -User <your-mailbox-upn> -AccessRights FullAccess -InheritanceType All

# Query for candidates and log results (no other action taken)

# Example 1 - For a single mailbox  
Get-Mailbox  | Search-Mailbox -SearchQuery 'all:"This contact was updated from Microsoft Lync" OR all:"This contact was added from Microsoft Lync"' -TargetMailbox LyncContactRemoval -TargetFolder Cleanup -LogLevel Full -LogOnly

#   Example 2 - For a couple of mailboxes (this filter uses the mailbox alias value, but you could filter on any field that makes sense to you)  
Get-Mailbox | ?{$_.alias -eq '' -OR $_.alias -eq ';' -or $_.alias -eq ''} | Search-Mailbox -SearchQuery 'all:"This contact was updated from Microsoft Lync" OR all:"This contact was added from Microsoft Lync"' -TargetMailbox LyncContactRemoval -TargetFolder Cleanup -LogLevel Full -LogOnly

#   Example 3 - For all mailboxes  
Get-Mailbox | Search-Mailbox -SearchQuery 'all:"This contact was updated from Microsoft Lync" OR all:"This contact was added from Microsoft Lync"' -TargetMailbox LyncContactRemoval -TargetFolder Cleanup -LogLevel Full -LogOnly

# Note - these commands will include the dumpster by default. If you want to exlude the mailbox dumpster from the query, add "-SearchDumpster:$false" to the end of the command

# Open the mailbox in Outlook and locate the TargetFolder path you used. This will include a mail object with the results of the query, and a CSV file attached containing the precise results down to a per-contact level. Validate your results are as you expect, and then either proceed with deletion, or refine your query and re-run the search.    
# To actually delete the objects.. do this

#  For a single mailbox  
Get-Mailbox  | Search-Mailbox -SearchQuery 'all:"This contact was updated from Microsoft Lync" OR all:"This contact was added from Microsoft Lync"' -DeleteContent

# For a couple of mailboxes (this filter uses the mailbox alias value, but you could filter on any field that makes sense to you)
Get-Mailbox | ?{$_.alias -eq '' -OR $_.alias -eq '' -or $_.alias -eq ''} | Search-Mailbox -SearchQuery 'all:"This contact was updated from Microsoft Lync" OR all:"This contact was added from Microsoft Lync"' -DeleteContent

# For all mailboxes - note this will take a loooong time. For 232 mailboxes with 350,000 duplicate contacts, this process took 7 hours to complete.
Get-Mailbox | Search-Mailbox -SearchQuery 'all:"This contact was updated from Microsoft Lync" OR all:"This contact was added from Microsoft Lync"' -DeleteContent -SearchDumpster:$false

 


User not in service error


I encountered this issue recently when attempting to Lync call an internal user (P2P, not via PSTN), and was initially a little surprised to see this error message return almost instantly.

Fair enough if I’d dialed a number that wasn’t valid, but I wasn’t dialing a phone number, and this was a user that I could see was online and available. What the….

In reality the cause is fairly obvious, but to an end-user this could be fairly confusing, so I thought I’d go through the debug process to be thorough. A quick pick through my local trace logs returned this:

history-info: <sip:user@domain.com?Reason=SIP%3Bcause%3D302%3Btext%3D%22Moved%20Temporarily%22>;index=1;ms-retarget-reason=forwarding
ms-diagnostics: 13006;reason=”Request forwarded, any previous branches cancelled.”;source=”front-end-servername”;appName=”InboundRouting”

which ultimately resulted in:

SIP/2.0 404 No matching rule has been found in the dial plan for the called number

All this talk of numbers when you’re not dialing a number seems a little odd, until you consider the user-controlled call forwarding feature. Then it all makes sense again.

If a user has elected to forward their calls (not sim-ring) to another number, but have entered a number that cannot be normalised for some reason (could be their fault or yours), then Lync will quite legitimately return the out of service error. Question then becomes, who needs to fix it? The user, or the Lync Admin? How do you know?

To find out, you can consult either the server logs or the SSRS QoE reports. I tend to jump into the QoE reports first in situations like this as it is often quicker. On the QoE report server, run the User Activity Report, bring up calls from the offending user, and open the detail of the call that failed. You will find a number of 404 response codes, and if you drill into the one with a diagnostic ID of 14010, you’ll see the following:

To user URI: sip:00001234567;phone-context=dialplanname@sipdomain.com
Diagnostic header: 14010; reason=”Unable to find an exact match in the rules set”; source=”lyncfe”; CalledNumber=”00001234567“; ProfileName=”dialplanname“; appName=”TranslationService

So in this case, the user is attempting to foward to 00001234567, and Lync is trying to normalise this based on the rules associated with the dial plan listed under ProfileName. It can’t find a rule to match this number format, so fails and essentially rejects the number. From this error detail you can then evaluate the number and determine if the user has fat-fingered an impossible number, or whether perhaps you need to tweak your normalisation rules so this one matches.


JB / The Daywalker

Ginger IT dude hanging out down in New Zealand, playing with technology since ages ago.

Currently Service Delivery Manager at Silicon Systems, formerly Skype for Business MVP, and generally into all things Microsoft (and a few things that aren’t).

When I’m not nerding out on technology, you can find me running ultramarathons, brewing beer, or in my woodshop building something.


On The Socials

Visit Us On LinkedinVisit Us On TwitterVisit Us On Facebook