Skip to content

Instantly share code, notes, and snippets.

@sheagcraig
Last active July 15, 2021 17:48
Show Gist options
  • Save sheagcraig/4bfb6249faf1ac413508f23dec448cd3 to your computer and use it in GitHub Desktop.
Save sheagcraig/4bfb6249faf1ac413508f23dec448cd3 to your computer and use it in GitHub Desktop.
Uninstall Microsoft Lync

UninstallLync

According to Microsoft, Lync must be completely uninstalled, following the procedures documented at https://technet.microsoft.com/en-us/library/jj945448(v=office.14).aspx, at least for Calendar functionality to work correctly.

This repo contains a python script for removing all referenced Lync components for all normal users on a machine, i.e., with homes in /Users.

It is somewhat naive in that it assumes the users have not moved the Lync keychain items out of the Login keychain and into some other keychain. It handles the potential for multiple "[email protected]" certificates in the login keychain, however. This is all done by running the security command as a subprocess. Improvements could probably be made to do this all with the Security Framework and the PyObjC bridge, but this gets the job done.

Using UninstallLync in Your Environment

You will need to edit the pattern global APP_PW_PATTERN at the top of the uninstall_lync.py script to look for your environment's email domain. Again, this could be made more generic by just building a pattern that accepts all email addresses as part of the OC_KeyContainer__<email address> construction that Lync uses, but it gets the job done. Feel free to send me a better implementation! If you're of the "get it done" mentality, just edit this pattern to use your domain. Examples are included of how to regex search for multiple email domains as well.

#!/bin/bash
makepkginfo --name="UninstallLync" \
--displayname="Uninstall Microsoft Lync" \
--description="Skype for Business requires a complete removal of all Microsoft Lync components and data. This uninstaller takes care of cleanly removing everything." \
--pkgvers="1.0" \
--installcheck_script="installcheck.sh" \
--postinstall_script="uninstall_lync.py" \
--nopkg \
--catalog="phase3"
#!/bin/bash
if [[ -e "/Applications/Microsoft Lync.app" ]]; then
exit 0
else
exit 1
fi
#!/usr/bin/python
# Based on:
# https://technet.microsoft.com/en-us/library/jj945448(v=office.14).aspx
import glob
import os
import re
import shutil
import subprocess
FILES_TO_REMOVE = (
"/Applications/Microsoft Lync.app",
"/Users/*/Library/Preferences/com.microsoft.Lync.plist",
"/Users/*/Library/Preferences/ByHost/MicrosoftLyncRegistrationDB.*.plist",
"/Users/*/Library/Logs/Microsoft-Lync-x.log",
"/Users/*/Library/Logs/Microsoft-Lync.log",
"/Users/*/Documents/Microsoft User Data/Microsoft Lync Data",
"/Users/*/Documents/Microsoft User Data/Microsoft Lync History",
"/Library/Internet Plug-Ins/MeetingJoinPlugin.plugin")
# Edit to include your company's email domain
# e.g.:
# APP_PW_PATTERN = r".*(OC_KeyContainer__.*@tacos.com).*"
# Multiple domains:
# APP_PW_PATTERN = r".*(OC_KeyContainer__.*@(?:tacos|burritos).com).*"
APP_PW_PATTERN = r".*(OC_KeyContainer__.*@(?:sas|jmp).com).*"
def main():
# Get Lync unloaded
try:
subprocess.check_call(["killall", "Microsoft Lync"])
except subprocess.CalledProcessError:
pass
# Remove easy stuff
for removal in FILES_TO_REMOVE:
removals = glob.glob(removal)
for removal in removals:
if os.path.isdir(removal):
shutil.rmtree(removal, ignore_errors=True)
else:
try:
os.remove(removal)
except OSError:
pass
# Remove keychain stuff
keychains = glob.glob("/Users/*/Library/Keychains/login.keychain*")
for keychain in keychains:
try:
dump = subprocess.check_output(["security", "dump-keychain",
keychain])
except subprocess.CalledProcessError:
continue
lync_items = set()
for line in dump.splitlines():
match = re.search(APP_PW_PATTERN, line)
if match:
lync_items.add(match.group(1))
for item in lync_items:
print "Removing {} from {}.".format(item, keychain)
try:
subprocess.check_call(
["security", "delete-generic-password", "-a", item,
keychain])
except subprocess.CalledProcessError as err:
print err.message
email_address = None
if lync_items:
search_string = lync_items.pop()
email_address = search_string.split("__")[1]
certs_remaining = True
while certs_remaining:
try:
issuer_check = subprocess.check_output(
["security", "find-certificate", "-Z", "-c",
email_address, keychain])
except subprocess.CalledProcessError as err:
if err.returncode == 44:
certs_remaining = False
continue
if search_string in issuer_check:
print "Trying to delete the email addy cert"
# Get the hash to use in identifying the cert for removal.
search = [line for line in issuer_check.splitlines() if
"SHA-1 hash" in line]
if search:
cert_hash = search[0].partition(":")[2].strip()
try:
subprocess.check_call(
["security", "delete-certificate", "-Z", cert_hash,
keychain])
except subprocess.CalledProcessError as err:
print err.message
# Remove keychain db items
for item in glob.glob("/Users/*/Library/Keychains/OC_KeyContainer__*"):
print "Removing keychain folder item {}".format(item)
try:
os.remove(item)
except OSError:
pass
if os.path.exists("/usr/local/bin/dockutil"):
try:
subprocess.check_call(["dockutil", "--remove", "Microsoft Lync",
"--allhomes"])
except subprocess.CalledProcessError as err:
print err.message
if __name__ == "__main__":
main()
@childrss
Copy link

childrss commented Nov 6, 2018

Hey Shea --

Thanks so much for the code sharing. I noticed that it did not delete the package installer receipts out of /var/db/receipts so I added that code as well as code in the installcheck.sh to clean up those systems where this had already ran but had not gotten the receipts.

Unfortunately there is no pull request on gist.github, so if you could take a look at my fork and copy and and paste it in. For the uninstall_lync.py, all of the additions are between the last import command and FILES_TO_REMOVE.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment