Monday, March 11, 2013

Lync Server 2013 - Bulk Updating Contact Groups

I have been working on a rollout of Lync 2013 at my company over the last couple weeks. We are moving from an OpenFire/Spark setup to Lync mostly because we wanted to give our users the ability to do video chats and screen share's easily and Spark did not really provide this functionality for us.

The biggest thing we found lacking in Lync 2013 is no central management of Contact Groups. With Spark we setup the groups and they are loaded whenever a user signs on.



Our users really like having this list and we did not want them to have to manually configure all of the groups. I was not able to find any specifics of exactly how to do this on the web so I decided to roll up my sleeves and do it myself.  There are a few more things I would like to add to it and clean up but I wanted to get it out in the wild for other people to use.

Currently this script will only update the Contact Groups for a user who has logged into the server at least once. Additionally if the user has already added some of the groups in the past it will retain the original order of the previously added groups and append the new ones on at the bottom.

You will need 7-Zip installed on your link server to run this script.

Here are the basic steps to use the script:
  1. Create a reference account and add the Contact Groups you want to load for your other users.

  2. Log into your Lync Server and from the Lync Management Shell export the user you updated with the following command.

    Export-CsUserData -PoolFqdn "YOURLINKPOOL" -UserFilter first.last@something.com -FileName "c:\temp\ExportedUserData.zip"
    

  3. Extract the Zip file and open the DocItemSet.xml file.  I recommend using Notepad++ with the XML Tools Plug to format that file into something readable.

    We are interested in the "ContactGroups" XML node.  Each ContactGroup entry in this list corresponds to one of the contact groups you added to the reference account.  Copy and paste this out of the XML file.  It should look something like this:

     
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
     
    
  4. You're ready to copy the script below. Update variables as you see fit in the the Settings section on top. You will definitely have to update the following variables:

    $lyncpoolfqdn - to your own FQDN

    $replacement - paste your default ContactGroups XML in place of my sanitzed version


##########################################################################################################################
#
#    Name:            Update_Lync_2013_Groups.ps1    
#    Author:         Charles Ulrich
#    Date:            03/08/2013
#    Description:    Script to update the Contact List for 1 or All Lync 2013 users.
#
#    Requirements:    1. MUST BE RUN ON LYNC SERVER - Export-CSUserData does not like Remote Shell or Implicit Remoting
#                    2. 7-Zip must be installed - Used to manage the ZIP files from the export and needed for the import.
#                    3. If not running under Powershell v3.0 see comments in MultipleSelectionBox function.
#
#
##########################################################################################################################

# Settings

#Lync Server
$lyncpoolfqdn = "YOURLINKSERVERPOOL"

#Base File Path
$BaseFilePath = "c:\scripts\lync_user_export"

#7-Zip Paths
$7ZipPath = "c:\Program Files\7-zip\7z.exe"
$7ZipOutputDir = $BaseFilePath + "\working"
$7ZipOutputParam = "-o" + $7ZipOutputDir
$7ZipIncludeFiles = $7ZipOutputDir + "\*.xml"

#Lync XML and ZIP file paths
$LyncXMLFile = $7ZipOutputDir + "\DocItemSet.xml"
$ExportFileNamePath = $BaseFilePath + "\ExportedUserData.zip"
$UpdatedFileNamePath = $BaseFilePath + "\UpdatedUserData.zip"

# replacement node with child nodes
[xml]$replacement = @'
    <ContactGroups>
      <ContactGroup Number="1" DisplayName="fg=="/>
      <ContactGroup Number="2" DisplayName="WJvopsjer3ojfaopj" ExternalUri="Pfaohiweifgouwe90ufjSDAW$jop3958osdkpodasok32480mVkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="3" DisplayName="SFDEW3523j" ExternalUri="Pfaohiweifgouwe90ufjSDAW$jop39357673425hdfbsart3462twqgaedfgajoisdfoipqhuy38tpy32u980hfiaosdhiogpkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="4" DisplayName="35uyh" ExternalUri="Pfaohiweifgouwe90ufjSDAW$jop3958osdkpodasok32480mVkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="5" DisplayName="fbdsfh346" ExternalUri="Pfaohiweifgouwe27y0ahsvokja;sdC9n3904kjlkJKSL904="/>
      <ContactGroup Number="6" DisplayName="sfdhw3466" ExternalUri="Pfaohvbasrtyq4tyasv58osdkpodasok32480mVkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="7" DisplayName="asetg4363y6" ExternalUri="Pfaohiweifgouwe90vsdgwerhjudfbsdfkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="8" DisplayName="634tsagasdg" ExternalUri="Pfaohidsvsdgherhysdkpodasok32480mVkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="9" DisplayName="ngfd56w3" ExternalUri="Pfaohiweifgouwe90ufjdasok32480mVkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="10" DisplayName="asdgsa46" ExternalUri="Pfaohiweifgouwe90ufSDfsdtewtgvjopaiwejr302582385ioskjgl;skdaopsok32480mVkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="11" DisplayName="agasg" ExternalUri="Pfaohiweifgouwe90ufjSDAW$jop3958osdkpodasok32480mVkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="12" DisplayName="235wgsdfa" ExternalUri="Pfaohiweifgouwe90ufjSDAW$jop3958osdkpodasok32480mVkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="13" DisplayName="236gvbasetaw" ExternalUri="Pfaohiweifgouwe90ufjSDAW$jop3958osdkpodasok32480mVkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="14" DisplayName="asdgawqt3aqw" ExternalUri="Pfaohiweifgouwe90ufjSDAW$jop3958osdkpodasok32480mVkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="15" DisplayName="casdrwqt5" ExternalUri="Pfaohiweifgouwe90ufjSDAW$jop3958osdkpodasok32480mVkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
    </ContactGroups>
'@


Function MultipleSelectionBox ($inputarray,$prompt,$listboxtype) {

# Taken from Technet - http://technet.microsoft.com/en-us/library/ff730950.aspx
# This version has been updated to work with Powershell v3.0.
# Had top replace $x with $Script:x throughout the function to make it work. 
# This specifies the scope of the X variable.  Not sure why this is needed for v3.
# http://social.technet.microsoft.com/Forums/en-SG/winserverpowershell/thread/bc95fb6c-c583-47c3-94c1-f0d3abe1fafc

$Script:x = @()

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 

$objForm = New-Object System.Windows.Forms.Form 
$objForm.Text = $prompt
$objForm.Size = New-Object System.Drawing.Size(300,600) 
$objForm.StartPosition = "CenterScreen"

$objForm.KeyPreview = $True

$objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter") 
    {
        foreach ($objItem in $objListbox.SelectedItems)
            {$Script:x += $objItem}
        $objForm.Close()
    }
    })

$objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape") 
    {$objForm.Close()}})

$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,520)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"

$OKButton.Add_Click(
   {
        foreach ($objItem in $objListbox.SelectedItems)
            {$Script:x += $objItem}
        $objForm.Close()
   })

$objForm.Controls.Add($OKButton)

$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(150,520)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.Add_Click({$objForm.Close()})
$objForm.Controls.Add($CancelButton)

$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,20) 
$objLabel.Size = New-Object System.Drawing.Size(280,20) 
$objLabel.Text = "Please make a selection from the list below:"
$objForm.Controls.Add($objLabel) 

$objListbox = New-Object System.Windows.Forms.Listbox 
$objListbox.Location = New-Object System.Drawing.Size(10,40) 
$objListbox.Size = New-Object System.Drawing.Size(260,20) 

$objListbox.SelectionMode = $listboxtype

$inputarray | ForEach-Object {[void] $objListbox.Items.Add($_)}

$objListbox.Height = 470
$objForm.Controls.Add($objListbox) 
$objForm.Topmost = $True

$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()

Return $Script:x
}


#Load Lync Powershell Commands
Import-Module Lync

#Who should we update
$sora = Read-Host "Do you want to update (A)ll users or a (S)ingle user? (S or A)"

If ($sora -eq "S")
    {
        # Get user to update 
        $userlist = Get-CSUser
        $user_email=MultipleSelectionBox $userlist.UserPrincipalName "Choose Lync 2013 User" "One"
        
        #Export Single Users Data
        Export-CsUserData -PoolFqdn $lyncpoolfqdn -UserFilter $user_email -FileName $ExportFileNamePath
    }
Else
    {
        #Export All Users Data
        Export-CsUserData -PoolFqdn $lyncpoolfqdn -FileName $ExportFileNamePath
    }

#Extract the Exported Zip file. Requires 7-Zip
Write-Host " "
&$7ZipPath e $ExportFileNamePath $7ZipOutputParam
Write-Host " "
Write-Host " "
$original = [xml] (Get-Content $LyncXMLFile)

#Set our loop counter to 0
$count = $original.DocItemSet.DocItem.Count + 1

Write-Host "########################################################################"

#Loop through all DocItem Elements and replace any with ContactGroups in them
For ($i=0; $i -lt $count; $i++) {
        If (($original.DocItemSet.DocItem[$i].Data.HomedResource.ContactGroups.ContactGroup.Count -gt 0)) 
            {
                # get the target node
                
                Write-Host " "
                Write-Host "Working on XML Node: " $original.DocItemSet.DocItem[$i].Name
                Write-Host " "
                Write-Host "Contact Groups Before: "$original.DocItemSet.DocItem[$i].Data.HomedResource.ContactGroups.ContactGroup.Count
                Write-Host " "
                $inner = $original.DocItemSet.DocItem[$i].Data.HomedResource.ContactGroups
                
                # import the replacement values
                $new = $original.ImportNode($replacement.ContactGroups, $true)
                
                # replace old node with new one (replacement node)
                $dump = $original.DocItemSet.DocItem[$i].Data.HomedResource.ReplaceChild($new, $inner)
                
                Write-Host "Contact Groups After: "$original.DocItemSet.DocItem[$i].Data.HomedResource.ContactGroups.ContactGroup.Count
        }
    }

Write-Host " "
Write-Host "########################################################################"
Write-Host " "

#Remove blank xmlns tags created by importing the node
$original = [xml] $original.OuterXml.Replace(" xmlns=`"`"", "")

# save changes (full path to file)
$original.Save($LyncXMLFile)

# create updated zip file
& $7ZipPath a $UpdatedFileNamePath $7ZipIncludeFiles

Write-Host " "
Write-Host "########################################################################"
Write-Host " "
Write-Host "The XML file has been updated with the default groups."
Write-Host " "
Write-Host "If you want to take a look at the file its path is"
Write-Host " " 
Write-Host $LyncXMLFile
Write-Host " "
Write-Host "This file will be deleted once this script has finished."
Write-Host " "
Write-Host "########################################################################"
Write-Host " "

$sure = Read-Host "About to upload the changes to the server.  Are you sure? (Y or N)"

If ($sure -eq "Y")
    {
        Write-Host " "
        Write-Host "Updating Server with the new Contact Group Settings."
        Write-Host " "
        Write-Host "The user(s) will have no Contact groups until they" 
        Write-Host "log off and back on to Lync."
        
        If ($sora -eq "S")
            {
                # Update the server with the new User Data
                Update-CsUserData -Filename $UpdatedFileNamePath -UserFilter $user_email
            }
        Else
            {
                # Update the server with the new User Data
                Update-CsUserData -Filename $UpdatedFileNamePath
            }
        Write-Host " "
    }
Else
    {
        Write-Host " "
        Write-Host "Update Aborted!!!!!!"
        Write-Host " "
        Write-Host "Please Come Again!  :) " 
        Write-Host " "
    }

Write-Host "I will clean up the files and close the window after you hit Enter."
Write-Host " "

PAUSE

# Clean Up
Remove-Item ($BaseFilePath + "\*") -recurse

54 comments:

  1. hi, nice work, I can manage&copy contact groups for all my lync users.
    In addition, can we copy contact groups and its members?

    ReplyDelete
  2. I am not positive but I am willing to bet the script could be tweaked a bit to handle that.

    In order to make the contact list dynamic I tied them to AD Groups so that as we add new people they get added to the list automatically. If you built out specific groups and changes would have to pushed out to everyone via a new script run.

    ReplyDelete
  3. thanks, great tool, but it would be really intresting to know how to export not only groups, but also its members. any ideas?

    ReplyDelete
    Replies
    1. The nice part about doing it this way is that the groups are all controlled by Active Directory membership. This way you don't have to repopulate this list over and over again.

      But, if you want to manually create groups then add users to them the next section of the XML file would be of interest. It follows directly after the ContactGroups Node and is named Contacts.

      I would encourage you to do the XML export command for a user that you pre-configure and take a look.

      Delete
  4. Hello Charles. Nice work. I see a nice effect with users have UPN unequal the SIP Address, they user UPN not SIP Domain suffix shown in the selection window.

    I think this row include the logic to present the UPN

    # Get user to update
    $userlist = Get-CSUser
    $user_email=MultipleSelectionBox $userlist.UserPrincipalName "Choose Lync 2013 User" "One"

    Have you an Idea how I could add the SIP Domain Suffix instead the UPN Suffix?

    Cheers
    Octavian

    ReplyDelete
    Replies
    1. You can see all of the available parameters by piping your output to Select-Object *

      Try this in the Lync Server Management Shell:

      #######################################################

      #Load your users
      $userlist = Get-CSUser

      #Print out the full details of the first user in the $userlist array
      $userlist[0] | Select-Object *

      #######################################################

      Based on this output you can tell the SIP address is stored in "SipAddress".

      So replace the $user_email line with:

      $user_email=MultipleSelectionBox $userlist.SipAddress "Choose Lync 2013 User" "One"

      Delete
    2. working with:

      $user_email=MultipleSelectionBox $userlist.WindowsEmailAddress "Choose Lync 2013 User" "One"

      Delete
  5. I am not sure how you tied the Groups to AD. I can't find it anywhere in the code.

    ReplyDelete
    Replies
    1. Lync does this automatically. When you are in the contact list search for an AD distribution group. Right click it and choose "Add to Contacts List". This will add the AD Group as a Lync Contact group and it will stay updated based on the membership of that group.

      Delete
    2. OK, that explains it. We still don’t run Exchange, so we have some problems with all connections.

      I made some changes to your script to be able to add groups to new contacts.
      In the loop I added a check for the name:
      If ($original.DocItemSet.DocItem[$i].Name -like "urn:lcd:*")
      {
      and then I added an Else-statement
      Else
      {
      $new = $original.ImportNode($replacement.ContactGroups, $true)
      $original.DocItemSet.DocItem[$i].Data.HomedResource.InsertBefore($new,$original.DocItemSet.DocItem[$i].Data.HomedResource.FirstChild)
      Write-Host "Created Contact Groups : "$original.DocItemSet.DocItem[$i].Data.HomedResource.ContactGroups.ContactGroup.Count
      }

      Delete
    3. I tried this modification to allow it to work for new users (never logged in), but it puts out bad XML. Does this not work if the user has logged in before?

      Delete
  6. Any idea how to add contacts to a brand new user? When I pull down the XML it's completely blank.

    ReplyDelete
    Replies
    1. You can use my changes above to enable Contacts for new users.

      Delete
  7. Hi,

    First off thank you for posting this script it is realy a great help!
    i'm now trying to get the script so it updates a users contact list rather than replace it on each run but seem to be having a mare getting the XML apend function to work and was wondering if somone could point me in the right direction?

    I am trying to do the above by running the following:
    $xml1 = New-Object XML
    $xml1.Load("D:\Scripts\OTL_Lync_ContactManager\Lync_User_export\ExportedUserData\DocItemSet.xml")
    ## Reads from Exported DocItemSet.xml

    $OldContacts = @($xml1.DocItemSet.DocItem.Data.HomedResource.ContactGroups.ContactGroup)[1]
    ## Using 1 rather than 0 as it contains Number isplayName ExternalUri
    $NewContacts = $OldContacts.clone()
    $NewContacts.Number = "99"
    ##Testing number
    $NewContacts.DisplayName = "IyMgTnlsb24gLSBFdmVyeW9uZQ=="
    $NewContacts.ExternalUri = "PGdyb3VwRXh0ZW5zaW9uIGdyb3VwVHlwZT0iZGciPjxlbWFpbD5ldmVyeW9uZUBueWxvbmNhcGl0YWwuY29tPC9lbWFpbD48L2dyb3VwRXh0ZW5zaW9uPg=="

    $xml1.DocItemSet.DocItem.Data.HomedResource.ContactGroups.AppendChild("$NewContacts")

    ## I think the issue is that it is trying to apend the new "ContactGroup" data into the "ContactGroups" Node rather than making a new "ContactGroup" node under "ContactGroups".

    Hope you can help

    Cheers

    James

    ReplyDelete
    Replies
    1. Sorry for the late reply. I have been in the middle of a crazy summer. I had a ton of trouble getting the XML to work the way I wanted it to as well. Where you able to figure it out?

      Delete
  8. Hello,

    Thanks for the script, this is great. One question though, you have either two options here: update one user, or update all users. Is the potential to selectively choose (some) users here?

    ReplyDelete
    Replies
    1. Sure. You would have to add another option. I would probably try to use a multiple selection box instead to to pick the users. Then in the case of some and not all users loop through the selected users exporting, updating, then importing them each 1 by 1.

      Delete
  9. I am having a weird issue that I do not seem to be able to find a solution for or in fact find anyone having the same problem. When I run the Export-CSUserData after adding my Distribution Groups to my test.user@domain.com I do not have the node in the XML file??? I have created several other test users, restarted the server, ran update-csaddressbook, ran update-csuserdatabase, gave it ample time to replicate and sync, no errors in the event log about anything and still no . Does anyone know why this would occur and how I can fix this? Thank you.

    ReplyDelete
  10. Hi,
    The User has not logged into Lync so the nodes have not been created,
    Unfortunately you cannot pre populate users.

    James

    ReplyDelete
    Replies
    1. Thank you for your reply. I have logged into lync with the test.user, that is how i added the distribution groups to the contact list. This test.user was going to be my "source" user that i was going to use for adding and organizing the distribution groups for the organization. Even in the OWA for the test.user i can see the groups added to the "Lync Contacts" section under settings. Then when i run the Export command no contactgroups node can be found in the xml i do not even see the contact node for the groups i just created manually for that user as mentioned above in a previous post by Charles UlrichApril 17, 2013 at 12:20 PM.

      Delete
  11. One question. When I'm adding Distribution group to my contact list I just see their e-mail address in the contact list, not description or name as you have on the second screenshot at the top of this page? But why? All Descriptions and Names are filled in...

    ReplyDelete
    Replies
    1. I am not sure. I did not do anything special to make it show the Group Name. My first guess would be there is some problem with the communication between Lync and AD.

      Delete
    2. Do you have Exchange in your infrastructure? How DG were created? With which tool?

      Delete
    3. Yes...Exchange 2010, AD and Forest at 2010 functional level.

      They are all mail enabled security groups so they were most likely created via Exchange Console.

      Delete
  12. It is really great.
    It is working fine. But if UCS (unified contact store) is enabled for a user, it doesn’t export the groups to that user. As a solution, I disabled the UCS from all users.

    ReplyDelete
  13. Hello,

    great script, but i get an error when i come to the import section? I get an error like this:

    Update-CsUserData : The input is not a valid Base-64 string as it contains a no
    n-base 64 character, more than two padding characters, or an illegal character
    among the padding characters.
    At V:\luncusers.ps1:225 char:1
    + Update-CsUserData -Filename $UpdatedFileNamePath -UserFilter $user_email
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [Update-CsUserData], Forma
    tException
    + FullyQualifiedErrorId : The input is not a valid Base-64 string as it co
    ntains a non-base 64 character, more than two padding characters, or an il
    legal character among the padding characters. ,Microsoft.Rtc.Management.AD
    .Cmdlets.ImportOcsUserDataCmdlet

    Any Ideas?

    ReplyDelete
  14. I suspect there is probably something wrong with your import file.

    It is failing on the Update-CSUserData command. I would try running just that line manually.

    ReplyDelete
  15. Is it possible to modify this script to Update all of the groups with the according users from the Exported file?
    And has anyone tried to do this?

    ReplyDelete
  16. Great Script for the Groups but I'm looking for the same thing as Reinis due to Lync 2011 for Mac not able to support AD Groups. any suggested on how I loop adding users into a script?

    Thanks

    ReplyDelete
  17. Hello Charles, would you have another script that can import a master contact list with user groups and all its members?
    I just tried your original script with Exchange Distribution groups and ran into two pit falls. 1) Our Lync 2013 Clients does not allow us to add more than 10 distribution groups 2) Users on the Lync client status tabs, don't appear online added from Distribution group. If you have another example script that would be great
    Thanks
    Dan

    ReplyDelete
  18. Great script Charles. And also thanks to raderarn for fix to add mannual groups with users. But here is the question how to delete user from list?

    ReplyDelete
  19. I adapted the original script to work with Contacts instead of groups.

    $replacement = [xml] @'





    '@

    #Target DocItem Node and replace with new Contacts
    $count = $original.DocItemSet.DocItem.Count + 1
    For ($i=0; $i -lt $count; $i++) {
    If (($original.DocItemSet.DocItem[$i].Data.HomedResource.ContactGroups.ContactGroup.Count -gt 0)) {

    Write-Host " "
    Write-Host "Working on XML Node: " $original.DocItemSet.DocItem[$i].Name
    Write-Host " "
    Write-Host "Contacts Before: "$original.DocItemSet.DocItem[$i].Data.HomedResource.Contacts.Contact.Buddy.Count
    Write-Host " "

    $inner = $original.DocItemSet.DocItem[$i].Data.HomedResource.Contacts

    #Import the replacement values
    $new = $original.ImportNode($replacement.Contacts, $true)

    #Replace old node with new one (replacement node)
    $dump = $original.DocItemSet.DocItem[$i].Data.HomedResource.ReplaceChild($new, $inner)

    Write-Host "Contacts After: "$original.DocItemSet.DocItem[1].Data.HomedResource.Contacts.Contact.Buddy.Count

    }
    Else {

    Write-Host "Error - No Contact Elements exist in this DocItem Node"

    }
    }

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
    2. This comment has been removed by the author.

      Delete
    3. Hi William,

      I've tried your modification above. But somehow i got following error:
      Exception calling "ReplaceChild" with "2" argument(s): "Object reference not set to an instance of an object."
      Any idea how to resolve this?

      Thanks

      Delete
    4. I´m geeting the same error as informed by Sully. But it happens only to a few users, others are running perfectly.

      Do you know what´s the problem?

      Best Regards,

      Delete
  20. The DocItem Elements were stripped out when I posted resulting in a blank entry. Basically, you populate with Contacts -- everything between and with each contact on a single line followed by and a CR/LF.

    ReplyDelete
  21. This code will update the Groups AND the Contacts for all users....WITHOUT the use of 7zip.
    Instead it uses the PCSX powershell module https://pscx.codeplex.com/ (which is a pre-req to use this script)

    I removed the single user option for this as I wanted to make all contact groups for the company the same but i'm sure you can rewrite to your needs.

    Download the zip from here and Read the ReadMe file.
    http://robertsmodernlife.com/blog/technology/lync-2013-bulk-contact-updating

    ReplyDelete
  22. There is a note that this must be run on the Lync server. Has anyone tried with Lync Online as part of Office 365?

    ReplyDelete
    Replies
    1. Have you found an online version solution?

      Delete
  23. exactly what i needed, thanks for sharing.

    ReplyDelete
  24. I had to adapt the script for my environment, but it worked perfectly.

    Tks for share it.

    ReplyDelete
  25. One thing that a realized, if the user if recent created and the account was never logged on it, the script does not work.
    The reason of that is because when you export the csdata the file does not contain the node and the script cannot replace a node that it didn´t find.

    ReplyDelete
  26. Hi Charles!

    When script tries to update server with new Contact Group settings, following exceptions are thrown:
    "Due to DCOM-error connection to server %Here_comes_my_server(pool)_fqdn% can't be established. Check if Front-End-Service for this pool is running"
    Of course, the service is running and pool is functional.

    How can I bypass this error?
    Thanks in advance
    Victor

    ReplyDelete
  27. I ran the command to update all users.. but i seem to be getting an error for some on Update-CSUserData: Out of objects in the pool, the quota was reached.. any ideas why that might be?

    ReplyDelete
    Replies
    1. I also occassionally get an error when i try this for a single user.. Update-CSUserData -Filename $updatedfilenamepath -userfilter $user_email Invalidoperation comexception HRESULT 0xc3ee7950

      Delete
    2. Also.. some minutes later.. often the same user that failed will then work.. very strange.. and in addition.. the update option i thought didnt erase custom groups people have made, but it does.

      Delete
  28. Hi Charlie, I'm trying to use your script to import the contacts in Skype for Business, but I imported only a group of contacts. They are empty. How do I imortirovat also contact these groups?

    ReplyDelete
    Replies
    1. Anton, Sorry but I am not sure that this will work at all in Skype for Business. I have not used this code myself since late 2013. However please post back here if you figure it out as this page still sees lots of traffic.

      Delete
    2. I have just tried this with SfB and still works a treat!... Just make sure you run it in Windows Powershell and not the SfB Powershell.

      Delete
  29. I have discovered, that if you are running into errors (HRESULT), etc... If you manually delete the current contacts/groups from the users list, and attempt to run this script, it works flawlessly.

    The other alternative, is to disable/re-enable the user from the SfB Management Shell. The only crappy thing about that, is that you will then need to login the user at least once, to update their list.

    I have tested, and this is working with SfB 2015. Wonderful script, my friend!

    ReplyDelete