繁体   English   中英

如何更改几个Active Directory用户密码?

[英]How can I change several Active Directory user passwords?

我正在尝试以编程方式更改多个用户的密码,特别是在不使用System.DirectoryServices.AccountManagement(PrincipalContext)的情况下,我有这段工作代码:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.DirectoryServices;

namespace ADScriptService.core
{
    class ExportTool
    {
        const AuthenticationTypes = AuthenticationTypes.Secure | AuthenticationTypes.Sealing | AuthenticationTypes.ServerBind;
        private static DirectoryEntry directoryEntry = new DirectoryEntry(ADScriptService.Properties.Settings.Default.ActiveDirectoryPath, ADScriptService.Properties.Settings.Default.ServerAdminUser, ADScriptService.Properties.Settings.Default.ServerAdminPwd, AuthenticationTypes.Secure);
        private static DirectorySearcher search = new DirectorySearcher(directoryEntry);
    public void Export()
    {

        string path = ADScriptService.Properties.Settings.Default.ActiveDirectoryPath;
        string adminUser = ADScriptService.Properties.Settings.Default.ServerAdminUser;
        string adminPassword = ADScriptService.Properties.Settings.Default.ServerAdminPwd;

        string userName = "exampleUser";
        string newPassword = "P455w0rd";
        try
        {
            search.Filter = String.Format("sAMAccountName={0}", userName);
            search.SearchScope = SearchScope.Subtree;
            search.CacheResults = false;
            SearchResult searchResult = search.FindOne();
            if (searchResult == null) Console.WriteLine("User Not Found In This Domain");
            DirectoryEntry userEntry = searchResult.GetDirectoryEntry();

            userEntry.Path = userEntry.Path.Replace(":389", "");
            Console.WriteLine(String.Format("sAMAccountName={0}, User={1}, path={2}", userEntry.Properties["sAMAccountName"].Value, userEntry.Username, userEntry.Path));
            userEntry.Invoke("SetPassword", new object[] { newPassword });
            userEntry.Properties["userAccountControl"].Value = 0x0200 | 0x10000;
            userEntry.CommitChanges();
            Console.WriteLine("Se ha cambiado la contraseña");


        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }
}
}

这是一个只有一个用户的示例,但是我的程序应该做的是遍历约120k个用户。 但是,设置搜索过滤器,查找一个结果并获取DirectoryEntry的操作每个用户大约需要2到3秒钟,因此我试图使用DirectoryEntry.Children属性提供的DirectoryEntries结构,这意味着在之后替换六行仅使用DirectoryEntry userentry = directoryEntry.Children.Find("CN=" + userName); “ try {” DirectoryEntry userentry = directoryEntry.Children.Find("CN=" + userName);

因此,示例代码如下所示:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.DirectoryServices;

namespace ADScriptService.core
{
    class ExportTool
    {
        const AuthenticationTypes = AuthenticationTypes.Secure | AuthenticationTypes.Sealing | AuthenticationTypes.ServerBind;
        private static DirectoryEntry directoryEntry = new DirectoryEntry(ADScriptService.Properties.Settings.Default.ActiveDirectoryPath, ADScriptService.Properties.Settings.Default.ServerAdminUser, ADScriptService.Properties.Settings.Default.ServerAdminPwd, AuthenticationTypes.Secure);
        private static DirectorySearcher search = new DirectorySearcher(directoryEntry);
    public void Export()
    {

        string path = ADScriptService.Properties.Settings.Default.ActiveDirectoryPath;
        string adminUser = ADScriptService.Properties.Settings.Default.ServerAdminUser;
        string adminPassword = ADScriptService.Properties.Settings.Default.ServerAdminPwd;

        string userName = "exampleUser";
        string newPassword = "P455w0rd";
        try
        {
            DirectoryEntry userEntry = directoryEntry.Children.Find("CN=" + userName);

            userEntry.Path = userEntry.Path.Replace(":389", "");
            Console.WriteLine(String.Format("sAMAccountName={0}, User={1}, path={2}", userEntry.Properties["sAMAccountName"].Value, userEntry.Username, userEntry.Path));
            userEntry.Invoke("SetPassword", new object[] { newPassword });
            userEntry.Properties["userAccountControl"].Value = 0x0200 | 0x10000;
            userEntry.CommitChanges();
            Console.WriteLine("Se ha cambiado la contraseña");


        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }
}
}

但是此代码在调用行中出现以下错误(userEntry.Invoke("SetPassword", new object[] { newPassword });

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Runtime.InteropServices.COMException: The RPC server is unavailable. (Excepción de HRESULT: 0x800706BA)

用英语这意味着RCP服务器不可用。 我已经在这里停留了几天,但我发现这可能是由于身份验证问题所致。 调用方法“ Groups”有效( userEntry.Invoke("Groups"); ),并且管理员(我将用来登录ActiveDirectory的用户)具有所有特权。 同样,密码策略是完全允许的,没有最小长度或复杂性。

同样,由于程序必须迭代,因此实际程序实际上通过以下方式遍历DirectoryEntry的子级:

foreach(DirectoryEntry child in directoryEntry.Children) 
{
    child.Invoke("SetPassword", new object[] { newPassword });
    child.CommitChanges();
}

非常感谢你!

我认为使用directoryEntry.Children.Find("CN=" + userName)不会给您带来很多性能改进。 sAMAccountName属性是索引属性,因此搜索非常快。 这是您可以进行的最快搜索之一。

但是请注意,您的两个代码块不相等。 Find("CN=" + userName)试图将userName与帐户名: cn属性进行匹配。 但是,带有DirectorySearcher代码块会将userNamesAMAccountName属性匹配。 cnsAMAccountName属性不一定相同(尽管它们可能在您的域中)。

但是,如果您仍然想使用Children.Find() ,我怀疑问题可能出在您的DirectoryEntry Path中。 你为什么做这个?

userEntry.Path = userEntry.Path.Replace(":389", "");

您的ADScriptService.Properties.Settings.Default.ActiveDirectoryPath是否具有:389 如果它以LDAP://开头(默认LDAP端口为389),则不需要。

您的userEntry.Path应该看起来像(取决于域) LDAP://CN=user,OU=Users,DC=domain,DC=com 如果不是,那么您需要修复它。

旁注:除了更改搜索之外,您还可以采取其他措施来加快速度。 Properties集合使用缓存。 当您访问属性时,它将检查该属性是否已在缓存中,如果已使用,则使用该缓存。 但是,如果该属性不在缓存中,则它将向Active Directory询问每个具有value的属性 如果您只想读取一个或两个属性(特别是要对成千上万个帐户执行此操作),则这是昂贵且不必要的。

解决此问题的一种方法是,在访问任何Properties之前,告诉它仅使用RefreshCache获取所需的Properties 像这样:

userEntry.RefreshCache(new [] { "sAMAccountName", "userAccountControl" });

然后,当您访问这些属性时,它将已经将它们存储在缓存中,并且不会联系AD来获取任何内容。

另外,如果要在成千上万的帐户中大循环运行此程序,则建议您将DirectoryEntry放入using语句中(或在完成后调用userEntry.Dispose() )。 通常您不需要这样做,因为垃圾回收非常擅长清理它们。 但是,由于您正在运行一个大循环,因此垃圾收集器没有机会进行任何清理,因此您的过程最终可能会占用越来越多的内存,直到循环最终停止为止。 在进行开始处理未使用的DirectoryEntry对象之前,我曾有过像这样的大工作占用数GB内存。

更新:实际上,请忘记我对上面的DirectoryEntry所说的内容。 实际上,您根本不需要使用DirectoryEntry 不要使用searchResult.GetDirectoryEntry() 这里的问题是您已经进行搜索以找到该帐户。 但是现在您正在创建DirectoryEntry ,它将再次调用AD以获取您已经拥有的信息。 那可能就是您主要表现受到打击的地方。

而是使用搜索返回的属性。 现在,就像DirectoryEntry.Properties一样,如果您不指定要在搜索中返回的属性,那么它将返回所有它们,而您不一定需要这些属性。 因此,应设置PropertiesToLoad集合,如下所示:

search.PropertiesToLoad.AddRange(new [] { "sAMAccountName", "userAccountControl" });

然后,在搜索之后,您可以使用它来获取找到的帐户的sAMAccountName

searchResult.Properties["sAMAccountName"][0]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM