CRM Extensions

MS CRM 2011: Bulk refresh of user details from AD

When you create user in CRM all the available information is populated from AD into CRM user form. But in case information was changed in AD (email box, phone e.t.c.) – information will remain unchanged till the moment you will open form of user and change it. In case a lot of information was changed it will be quite boring to update users information one-by-one.

Following post describes how to allow bulk update of user details.

First step – JavaScript that will do update of data – create webresource, put inside following code, save and publish:

function RefreshUsersADInfo(selectedusers)
{
    var orgserviceurl = Xrm.Page.context.prependOrgName("/XRMServices/2011/Organization.svc/web");

    for(var i = 0; i < selectedusers.length; i++)
    {
        var domainname = GetDomainName(selectedusers[i], orgserviceurl);
        UpdateUserADInfo(selectedusers[i], domainname, orgserviceurl);
    }

    crmGrid.Refresh();
}

function UpdateUserADInfo(userid, domainname, orgserviceurl)
{
    var oCommand=new RemoteCommand("UserManager","RetrieveADUserProperties");
    if(oCommand!=null)
    {
        oCommand.SetParameter("domainAccountName", domainname);
        var oResult = oCommand.Execute();

        var request = "<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">"+
          "<s:Body>"+
            "<Update xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">"+
              "<entity xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">"+
                "<a:Attributes xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic">";

        if(oResult.Success&&!IsNull(oResult.ReturnValue)&&oResult.ReturnValue.length>0)
        for(var oUserXmlDoc=loadXmlDocument(oResult.ReturnValue),oNodeList=oUserXmlDoc.documentElement.childNodes,i=0;i<oNodeList.length;i++)
        {
            var oNode=oNodeList.item(i);

            request += "<a:KeyValuePairOfstringanyType>"+
            "<b:key>" + oNode.tagName + "</b:key>"+
            "<b:value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema">" + oNode.text + "</b:value>"+
            "</a:KeyValuePairOfstringanyType>";
        }

        request +="</a:Attributes>"+
        "<a:EntityState i:nil="true" />"+
        "<a:FormattedValues xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />"+
        "<a:Id>" + userid + "</a:Id>"+
        "<a:LogicalName>systemuser</a:LogicalName>"+
        "<a:RelatedEntities xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />"+
        "</entity>"+
        "</Update>"+
        "</s:Body>"+
        "</s:Envelope>";

        var req = new XMLHttpRequest();
        req.open("POST", orgserviceurl, false);
        req.setRequestHeader("Accept", "application/xml, text/xml, */*");
        req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
        req.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Update");
        req.send(request);
    }
}

function GetDomainName(userid, orgserviceurl)
{
    var request = "<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">"+
     "<s:Body>"+
       "<Retrieve xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">"+
         "<entityName>systemuser</entityName>"+
         "<id>" + userid + "</id>"+
         "<columnSet xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">"+
           "<a:AllColumns>false</a:AllColumns>"+
           "<a:Columns xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays">"+
             "<b:string>domainname</b:string>"+
           "</a:Columns>"+
         "</columnSet>"+
       "</Retrieve>"+
     "</s:Body>"+
    "</s:Envelope>";

    var req = new XMLHttpRequest();
    req.open("POST", orgserviceurl, false);
    req.setRequestHeader("Accept", "application/xml, text/xml, */*");
    req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    req.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Retrieve");
    req.send(request);

    var attributes = req.responseXML.getElementsByTagName("a:KeyValuePairOfstringanyType");
    if (attributes == null || attributes.length == 0)
        return null;

    for(var i = 0; i < attributes.length; i++)
    if (attributes[i].selectSingleNode("./b:key").text == "domainname")
    {
        var node = attributes[i].selectSingleNode("./b:value");
        if (node != null)
            return node.nodeTypedValue;
    }

    return null;
}

 

Second step – modify ribbon to add “Refresh” button to users grid. I prefer to use tools for it and here are screenshots:




Here is RibbonXml:

<RibbonDiffXml>
<CustomActions>
<CustomAction Id="xs.HomepageGrid.systemuser.MainTab.Management.refreshadinfo.CustomAction" Location="Mscrm.HomepageGrid.systemuser.MainTab.Management.Controls._children" Sequence="41">
<CommandUIDefinition>
<Button Id="xs.HomepageGrid.systemuser.MainTab.Management.refreshadinfo" Command="xs.HomepageGrid.systemuser.MainTab.Management.refreshadinfo.Command" Sequence="96" ToolTipTitle="$LocLabels:xs.HomepageGrid.systemuser.MainTab.Management.refreshadinfo.LabelText" LabelText="$LocLabels:xs.HomepageGrid.systemuser.MainTab.Management.refreshadinfo.LabelText" ToolTipDescription="$LocLabels:xs.HomepageGrid.systemuser.MainTab.Management.refreshadinfo.Description" TemplateAlias="o2" Image16by16="/_imgs/grid/refresh16.gif" />
</CommandUIDefinition>
</CustomAction>
</CustomActions>
<Templates>
<RibbonTemplates Id="Mscrm.Templates"></RibbonTemplates>
</Templates>
<CommandDefinitions>
<CommandDefinition Id="xs.HomepageGrid.systemuser.MainTab.Management.refreshadinfo.Command">
<EnableRules>
<EnableRule Id="xs.HomepageGrid.systemuser.MainTab.Management.refreshadinfo.Command.EnableRule.SelectionCountRule" />
</EnableRules>
<DisplayRules />
<Actions>
<JavaScriptFunction Library="$webresource:xs_refreshusers.js" FunctionName="RefreshUsersADInfo">
<CrmParameter Value="SelectedControlSelectedItemIds" />
</JavaScriptFunction>
</Actions>
</CommandDefinition>
</CommandDefinitions>
<RuleDefinitions>
<TabDisplayRules />
<DisplayRules />
<EnableRules>
<EnableRule Id="xs.HomepageGrid.systemuser.MainTab.Management.refreshadinfo.Command.EnableRule.SelectionCountRule">
<SelectionCountRule Minimum="1" AppliesTo="SelectedEntity" />
</EnableRule>
</EnableRules>
</RuleDefinitions>
<LocLabels>
<LocLabel Id="xs.HomepageGrid.systemuser.MainTab.Management.refreshadinfo.LabelText">
<Titles>
<Title languagecode="1033" description="Refresh" />
</Titles>
</LocLabel>
<LocLabel Id="xs.HomepageGrid.systemuser.MainTab.Management.refreshadinfo.Description">
<Titles>
<Title languagecode="1033" description="Refreshes Users' Info from AD" />
</Titles>
</LocLabel>
</LocLabels>
</RibbonDiffXml>

In the case you have performed everything correctly in users ribbon you will find new button:

Functions that are used for fetching of users’ information are undocumented so I assume that in common this customization is unsupported.

I want to say special thanks to my friend Artem Grunin who is former MVP and employee of Microsoft. Here is his article regarding similar issue for CRM 4.0 – http://fixrm.wordpress.com/2011/02/02/how-to-update-crm-user-profile-from-active-directory/

5 Comments

  1. I implemented per instructions – removed Main Phone and email address from a user (info came from AD during original import) – refreshed user – Main Phone and email address were NOT updated.

  2. Hi there,

    I’ve also implemented the JS and button step by step in CRM 2016 but the functionality is not working as you’ve explained.

    Could you please revisit it and see if any changes are required in your code.

    1. Hello Kumar,
      I did a quick check and it looks like payload and endpoint used are still the same in CRM 2016 but there is a high chance that this code (written when CRM 2011 supported only IE) would not work now. I believe to make it work you will have to rework send of request and parsing of response pieces. Also if I’m not wrong Xrm.Page.context.prependOrgName method was deprecated and not available now so you will have to replace it with Xrm.Page.context.getClientUrl. Fell free to ask any questions and share with your progress.
      Thanks,
      Andrew

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.