Sunny J
Sunny J

Reputation: 449

AD password update via Powershell

Too all Powershell Experts: I need some help to figure out why this is working incorrectly. What I mean incorrectly, the accounts in CSV password get updated with a random password and email is sent to the users but i don't under why they are getting the same password, and everyaccount in the AD is addressed. See email output:

Firstuser email:

Hello Guest DefaultAccount user1, user2, user3, user4 test John Doe, 
Your AD Account user1, password has been updated to e,wQlsGfBE;J'4  

Second useremail

 Hello Guest DefaultAccount user1, user2, user3, user4 test John Doe, 
Your AD Account jdoe, password has been updated to e,wQlsGfBE;J'4  

what I'm trying to accomplish are as follows:

  1. List item
  2. Import one column cvs with just "samAccountName"
  3. reset password of the accounts listed in the csv with a random generated 20 character long password
  4. Email the AD account associated email address with the new password. (I'm doing this because their email address if different from their AD Account), so they are able to receive their email.

Import-Module ActiveDirectory
# Import AD user objects and store in users variables 
$users = Get-ADUser -Filter *  -Properties Name, EmailAddress,SamAccountName | Select SamAccountName,Name,EmailAddress
    
$Name = $users.Name
$SamAccountName = $users.SamAccountName
$EmailAddress = $users.EmailAddress

#Date
$date = (Get-Date -Format F) 

#Generate a random password and store in newpwd 
$newpwd = -join (33..126|%{[char]$_}|Get-Random -Count 20)
$newPassword = ConvertTo-SecureString -AsPlainText "$newpwd" -Force

# Import users from CSV
Import-Csv "C:\Userlist.csv" | ForEach-Object {
$samAccountName= $_."samAccountName"

        #Proceed with password reset
            Set-ADAccountPassword -Identity $samAccountName -NewPassword $newPassword -Reset
            Set-AdUser -Identity $samAccountName -ChangePasswordAtLogon $true

#Server configuration 
$smtpServer =""
$SmtpPort = ""
# sending option
$from = "[email protected]"
$to = $EmailAddress 
$subject  ="AD Password updated - " + $date
$priority = "Normal"
$from = ""

$body = @"
Hello $name, <br> 
Your AD Account $samAccountName, password has been updated to $newpwd
"@
try {
# Send the report email
Send-MailMessage -To $to -Subject $subject -BodyAsHtml -body $body -SmtpServer $smtpServer -Port $SmtpPort -From $From -Priority $priority  
}
catch{
    write-warning "error in sending. $_"
}  # End User Processing
            
    }

The Account password is getting updated, but just can't send the emai

Upvotes: 0

Views: 1091

Answers (2)

Steven
Steven

Reputation: 7087

Here, I'm just building on @Theo's answer.

Import-Module ActiveDirectory

# Import AD user objects and store in users variables 
$Users = @{} 
Get-ADUser -Filter *  -Properties Name,EmailAddress,SamAccountName | Select-Object SamAccountName,Name,EmailAddress |
ForEach-Object{ $Users.Add( $_.samAccountName, $_ ) }

#Date
$Date = (Get-Date -Format F) 

# Generate a random password and store in newPassword 
$newPassword = -join ( 33..126 | ForEach-Object{ [char]$_ }| Get-Random -Count 20 )
$newPassword = ConvertTo-SecureString -AsPlainText "$newPassword" -Force
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# Move to the loop if you want each user's pwd to be different!!!

# Put this here so you can indent the loop easier...
$BaseBody = 
@"
Hello %NAME%, <br> 
Your AD Account %SAMACCOUNTNAME%, password has been updated to %NEWPASSWORD%
"@

# Server configuration & recipien configurations...:
$MailParams = @{
    $smtpServer = "YorServer.example.com" #MAKE SURE YOU CHANGE THIS
    $SmtpPort   = 25
    $from       = "[email protected]"
    $To         = ""
    $subject    ="AD Password updated - " + $date
    $Body       = ""
}
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# Removed -Priority if it's alwasy going to be normal you don't need it.

# Import users from CSV
Import-Csv "C:\Userlist.csv" | 
ForEach-Object {
    $samAccountName = $_."samAccountName"

    # Proceed with password reset
    Set-ADAccountPassword -Identity $samAccountName -NewPassword $newPassword -Reset
    Set-ADUser -Identity $samAccountName -ChangePasswordAtLogon $true
    
    # Get the Name & Email address from the $Users Hash...:
    $EmailAddress = $Users[$samAccountName].EmailAddress
    $Name         = $Users[$samAccountName].Name

    # Reset the To & Body values
    $MailParams['To']   = $EmailAddress
    $MailParams['Body'] = $BaseBody.Replace('%NAME%', $Name).Replace('%SAMACCOUNTNAME%', $samAccountName).Replace('%NEWPASSWORD%', $newPassword)    

    try { # Send the report email
        Send-MailMessage @MailParams -ErrorAction Stop
    }
    catch{
        write-warning "Error in sending : $_"
    }
} # End User Processing
  • Used a hash table to store the AD user objects so you don't have to re-execute a Where{} on every loop iteration. Depending on the number of users this could be quite a bit faster. When relating data between 2 sources I gravitate toward a hash tables.
  • Moved the $Body var out of the loop. Here strings don't like to be indented, by moving it out we don't have to interfere with the loop indentation; improving readability. Then inside the loop we can get the actual body by doing some string replacement.
  • I also placed the $MailParams parameter hash outside the loop. Then inside the loop I reset the 2 keys that vary. Nitpicking, but I'd rather not declare the hash on every loop iteration either.
  • $Reused the $newPassword variable rather than have the extra $newpdw variable just laying around.
  • Expanded the aliases, just because PSScriptAnalyzer complains about it...

Obviously this is untested!

Upvotes: 2

Theo
Theo

Reputation: 61243

When you do $Name = $users.Name, the $Name variable gets an array containing ALL usernames in the CSV.

Remove that line and instead set the variable inside your ForEach-Object loop:

# Import users from CSV
$UserList = Import-Csv "C:\Userlist.csv" | ForEach-Object {
    $samAccountName= $_.samAccountName
    $name = $_.Name
    # then the rest of your code
}

Note:

  • SmtpPort takes an integer, not a string
  • From cannot be an empty string

P.S. Try to do some proper code indentation, so it is much easier to see where a code block (like ForEach-Object) starts and ends)


Your code revised:

Import-Module ActiveDirectory
# Import AD user objects and store in users variables 
$users = Get-ADUser -Filter *  -Properties Name, EmailAddress,SamAccountName | Select SamAccountName,Name,EmailAddress

#Date
$date = (Get-Date -Format F) 

# Import users from CSV
Import-Csv "C:\Userlist.csv" | ForEach-Object {
    # read this from the CSV
    $samAccountName= $_.samAccountName
    # get the user object from the $users array
    $user = $users | Where-Object { $_.SamAccountName -eq $samAccountName }
    $name = $user.Name
    $emailAddress = $user.EmailAddress

    #Generate a random password and store in newpwd 
    $newpwd = -join (33..126|%{[char]$_}|Get-Random -Count 14)
    $newPassword = ConvertTo-SecureString -AsPlainText "$newpwd" -Force

    $body = @"
Hello $name, <br> 
Your AD Account $samAccountName, password has been updated to $newpwd
"@

    #Proceed with password reset
    Set-ADAccountPassword -Identity $samAccountName -NewPassword $newPassword -Reset
    Set-AdUser -Identity $samAccountName -ChangePasswordAtLogon $true

    #Server configuration 
    $mailParams = @{
        SmtpServer = "YourSMTPServer"
        Port = 25  # <-- an integer value
        # sending option
        From = "[email protected]"
        To = $user.EmailAddress 
        Subject  ="AD Password updated - " + $date
        Priority = "Normal"
        Body = $body
        BodyAsHtml = $true
    }
    try {
    # Send the report email
    Send-MailMessage @mailParams -ErrorAction Stop
    }
    catch{
        write-warning "error in sending. $_"
    }  # End User Processing
}

Upvotes: 2

Related Questions