Tuesday, 25 August 2015

Reset User Passwords with AD Self Service Portal

I ran across a problem when we wanted specific users to be able to reset there passwords through some sort of self service mechanism.

After looking at many services(very expensive services!) to reset passwords which a lot of required enrolling, and where a pain to setup, which  I stumbled across the following page 

Although I was explicitly looking for a method of password reset via email I went about and modified the script to suit my needs.

so without further ado i present

function Create-RandomString()
  $aChars = @()
  $aChars = "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "C", "b", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "2", "3", "4", "5", "6", "7", "8", "9", "_", ";"
  $intUpperLimit = Get-Random -minimum 8 -maximum 10

  $x = 0
  $strString = ""
  while ($x -lt $intUpperLimit)
     $a = Get-Random -minimum 0 -maximum $aChars.getupperbound(0)
     $strString += $aChars[$a]
     $x += 1

  return $strString

#prereqs need to be installed
# - Remote Admin tools for AD - RSAT
# - Exchange pluggins http://www.microsoft.com/en-us/download/confirmation.aspx?id=42951
#original script from
#password change script for changing the reset password
#Configuration Block
#SMTP Server that mail will go OUT through
$SmtpServer = "smtp.email.com"
#email address that will be used as the REPLY email address
$ResetEmail = "donotreply@email.com"
#the email account to check
$Username = "PassReset@email.com"
$Password = "P@ASSWORD"
#exchange web address, this "should" work for any o365 install by changing the username and password above
$MailServer = "https://outlook.office365.com/ews/exchange.asmx"
$ExchangeVersion = "Exchange2013"
#AD Field that the recovery email address is stored in AD. The email recieved needs to come from the email address specified in the AD Field
$ADMailField = "mail"
#AD Server to send the password reset to. You can use the FQN or IP Address
$ADServer = "DC.local"

#User who shold recieve email notifications that a password was reset or an invalid request was sent.
$LoggingUser = "log@email.com"

#Download for file is here: http://www.microsoft.com/en-us/download/confirmation.aspx?id=42951
[Reflection.Assembly]::LoadFile("C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll")

$email = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::$ExchangeVersion)
$email.Credentials = New-Object Net.NetworkCredential($Username, $Password)
$uri=[system.URI] $MailServer
$email.Url = $uri
$inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($email,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox)

if ($inbox.UnreadCount -gt 0) {
 $PropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
 $PropertySet.RequestedBodyType = [Microsoft.Exchange.WebServices.Data.BodyType]::Text;
  # Set search criteria - unread only
 $SearchForUnRead = New-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::IsRead, $false)
 $items = $inbox.FindItems($SearchForUnRead,10)  #return only 10 unread mail items
  Import-Module -Name ActiveDirectory

 foreach ($item in $items.Items) {
   # load the property set to allow us to view the body

  if($item.Body.text -Like "*") {
   $EmailAddress = $item.From.address
   $user = Get-ADUser -Filter {$ADMailField -eq $EmailAddress} -Properties $ADMailField -Server $ADServer
   If ($user -ne $null) {
    $PW = Create-RandomString
    if ($PW.length -gt 1) {
     Set-ADAccountPassword -identity $user.SamAccountName -Server $ADServer -Reset -NewPassword (ConvertTo-SecureString -AsPlainText $PW -Force)
     Unlock-ADAccount -identity $user.SamAccountName -Server $ADServer
     $PasswordAge = (Get-ADUser $user -Properties PasswordLastSet | Select PasswordLastSet)
     if ($PasswordAge.PasswordLastSet -ge (Get-Date).AddMinutes(-1)) {
     $Body = "Password reset for " + $user.SamAccountName + "-" + $user.DistinguishedName
     $UsernameReset = $user.SamAccountName
     send-mailmessage -to $LoggingUser -from $ResetEmail -subject "Password Reset - $PW" -body $Body -SmtpServer $SmtpServer
     send-mailmessage -to $item.From.address -from $ResetEmail -subject "Pass Login" -body "Your Username and Password are the following,`nUsername: $UsernameReset `nPassword: $PW`nTo change yout password please visit `n`nIf you are still having problems with your login please contact " -SmtpServer $SmtpServer
   else {
    send-mailmessage -to $LoggingUser -from $ResetEmail -subject "Invalid email" -body "email address was not found " $item.From.address -SmtpServer $SmtpServer
    send-mailmessage -to $item.From.address -from $ResetEmail -subject "Invalid email " -body "Your email was not found." -SmtpServer $SmtpServer
  $item.Isread = $true

The script works by doing the following,
- An email is sent from the account that cannot login to a specific account such as above PassReset@email.com, this is important that the email was sent FROM the account that has the associated email address you want to reset
- AD lookup is done filtering by the email address that it was received from.
- email sent back from a different address such as donotreply@email.com to prevent a mail loop from automatic replies with the username and password reset

This works off the concept that the email address that is sending the email is secure and as such is the authoritative source to find the account and provide details for.

I then used the following
to allow the user to change there password once they have logged in and that's it! we are all done.

I was also suggested this one which looks like it would work, over kill but would work.

I updated the script with a couple more variables to make it easier to configure
forgot to update the download the link

EDIT3: moved the function to the top

EDIT4: updated with a prereq


  1. Where are you setting the users recovery email at in AD? This will be an external account like gmail correct?

    I just want to make sure I'm getting the flow correct...
    User sends an email from an external account to the 'rest-pw@exchange-doamin.com' account
    The script sees the email from the external account in the 'rest-pw@exchange-doamin.com' inbox, resets the pw and sends it back to the users external account.

    1. Hi Zane93,

      We are currently using the mail field as the recovery email address but the script could be easily changed to another ad field with a different email address in it.

      That wasnt clear in my post and i might modify the script to allow the AD field that the recovery email addressed is stored in to be customized.

      Grab me if you have any problems.

    2. the line you need to change is
      $user = Get-ADUser -Filter {mail -eq $EmailAddress} -Properties mail
      $user = Get-ADUser -Filter {whatadfieldyouwant -eq $EmailAddress} -Properties whatadfieldyouwant

    3. I just updated the mail attribute to be a variable if you wanted to use it

    4. I was wondering if I could get some insight. I am a new system admin and I am looking for this password solution. I have entered what I believe is the right information for my setup but when I email the reset email i dont get reply back from either email the reset or the donotreply. If you have some time I would really appreciate some guidance. I was attempting the original way through the other post with the mobile phone but would prefer to do resets through email like yourself.
      I have no real experience in programming except some intro video courses.
      Is this able to be ran from Powershell alone? Im doubting it as I keep getting
      $inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($email,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox)
      How would I test this and how to figure out where I'm going wrong.

      Thanks in advance. I know this is an old write up now.

    5. Hi Andre, Sorry for the late reply. All the configuration needed should be in the variables at the top of the script. If you are not receiving emails it would be most likely the mail server specified but it is difficult to tell without any error messages to go off.


    6. I also forgot to add, make sure you have all the pre-reqs installed with the following,
      RSAT - Remote Server Administration Tools
      Exchange plugin - http://www.microsoft.com/en-us/download/confirmation.aspx?id=42951

  2. This comment has been removed by the author.

  3. Hi there, wonder if you can help with the below error im getting. The script did work on a couple of tests but has since stopped when I was hoping to show my manager. I cant seem to work out why I get the below message? no settings (Other than the ones needed) have changed
    Set-ADAccountPassword : A referral was returned from the server
    At C:\Scripts\PasswordSelfService\ADSelfServicePassowrdResetv1.ps1:47 char:7
    + Set-ADAccountPassword -identity $user.SamAccountName -Reset -NewPassword ( ...
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (exch.test1:ADAccount) [Set-ADAccountPassword], ADException
    + FullyQualifiedErrorId : ActiveDirectoryServer:8235,Microsoft.ActiveDirectory.Management.Commands.SetADAccountPassword

    1. Forgot to Add that if I test it where the email address doesn't exist in the AD account properties I get the email that mentions about it being an invalid email address

    2. Hi Rabbit,

      have you got the Remote Admin Tools for AD installed as well as the exchange api plugin at http://www.microsoft.com/en-us/download/confirmation.aspx?id=42951 ?
      if either of those are not installed it would cause some problems with resetting the password, such as the not recognizing the command.

      I also noticed that i omitted specifying the AD server in my reset password command which may help you as well, the reset command would look like,
      Set-ADAccountPassword -identity $user.SamAccountName -server "server" -Reset -NewPassword (ConvertTo-SecureString -AsPlainText $PW -Force)

      ohh and one last one the account running the script would need to have rights to set the password as well

    3. Hi Rabbit,

      I updated the script to specify the AD server and the mail attribute that is being pulled from AD if you would like to use that instead

    4. Cool dude, i'll give it a go, I think you could be right with it referring to a specific AD server, as a lot of my research would suggest. Also is there a way you use for getting users to sign up to this to save us Admin staff having to it?
      Ive read this link http://deployhappiness.com/gathering-active-directory-mobile-phone-attributes/ and wondered if you did that or had any other way(s)

    5. Success haven't come across the issue anymore with testing. ONe thing I have noticed is that you have the FUNCTION Create-RandomString at the bottom of the script. If I seem to leave it there then the script returns the error of not knowing/finding Create-RandomString.
      To get past this ive simply cut the function and pasted it up the top after the $LoggingUser configuration option. hope this helps. And would still be interested in hearing your thoughts on a user registration. Good work fella

    6. I updated the script with the function from the top. I have seen that problem before but i am running it on a newer server and so completely forgot about it.

      as far as email addresses go, we have a pre-populated management system which syncs to our AD server which i get the email addresses from :) lucky me!

      if you are looking for a self service portal http://www.dovestones.com/ has one of the cheaper ones.

      sorry i couldnt help you on that one

  4. Ive decided to use this (http://deployhappiness.com/gathering-active-directory-mobile-phone-attributes/) for the password reset self sign up. Ive tweaked what I needed and have created a nice GUI front face for our staff using Sapien Powershell Studio. Testing should be within the next fortnight and then live once management sign it off..... thank you again for you're help and advice etc

    1. Would you be willing to share you tweaked version with GUI?