Three years ago I published one of my first blog articles detailing how to clean up leftover Lync and Skype for Business user accounts, and it has become one of my most viewed posts. Included at the bottom of that post is a small script example to show how to perform this task across multiple servers. I have a version of a longer script to do all this, but it was one of the first major PowerShell scripts I wrote, and, to be honest, was not that great. I finally took the time to re-write it to add some error checking and not rely on the SQL PowerShell module. The script is now available here on my GitHub repo under the name Remove-DuplicateSipAddress.ps1, and this post will go over how to use it.
I recommend running this script on a Lync or Skype for Business Server directly. This will make it easier for using some of the management cmdlets as well as making sure you have administrative rights to the other servers and SQL databases. A remote PowerShell session would be acceptable as well. The script will check to see if these modules are loaded and available, and if not, the script will exit, like this:
Currently the script only has one parameter: SipAddress. This is a required parameter and should be the SipAddress that you want to search for to remove. The script will search and see if the SipAddress is currently attached to an active user account. If it is, the script will prompt to disable and remove the user account by running Disable-CsUser, which will result in data loss for the user (unless you have exported user data prior to removing the account).
If the SipAddress is not currently attached to a user account, the script will let you know that the SipAddress is not currently attached to a user account, but you can continue to run the script if you want.
Once the account has been removed and verified as disabled, the script will start searching the RTCLocal instance on each Standard Edition, Front End, and Director server in the environment to see if the account still exists. If it finds a match, it will attempt to remove the account using the RtcDeleteResource stored procedure.
The main difference between this script and the small example provided in the aforementioned blog post is this script does not use the Invoke-SqlCmd cmdlet from the SQL PowerShell module. I wanted to write the script in such a way that it does not depend on a PowerShell module that may not be installed or available on the system. Instead, I relied on .NET framework components to create the SQL connection and execute the queries and stored procedures. This adds quite a bit of code and complexity to the script, but I hope this is the correct direction to go. The script does rely on the Lync or Skype for Business Management Shell modules to be installed only because it makes use of the Get-CsUser and Disable-CsUser cmdlets.
One disadvantage I encountered with using .NET to create the SQL connections to remote servers was the local Windows firewall blocking the connection attempt. If you encounter an error in the script when trying to run it against remote SQL instances, make sure the UDP port 1434 is open as well as allowing the SQLServr.exe process for the RTCLocal instance. The executable is located in the installation path for SQL Express for the RTCLocal instance, for example, C:\Program Files\Microsoft SQL Server\MSSQL12.RTCLOCAL\MSSQL\Binn. This was for SQL Server Express 2014 for Skype for Business Server 2015 and will probably be slightly different for Lync Server 2013 or if you pre-installed SQL Express to a different directory. If you encounter this issue, you should see a warning output like this:
I only tested this script against three Standard Edition servers running Skype for Business Server 2015. The SQL queries are valid for Lync Server 2013 server and across Front End and Director servers. As usual, run this and other scripts at your own risk. There is probably some redundancies and optimization I could probably do to this script. If you see any, feel free to leave me a comment below, or find my email address in the help section of the script. I will continue to update and make changes to the script and publish changes out on GitHub and TechNet Gallery.