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