Click here to Skip to main content
Click here to Skip to main content

Remotely updating local user passwords with DirectoryServices

, 2 Aug 2006
Rate this:
Please Sign up or sign in to vote.
This article shows how to remotely change the password for a local user using DirectoryServices. Includes code to enumerate all computers in an Active Directory.

Sample Image - masspwchange1.gif

Introduction

A good dictum to maintaining a strong password is to change it often. In an Active Directory environment, enforcing a password change policy is a simple matter as it is all centralized. One account that very often gets overlooked on an Active Directory domain is local administrator accounts. These accounts are important for managing the computer when Active Directory is not applicable.

This application takes on the challenge of remotely and enmass modification of the local admin account password of computers in your Active Directory topology.

To make the task more efficient, the application also includes code to enumerate and list all of the computers under a user specified LDAP path. Also included is a BackgroundWorker to asynchronously do the actual password changing, with a progress bar to keep the user up to date on the progress; this is important as each password change can take up to 5 seconds.

If you notice, this application is not very OO, mostly for simplicity of explanation. It is not a good idea to put actual problem domain logic in your UI source files.

Background

If you are familiar with using ADSI with WSH, some of this will be familiar; in fact, the DirectoryServices namespace classes are implemented on top of the ADSI COM interface, so most things you are familiar with from ADSI can map in some way to the DirectoryServices classes.

Using the code

The code is commented fairly well, and is only about two pages long, so there is not much specifically to mention about the code.

I will call out two or three pieces of interest from the source below. I will remove exception handling for clarity/brevity.

Finding computers

// Map the LDAP root to search for computers in
// If the root is empty it defaults to the current domain
DirectoryEntry dir;

if (rootBox.Text.Length > 0)
    dir = new DirectoryEntry(string.Format("LDAP://{0}", rootBox.Text));
else
    dir = new DirectoryEntry();

// Initialize the searcher to find directory objects of class "computer"
DirectorySearcher srch = new DirectorySearcher(dir);
srch.Filter = "(objectClass=computer)";

// Execute the search to find all occurances
SearchResultCollection results = srch.FindAll();

// Iterate over the list of computers found and add them to the list
foreach (SearchResult res in results)
{
    DirectoryEntry ent = res.GetDirectoryEntry();

    // Trim off the leading "CN="
    computerList.Items.Add(ent.Name.Substring(3));

    ent.Dispose();
}

Interesting to note that if you do not specify a root for the directory entry object, it defaults to the domain that you are currently logged in to.

Once you have the root you want to search from, you can instantiate a DirectorySearcher to do the actual searching for you. Here, we set the filter to (objectClass=computer) so that it will show us all the objects that derive from the computer schema class.

Then, it is as simple as asking the searcher to FindAll to return a collection of DirectoryEntry objects. All ADSI objects are wrapped as DirectoryEntry objects, with unique properties and methods available via Invoke* functions.

Here, we are iterating over the list and putting the name of each computer in a sorted listbox.

Referencing the Local User

// Get the arguments passed to the background worker
PasswordWorkerArgs args = (PasswordWorkerArgs)e.Argument;

// Iterate each selected computer name
int x = 0;
foreach (string cname in args.Names)
{
    // Get a remote reference to the administrator user on the local machine
    DirectoryEntry admin = new DirectoryEntry(string.Format("WinNT://{0}/" + 
                                             "Administrator, user", cname));

    // Invoke the SetPassword function on the admin user object
    admin.Invoke("SetPassword", new object[] { args.Password });

    // Save the password change
    admin.CommitChanges();

    // Dispose of the ADSI object
    admin.Dispose();

    // Report our progress as an integer percentage
    x++;
    passwordWorker.ReportProgress((int)(((double)x / args.Names.Length) * 100));
}

In this snip, you can see how we reference a computer's local Administrator user the WinNT ADSI provider.

PasswordWorkerArgs is a simple class to carry a list of computers and the password to set into the async function of the BackgroundWorker.

The args.Names property is a string[] of computer names. We reference the computer's Administrator user using the WinNT ADSI provider with the URI WinNT://COMPUTERNAME/Administrator, user. This tells the provider that we want a reference to a user called Administrator on a computer called COMPUTERNAME.

Once we have the ADSI object, we can call the SetPassword method using the Invoke method of the DirectoryEntry object. This will execute the SetPassword on the ADSI user COM object wrapped by the DirectoryEntry. We pass arguments to the method using a simple object array.

Any changes that are made to a DirectoryEntry object must be committed using the CommitChanges method. And, don't forget to dispose of your DirectoryEntry because it is just a managed wrapper over an unmanaged resource.

And at the end, we have the BackgroundWorker report its progress to the UI thread so that the progress bar can get updated.

Points of interest

If you are familiar with ADSI using WSH, you should be able to map that knowledge into using the DirectoryServices namespace to accomplish a lot of repetitive management tasks.

Why would you want to do directory scripting in a .NET language? There are a number of reasons:

  • Following the deluge of viruses that used WSH to do their dirty deeds, a lot of enterprises have disabled all WSH on their computers; a .NET application interacting with ADSI does not require WSH.
  • You may want management heads or even users to have some latitude in helping you manage themselves. I cringe at the thought of having a user edit or even run a .vbs or .js file. All users are comfortable with the Windows UI, not to mention the error avoided by letting the user interact with a predefined UI.
  • Obviously, a strongly typed and compiled language will give you more power and functionality. Not that you couldn't do almost anything in WSH with COM objects, but it will be orders of magnitude easier with .NET languages. Imagine exposing ADSI functions as webservices (I imagine Microsoft will do this eventually).

See my other article on a simple application to add users to Active Directory including their addresses, telephone numbers, etc... All from an XML document exported from an Excel template that any user in your company can edit.

History

  • Aug 8 2006: Initial article posted.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Josh Perry

United States United States
No Biography provided

Comments and Discussions

 
QuestionRemotely updating local user psw - how to? PinmemberBluebee7413-Oct-08 4:34 
AnswerRe: Remotely updating local user psw - how to? PinmemberHelvio11-Jul-14 14:38 
Generallocal admin group Pinmembercaptain121019-Jul-07 2:37 
QuestionLogging PinmemberTyeDaEnergyGuy10-Aug-06 11:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140721.1 | Last Updated 2 Aug 2006
Article Copyright 2006 by Josh Perry
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid