Automating Windows Password Changes in PowerShell

Automating Windows Password Changes in PowerShell

Many organizations have a need for scheduled, automated windows local admin password rotations with reporting functions.

The following PowerShell script covers these requirements. It uses a CSV to input the computers which need a password change. This could easily be changed to pipe directly from AD, some database, or even a simple text file. It then sets the local administrator account to a random password (assuming that the password is over a certain age) and records that password in a CSV in a secure location. When the loop is completed it will send a report explaining what happened to an admin address.

Please feel free to comment with questions or suggestions.

`
#Created: 11.04.2010
#Modified: 05.03.2011
#Description: Queries AD Export spreadsheet and checks connection/password age for each item.

#

Set script options

#

# Set CSV output path for storing passwords:
$optCSVPath = “C:\scripts\PWChangeResults-” + (Get-Date -format “yy-MMM-dd-HHmmss”) + “.csv”

# Password age that triggers reset action:
$optPwdAge = 45

# Set report message options:
$optMsgSMTPServer = “smtpserver.domain.local”
$optMsgFrom = “[email protected]
$optMsgTo = “[email protected]

#

Create supporting functions.

#

#Return age of local account password, or 0 if account is disabled.
Function Get-LocalPasswordAge([string]$server,[string]$user)
{
if (($user -ne “”) -and ($server -ne “”))
{
if (([ADSI]“WinNT://$server/$user,user”).AccountDisabled){return 0}
else {return [Math]::Round(([ADSI]“WinNT://$server/$user,user”).PasswordAge[0] /86400)}
}
}

#Set local server account password
Function Set-AdminPass([string]$strComputer,[string]$strPassword)
{
Write-Host Setting password for $strComputer
$account=[adsi](“WinNT://” + $strComputer + “/administrator, user”)
$account.psbase.invoke(“SetPassword”, $strPassword)
}

#

Load supporting library for random password generation

#

[Reflection.Assembly]::LoadWithPartialName(“System.Web”)

#

Build report and supporting “chunks”

#

# Build SMTP client
$smtp = new-object system.net.mail.smtpClient
$smtp.host = $optMsgSMTPServer

Build message

$message = new-object System.Net.Mail.MailMessage

You can add additional of the line below to add more recipients

$message.To.Add($optMsgTo)
$message.From = $optMsgFrom
$message.IsBodyHtml = $True
$message.Subject = “Local Admin Password Age and Change Report”

# Create array for results
$results = @()

#

Import spreadsheet with all domain added computers

#

# Foreach: Write status output to console, attempt to ping item, check password age and record information - then reset password, or record inability to ping
Import-CSV c:\adexport.csv | foreach {
Write-Host Starting $.Name
if (Test-Connection $
.Name -count 1 -quiet){
$pwdAge = Get-LocalPasswordAge $.Name “Administrator”
if ($pwdAge -gt $optPwdAge){
$result = “” | select ServerName, PWAge, NewPassword
$result.ServerName = $
.Name
$result.PWAge = $pwdAge.ToString()

Generate random password. This function can me modified to change number of special characters and length.

$result.NewPassword = [System.Web.Security.Membership]::GeneratePassword(10,2)
Set-AdminPass $result.ServerName $result.NewPassword
Write-Host $result
$results += $result
}
}
else {
$result = “” | select ServerName, PWAge
$result.ServerName = $_.Name
$result.PWAge = “Ping Fail”
Write-Host $result
$results += $result
}
}

# Create unique name for CSV to record output

# Populate the rest of the report email and send it
if ($results){
$results | export-csv $optCSVPath -NoTypeInformation
$resultNotes = “The following computers were either unreachable, or the script has just changed the password because it was nearing expiration.”

$msgHead = ‘‘
$msgBody = ($resultNotes + ($results | select ServerName, PWAge | convertto-html))
$message.Body = (convertto-html -head $msgHead -body $msgBody)
$smtp.Send($message)
}
else {
$message.Body = “All hosts responded and all passwords were less than the age option, or the input object is wrong.”
$smtp.Send($message)
}`