简体   繁体   English

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

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

I'm trying to programaticaly change several user's password, specifically without using System.DirectoryServices.AccountManagement (PrincipalContext) I have this piece of working code: 我正在尝试以编程方式更改多个用户的密码,特别是在不使用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);
        }
    }
}
}

This is an example with a single user, but what my program should do is iterate through ~120k users. 这是一个只有一个用户的示例,但是我的程序应该做的是遍历约120k个用户。 However, the operation of setting the search filter, finding one result and getting the DirectoryEntry takes about 2 or 3 seconds per user so I'm trying to use the DirectoryEntries structure given by the DirectoryEntry.Children property, which means replacing the six lines after "try{" with simply DirectoryEntry userentry = directoryEntry.Children.Find("CN=" + userName); 但是,设置搜索过滤器,查找一个结果并获取DirectoryEntry的操作每个用户大约需要2到3秒钟,因此我试图使用DirectoryEntry.Children属性提供的DirectoryEntries结构,这意味着在之后替换六行仅使用DirectoryEntry userentry = directoryEntry.Children.Find("CN=" + userName); “ try {” DirectoryEntry userentry = directoryEntry.Children.Find("CN=" + userName);

So the example's code would look like this: 因此,示例代码如下所示:

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);
        }
    }
}
}

But this code gets the following error in the invocation line (userEntry.Invoke("SetPassword", new object[] { newPassword }); : 但是此代码在调用行中出现以下错误(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)

which in english means RCP server unavailable. 用英语这意味着RCP服务器不可用。 I've been stuck here for some days, and I've only found that it can be because of some authentication problem. 我已经在这里停留了几天,但我发现这可能是由于身份验证问题所致。 Invoking the method "Groups" works ( userEntry.Invoke("Groups"); ) and the administrator, which is the user I'm loging in with to the ActiveDirectory has all the privileges. 调用方法“ Groups”有效( userEntry.Invoke("Groups"); ),并且管理员(我将用来登录ActiveDirectory的用户)具有所有特权。 Also the password policy is completly permissive with no minimum lenght or complexity. 同样,密码策略是完全允许的,没有最小长度或复杂性。

Again, because the program must iterate through, the real program actually iterates through the DirectoryEntry's children with: 同样,由于程序必须迭代,因此实际程序实际上通过以下方式遍历DirectoryEntry的子级:

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

Thank you very much! 非常感谢你!

I don't think using directoryEntry.Children.Find("CN=" + userName) will give you much performance improvement. 我认为使用directoryEntry.Children.Find("CN=" + userName)不会给您带来很多性能改进。 The sAMAccountName attribute is an indexed attribute, so the search is very fast. sAMAccountName属性是索引属性,因此搜索非常快。 That's one of the fastest searches you can make. 这是您可以进行的最快搜索之一。

But note that your two code blocks are not equal. 但是请注意,您的两个代码块不相等。 Find("CN=" + userName) is trying to match userName to the name of the account: the cn attribute. Find("CN=" + userName)试图将userName与帐户名: cn属性进行匹配。 But your code block with the DirectorySearcher is matching userName to the sAMAccountName attribute. 但是,带有DirectorySearcher代码块会将userNamesAMAccountName属性匹配。 The cn and sAMAccountName attributes are not necessarily the same (although they might be in your domain). cnsAMAccountName属性不一定相同(尽管它们可能在您的域中)。

But, if you still want to use Children.Find() , I suspect that the problem might be in your Path of the DirectoryEntry . 但是,如果您仍然想使用Children.Find() ,我怀疑问题可能出在您的DirectoryEntry Path中。 Why are you doing this? 你为什么做这个?

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

Does your ADScriptService.Properties.Settings.Default.ActiveDirectoryPath have :389 ? 您的ADScriptService.Properties.Settings.Default.ActiveDirectoryPath是否具有:389 It doesn't need to if it starts with LDAP:// (the default LDAP port is 389). 如果它以LDAP://开头(默认LDAP端口为389),则不需要。

Your userEntry.Path should look something like (depending on your domain) LDAP://CN=user,OU=Users,DC=domain,DC=com . 您的userEntry.Path应该看起来像(取决于域) LDAP://CN=user,OU=Users,DC=domain,DC=com If it's not, then you need to fix that. 如果不是,那么您需要修复它。

A side note: There is something you can do to speed this up a lot more than changing the search. 旁注:除了更改搜索之外,您还可以采取其他措施来加快速度。 The Properties collection uses a cache. Properties集合使用缓存。 When you access a property, it checks if it is already in the cache and, if so, uses the cache. 当您访问属性时,它将检查该属性是否已在缓存中,如果已使用,则使用该缓存。 But if the property is not in the cache, then it will ask Active Directory for every attribute that has a value . 但是,如果该属性不在缓存中,则它将向Active Directory询问每个具有value的属性 That is expensive and unnecessary if you only want to read one or two attributes (especially if you're doing it for thousands of accounts). 如果您只想读取一个或两个属性(特别是要对成千上万个帐户执行此操作),则这是昂贵且不必要的。

A way around this is to tell it to get only the attributes that you want using RefreshCache , before you access any of the Properties . 解决此问题的一种方法是,在访问任何Properties之前,告诉它仅使用RefreshCache获取所需的Properties Like this: 像这样:

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

Then when you access those properties, it will already have them in the cache and not reach out to AD to get anything. 然后,当您访问这些属性时,它将已经将它们存储在缓存中,并且不会联系AD来获取任何内容。

Also, if you are running this in a big loop over thousands of accounts, then I suggest you put the DirectoryEntry in a using statement (or call userEntry.Dispose() when you're done with it). 另外,如果要在成千上万的帐户中大循环运行此程序,则建议您将DirectoryEntry放入using语句中(或在完成后调用userEntry.Dispose() )。 Usually you don't need to since garbage collection is pretty good at cleaning them up. 通常您不需要这样做,因为垃圾回收非常擅长清理它们。 But because you're running a big loop, the garbage collector doesn't have a chance to do any cleaning, so your process can end up taking up more and more and more memory until the loop finally stops. 但是,由于您正在运行一个大循环,因此垃圾收集器没有机会进行任何清理,因此您的过程最终可能会占用越来越多的内存,直到循环最终停止为止。 I have had big jobs like this take several GB of memory until I started disposing the unused DirectoryEntry objects. 在进行开始处理未使用的DirectoryEntry对象之前,我曾有过像这样的大工作占用数GB内存。

Update: Actually, forget about what I said about the DirectoryEntry above. 更新:实际上,请忘记我对上面的DirectoryEntry所说的内容。 You don't actually need to use the DirectoryEntry at all. 实际上,您根本不需要使用DirectoryEntry Don't use searchResult.GetDirectoryEntry() . 不要使用searchResult.GetDirectoryEntry() The problem here is that you've already made a search to find the account. 这里的问题是您已经进行搜索以找到该帐户。 But now you're creating a DirectoryEntry , which is just going to make another call out to AD to get information you already have. 但是现在您正在创建DirectoryEntry ,它将再次调用AD以获取您已经拥有的信息。 That's probably where your main performance hit is. 那可能就是您主要表现受到打击的地方。

Instead, use the attributes returned from your search. 而是使用搜索返回的属性。 Now, just like DirectoryEntry.Properties , if you don't specify which attributes you want returned in the search, then it will return all of them, which you don't necessarily need. 现在,就像DirectoryEntry.Properties一样,如果您不指定要在搜索中返回的属性,那么它将返回所有它们,而您不一定需要这些属性。 So you should set the PropertiesToLoad collection, like this: 因此,应设置PropertiesToLoad集合,如下所示:

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

Then, after the search, you can use this to get the sAMAccountName of the account you found: 然后,在搜索之后,您可以使用它来获取找到的帐户的sAMAccountName

searchResult.Properties["sAMAccountName"][0]

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

相关问题 如何在具有子组的Active Directory组中找到用户? - How I can find a user in Active Directory Group with SubGroups? 如何在Active Directory中使用GUID(objectGUID)参数查找用户 - How I can find a User with the GUID(objectGUID) Parameter in Active Directory 如何在Active Directory中更改用户的登录名 - How to change login name of user in Active Directory 如何在我的应用程序中加密用户设置(例如密码)? - How can I encrypt user settings (such as passwords) in my application? Active Directory通知-如何确定更改是否是新用户 - Active Directory Notifications - how to tell if a change is a new user 如何使用 C# 以编程方式从 Azure Active Directory 禁用用户 - How can I disable a user from an Azure Active Directory programatically with C# 如何用Active Directory替换我的专有用户帐户和权限数据库? - How can I replace my proprietary user accounts and permissions database with Active Directory? 如何读取Active Directory对象的User-Parameters属性的内容? - How can I read the content of the User-Parameters attribute of an Active Directory Object? 如何使用C#检查用户是否在Active Directory中具有写权限? - How can I check if a user has write rights in Active Directory using C#? 如何使用asp.net获取活动目录中用户的“最小密码使用期限”? - How can I get 'minimum password age' of user in active directory using asp.net?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM