March 25th, 2009

Hey, Scripting Guy! How Can I Change the Passwords of Multiple Local Computer Accounts?

Hey, Scripting Guy! Question

Hey, Scripting Guy! We have certain accounts that are local accounts. These accounts are created when the computer is built, and the passwords are set and that is it. For as long as the computer exists, these accounts are never touched, and the passwords are never changed. To make matters worse, if indeed it can be worse, these account passwords are not really known. It seems that whoever built the PC made up a password for that account, and in order to actually log on to the account externally, we need to first go find the person who loaded the operating system onto the computer. Then he has to remember when the computer was built, and next he has to remember what password he was using during the time when the computer was built.

If he gets stuck on remembering when the computer was built, we can use WMI to gather that information, but if he cannot remember what password he was using in that time frame, we are stuck. Most of the computers are special-purpose machines that are in remote locations. I am, to be frank, quite concerned about the password situation on these machines. In some cases, the computers have been operating for five years, and the password has never been changed. It will take a horribly long time if I have to do this all by hand. I would like to implement change control for these computers, and write a script that I can use to change the passwords for these local accounts. Of course, when I say I would like to write a script, I really mean I would like for you to write such a script so I can copy it.

– TK

SpacerHey, Scripting Guy! Answer

Hi TK,

I was recently at the Microsoft office in Charlotte, North Carolina, in the United States. A friend who knows I like tea offered me a can and said, “Drink this, it’s great.” So I pulled the pop top on the can, took less than a single sip, and decided it really was not great. For me, there is no such thing as instant tea. Luckily, I happened to carrying my tea kit with me in my computer bag. So I got out my little tea pot the Scripting Wife gave me for Christmas, my little tin of Earl Grey tea, and my tea strainer that my friend Jit and Mrs. Jit gave me when I was in Canberra, Australia.

I proceeded to brew myself a proper cup of tea. Tea should not be rushed. It should be savored and appreciated. User account management should not be rushed either, but one need not linger over a mouse for the next three weeks either. The graphical user interface is not something that needs to be savored. And while I do appreciate it from time to time, when I have a simple one-off task to perform, I do believe I could rapidly grow weary of seeing repeated warning boxes being displayed for each of several thousand changes that may need to be made. Clearly, this is a task that calls for a script.

This week we will be looking at scripting Windows PowerShell as it applies to local account management. This is an area that comes up from time to time and for which there are not an awful lot of resources from which to choose. We have these tasks in the Script Center Script Repository pretty well hidden away in the Other Directory Services category. There are some great scripts in the Community-Submitted Scripts Center. Local account management has been a favorite topic of the “Hey, Scripting Guy!” articles over the years, and as a result we have a good selection of articles grouped together in the “Hey, Scripting Guy!” archive. The most extensive reference you will find is the MSDN coverage of the WinNT ADSI provider.

Well, TK, I wrote a script called ChangeUserPassword.ps1. It reads the contents of a text file with all the computer names that house the account whose password you need to change. A VBScript version of this script can be found in the Community-Submitted Scripts Center.

$computers = Get-Content -path C:\fso\computers.txt
$user = "aUser"
$password = "MyNewPassword!"
Foreach($computer in $computers)
{
 $user = [adsi]"WinNT://$computer/$user,user"
 $user.SetPassword($Password)
 $user.SetInfo()
}

To change the user account password remotely using the graphical user interface, you open up the Computer Management utility. Then you right-click the little computer at the top of the screen and choose Connect to another computer from the Action menu. Depending on the speed of the network or the speed of the computer, you may see a spinning disk or you may see an hour glass or you may see a message that says, “Connecting to remote computer. Please wait.” After you find the Local Users and Groups section under System Tools, right-click the user account. When you select Set Password and click past the long warning message, you are presented with the nice dialog box seen here:

Image of the Computer Management tool

 

Even if the process were as fast as it is on the local computer, it still takes time. If you have to change the password for a particular account on a thousand computers, the graphical utility simply is not a scalable solution.

That’s where the script comes in. The first thing we do is use the Get-Content cmdlet to read a text file that contains the names of all the computers that have accounts whose password we wish to change. The Get-Content will return an array with each element of the array holding the name of one of the computers. There is nothing special about the computers.txt file. It is a simple text file with the names of computers on individual lines. This is seen here:

Image of the New User dialog box

 

We use the Get-Content cmdlet, and each line from the computers.txt file is printed on its own individual line:

PS C:\> Get-Content -Path C:\fso\Computers.txt
Vista
Berlin
Lima
Sydney

In this example, we use the Get-Content cmdlet to read the text file. We store the returned data in a variable named $computers. On the next line of the example, we use the [0] to refer to the first item in the array. When it is printed out, we see that it holds the name Vista, which incidentally was the first name displayed in the previous example:

PS C:\> $computers = Get-Content -Path C:\fso\Computers.txt
PS C:\> $computers[0]
Vista
PS C:\>

We read the contents of the computers.txt file by using the Get-Content cmdlet, and we store the returned array of text in the $computers variable:

$computers = Get-Content -path C:\fso\computers.txt

Now we specify the name of the user whose password we are going to change. We assign the username to the $user variable:

$user = "aUser"

Then we specify the new password for the user. It is also a straightforward value assignment to a variable as seen here:

$password = "MyNewPassword!"

If you are uncomfortable with including the user password in the text of the script, you can easily use the Read-Host cmdlet to prompt you to type the password when you run the script. This is shown here:

PS C:\> $password = Read-Host -Prompt "Enter new password for the user"
Enter new password for the user: NewPassword1
PS C:\> $password
NewPassword1
PS C:\>

You will perhaps notice that when using the Read-Host cmdlet to solicit the password, the password is displayed as plain text. If this is uncomfortable to you, it is possible to mask the password by specifying the –asSecureString parameter. Notice that when the password is typed, it is masked with a series of asterisks. When we try to retrieve the password from the $password variable, we are told that the $password variable contains an instance of a System.Security.SecureString object:

PS C:\> $password = Read-Host -prompt "Enter new password for user" -asSecureString
Enter new password for user: ***********
PS C:\> $password
System.Security.SecureString

Next we need to work our way through the array of computer names stored in the $computers variable. When we hear the word array, we can immediately think ForEach, just like in VBScript. We use the ForEach statement to walk through the array of computers. The $computer variable is used as our placeholder to keep track of where we are in the array:

ForEach($computer in $computers)
{

It is time to connect to the user object. To do this, we use the [adsi] type accelerator and give it the WinNT ADSI provider. We talked about the technique of connecting to local objects on Monday. Refer to that article for a more complete discussion of the WinNT ADSI provider.

We give the WinNT ADSI provider the name of the computer contained in the $computer variable, and the name of the user that is specified in the $user variable. The second user listed is a hint to ADSI to tell it what kind of object we are working with. We store the returned user object in the $user variable as seen here:

$user = [adsi]"WinNT://$computer/$user,user"

Now we call the SetPassword method and give it the password we stored in the $Password variable:

$user.SetPassword($Password)

When we are finished changing the password, we call the SetInfo method to write the updates back to the account database:

$user.SetInfo()
}

TK, thanks for asking this question. We hope you will join us tomorrow as we present the final installment of our Local Account Management Week. Until tomorrow, take care.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys

Author

0 comments

Discussion are closed.