Search This Blog

Monday, November 3, 2008

Use PowerShell to change Local Admin Passwords on Multiple Machines

Ran into an issue with the need to change the local administrator password on several servers.
Decided to try my hand with PowerShell to do the job.

After some research I found a few scripts that got me close, so I have done some tweaking and created a script that will:
Thanks to http://poshcode.org/567 and http://myitforum.com/cs2/blogs/yli628/archive/2007/08/23/powershell-script-to-change-administrator-password-on-a-list-of-machines.aspx for getting me on my way with this.
  • Use a text file list of machine names as input to the script
  • Create a randomly generated complex password for each machine
  • Ping the machine to verify it is online
  • Change the local admin password to the new complex password
  • Write all changes and passwords to an Excel file

I decided to have it write the passwords to an Excel File so I can easily import them into our Secret Server password management system.

Your milage may vary but this is what I needed:

Copy the below code into PowerGUI Script Editor (or your favorite editor) and it should format all the line breaks correctly.

---------------------------


#
# Script Name: Change Local Admin Passwords
# Version: 1.5
# Author: Andrew H. Bradley II
# Date: 11/3/08
#
# Description:
# Requires Microsoft Excel to be available on the machine running this script!
#
# This script will accept a list of machine names from c:\MachineList.txt
# Using that list it will ping each machine to verify it is active.
# Once verified it will use the credentials of the script to change the local Administrator account password.
# It will then open an Excel Spreadsheet and write the status of each MachineName and what it's password now is.
# All passwords are randomly generated - the random nature of the passwords can be controlled via the script in line 122
#
# Special Thanks to Ying Li at myITforum.com and to the PowerShell Code Repository for the Complex Password Function
# http://myitforum.com/cs2/blogs/yli628
# http://poshcode.org/567
#
# Functions
Function New-ComplexPassword ([int]$Length=8, $digits=$null, $alphaUpper=$null, $alphaLower=$null, $special=$null)
{
# ASCII data taken from http://msdn2.microsoft.com/en-us/library/60ecse8t(VS.80).aspx
# Make sure the password is long enough to meet complexity requirements
if($digits+$alphaUpper+$alphaLower+$special -gt $Length) { throw "Password too short for specified complexity" }
# Define character groups and the number of each required by passwords
# In case this is used in a DCPromo answer files, theres a few chars to
# avoid: Ampersand, Less than, double quote and back slash
# (34,38,60,92)
$groups = @()
$group = New-Object System.Object
Add-Member -In $group -Type NoteProperty -Name "Group" -Value "0123456789" # 48..57
Add-Member -In $group -Type NoteProperty -Name "Count" -Value $Digits
$groups += $group
$group = New-Object System.Object
Add-Member -In $group -Type NoteProperty -Name "Group" -Value "ABCDEFGHIJKLMNOPQRSTUVWXYZ" # 65..90
Add-Member -In $group -Type NoteProperty -Name "Count" -Value $alphaUpper
$groups += $group
$group = New-Object System.Object
Add-Member -In $group -Type NoteProperty -Name "Group" -Value "abcdefghijklmnopqrstuvwxyk" # 97..122
Add-Member -In $group -Type NoteProperty -Name "Count" -Value $alphaLower
$groups += $group
$group = New-Object System.Object
Add-Member -In $group -Type NoteProperty -Name "Group" -Value '~`!@#$%^&*()-_={}[]\;:"<>?,./'' ' # 32..47, 58..64, 91..96, 123..126
Add-Member -In $group -Type NoteProperty -Name "Count" -Value $special
$groups += $group
# initilize random number generator
$ran = New-Object Random
# make sure password meets complexity requirements
foreach ($req in $groups)
{
if ($req.count)
{
$charsAllowed += $req.group
for ($i=0; $i -lt $req.count; $i++)
{
$r = $ran.Next(0,$req.group.length)
$password += $req.group[$r]
}
} elseif ($req.count -eq 0) {
$charsAllowed += $req.group
}
}
# make sure password meets length requirement
if(!$charsAllowed)
{
$groups % { $charsAllowed += $_.group }
}
for($i=$password.length; $i -lt $length; $i++)
{
$r = $ran.Next(0,$charsAllowed.length)
$password += $charsAllowed[$r]
}
# randomize the password
return [string]::join('',($password.ToCharArray()sort {$ran.next()}))
}
# Main Program
# Open Excel Object
$a = New-Object -comobject Excel.Application
$a.visible = $True
# Create a new WorkBook/WorkSheet
$b = $a.Workbooks.Add()
$c = $b.Worksheets.Item(1)
# Create the Column Headers
$c.Cells.Item(1,1) = "Machine Name"
$c.Cells.Item(1,2) = "Password Changed"
$c.Cells.Item(1,3) = "Report Time Stamp"
$c.Cells.Item(1,4) = "Password"
# Format the Column Headers
$d = $c.UsedRange
$d.Interior.ColorIndex = 19
$d.Font.ColorIndex = 11
$d.Font.Bold = $True
# Setup first data row following headers
$intRow = 2
# Run this loop for every machine listed in the MachineList.txt file
foreach ($strComputer in get-content C:\MachineList.Txt)
{
$c.Cells.Item($intRow,1) = $strComputer.ToUpper()
#This is using WMI to ping the computers and return a status
$Reply = Gwmi Win32_PingStatus -Filter "Address = '$strComputer'" Select-Object StatusCode
if ($Reply.StatusCode -eq 0)
{
# Use ADSI to change the local administrator account's password
$admin=[adsi]("WinNT://" + $strComputer + "/administrator, user")
# Call the New-ComplexPassword Function to create a new random password
$adminpass=New-ComplexPassword 8 3 2 2 1
# Set the password to the local machine
$admin.SetPassword($adminpass)
# Write the results to the Excel Sheet
$c.Cells.Item($intRow,2).Interior.ColorIndex = 4
$c.Cells.Item($intRow,2) = "Yes"
$c.Cells.Item($intRow,3) = Get-Date
$c.Cells.Item($introw,4) = $adminpass
}
#If the ping reqest fails
Else
{
$c.Cells.Item($intRow,2).Interior.ColorIndex = 3
$c.Cells.Item($intRow,2) = "Not Pingable"
$c.Cells.Item($intRow,3) = Get-Date
}

# Clear the variables for the next loop and increment the row number
$Reply = ""
$adminpass = ""
$intRow = $intRow + 1
}
# Autofit the entire WorkSheet
$d.EntireColumn.AutoFit()
cls