Trying to create a new Active Directory user, Invoke(“SetPassword”,pwd) throws “The RPC server is unavailable”

StackOverflow https://stackoverflow.com/questions/3954819

Pergunta

I'm trying to create a new user on my development active directory server using .NET System.DirectoryServices namespace.

I try using the following code:

DirectoryEntry dirEntry = new DirectoryEntry(path, "TESTDOM\\Administrator", "2109password", AuthenticationTypes.Secure | AuthenticationTypes.ServerBind);

object o = dirEntry.NativeObject;
DirectoryEntry newUser = dirEntry.Children.Add("CN=NewUser4", "user");
newUser.Properties["samAccountName"].Value = "NewUser4";
newUser.Properties["Description"].Add("User Description");

newUser.Invoke("SetPassword",  new object[] {"2109password"} );
newUser.CommitChanges();

I also tried committing using

newUser.CommitChanges();

before I call the Invoke to set the password. I always get a TargetInvocationException wrapping:

InnerException {"The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)"} System.Exception {System.Runtime.InteropServices.COMException}

The exception is always only thrown when I call

newUser.Invoke("SetPassword",  new object[] {"2109password"} );

If I call newUser.CommitChanges() before I try to call Invoke with SetPassword, the new user is created on the domain. I can then go manually to the AD machine and set the same password with no problems (so it's not a problem with the password string being against the rules). I've notice many post online about this but found no solution.

I think it might have something to do with the fact that the machine running the code is not a member in the domain. Although the user TESTDOM\Administrator is a member of the: administrators, domain admins, schema admin and enterprise admins groups on the TESTDOM domain.

Notice that I can't use System.DirectoryServices.AccountManagement namespace as I'm working with .NET 2 Any ideas on what can I do to solve this? I am desperate

Foi útil?

Solução 2

OK, I got it working:

 dirEntry = new DirectoryEntry(ldapPath, domainAdminUser, domainAdminPassword);
    dirEntry.Invoke("SetPassword", new object[] { newPassword });
    dirEntry.Properties["LockOutTime"].Value = 0; //unlock account

ldapPath should include the full DN of the user we're trying to change , so it should look something like:

string ldapPath = "LDAP://ad.domain.com:389/CN=username,OU=Users,DC=ad,DC=domain,DC=com"

Outras dicas

I guess you need to create the user first before setting properties to it, I usually do it by

/// <summary>
/// This Method will Create a new User Directory Object based on a Username and LDAP Domain
/// </summary>
/// <param name="sUserName">The Username of the New User</param>
/// <param name="sLDAPDomain">The LDAP Domain for the New User</param>
/// <returns></returns>
public DirectoryEntry CreateNewUser(string sUserName, string sLDAPDomain)
{
    //Set the LDAP qualification so that the user will be Created under the Users Container
    string LDAPDomain = "/CN=Users," + sLDAPDomain;
    oDE = new DirectoryEntry("LDAP://" + sADServer + "/" + sLDAPDomain, sADUser, sADPassword, AuthenticationTypes.Secure);

    oDEC = oDE.Children.Add("CN=" + sUserName, "user");
    oDE.Close();
    return oDEC;
}

then set any properties I need

/// <summary>
/// This will Set the Property of the Directory Entry Object
/// </summary>
/// <param name="oDE">The Directory Object to Set to</param>
/// <param name="sPropertyName">The Property Name</param>
/// <param name="sPropertyValue">The Property Value</param>
public void SetProperty(DirectoryEntry oDE, string sPropertyName, string sPropertyValue)
{
    //Check if the Value is Valid
    if (sPropertyValue != string.Empty)
    {
        //Check if the Property Exists
        if (oDE.Properties.Contains(sPropertyName))
        {
            oDE.Properties[sPropertyName].Value = sPropertyValue;
            oDE.CommitChanges();
            oDE.Close();
        }
        else
        {
            oDE.Properties[sPropertyName].Add(sPropertyValue);
            oDE.CommitChanges();
            oDE.Close();
        }
    }
}

then set the password

/// <summary>
/// This Method will set the Users Password based on Directory Entry Object
/// </summary>
/// <param name="oDE">The Directory Entry to Set the New Password</param>
/// <param name="sPassword">The New Password</param>
/// <param name="sMessage">Any Messages catched by the Exception</param>
public void SetUserPassword(DirectoryEntry oDE, string sPassword, out string sMessage)
{
    try
    {
        //Set The new Password
        oDE.Invoke("SetPassword", new Object[] { sPassword });
        sMessage = "";

        oDE.CommitChanges();
        oDE.Close();
    }
    catch (Exception ex)
    {
        sMessage = ex.InnerException.Message;
    }

}

And finally enable the account

/// <summary>
/// This Method will Enable a User Account Based on the Directory Entry Object
/// </summary>
/// <param name="oDE">The Directoy Entry Object of the Account to Enable</param>
public void EnableUserAccount(DirectoryEntry oDE)
{
    oDE.Properties["userAccountControl"][0] = ADMethods.ADAccountOptions.UF_NORMAL_ACCOUNT;
    oDE.CommitChanges();
    oDE.Close();
}

For a full implementation you can go here -> http://anyrest.wordpress.com/2010/02/01/active-directory-objects-and-c/

You are right that you need to create the account first and then call SetPassword. You can try to use Invoke SetInfo instead of CommitChanges and then SetPassword.

If that doesn't work as well my guess is that you are not setting a required property such as displayName.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top