We help IT Professionals succeed at work.

Password expiry email notification to that particular user - Powershell

jack jones
jack jones asked
on
199 Views
Last Modified: 2017-04-21
I have a 15 users i want to check their password expiry status and if its about to expire in 7days i want to sent mail notification to only that particular user.

In this case I m thinking to schedule the script everyday and check for the status. Not good in scripting anyhelp would be apprecicated.
Comment
Watch Question

CoralonSenior Citrix Engineer
CERTIFIED EXPERT

Commented:
This shouldn't be too hard..

Something like this should work..
#These are some basic static variables.. however, some replacements are made:
#     #USER# will be replaced with the user's displayName from ActiveDirectory
#     #DAYS# will be replaced with the number of days until the password actually expires; 
#     #EXPIREDATE# is replaced with the calculated expiration date
$SMTPFrom = 'Change_Your_Password@domain.tld'
$SMTPServer = 'smtp.domain.tld'
$SMTPSubject = "Attention #USER# -- Your password expires in #DAYS# days; please change it as soon as possible"
$SMTPBody = "Your password will be expiring soon (#EXPIREDATE#) which is in #DAYS#, please change it as soon as you can"
$ADBase = 'DC=domain,DC=tld'

# This is a table of the users to monitor.. you will add each one with their login name, and the secondary value is their email address
$UserHash = @{}
$UserHash.Add('user1','user1@domain.tld')
$UserHash.Add('user2','user2@domain.tld')

#This section gets the MaxPasswordAge for the domain -- Please note, this does *not* work if you have granular password policies in place for these users!
#MaxPwdAge is stored in Ticks.. we divide it by TicksPerDay to get the MaxPwdAge in Days
$Root = [ADSI]"LDAP://$ADBase"
$filter = "(&(objectCategory=domainDNS)(distinguishedName=$ADBase))"
$Searcher = New-Object -TypeName System.DirectoryServices.DirectorySearcher($Root, $filter)
$DC = $Searcher.FindOne()
[int64]$MaxPwdAge = [System.Math]::Abs($DC.Properties.Item('maxPwdAge')[0]) 
$MaxPwdAge = $MaxPwdAge / [timespan]::TicksPerDay


#This section actually checks AD for each user, pulls their pwdLastSet value (password last set); calculates the various dates, and sends them an email.
$Searcher = New-Object -TypeName System.DirectoryServices.DirectorySearcher
$Now = [datetime]::Now

foreach ($User in $UserHash.Keys)
{
    $Searcher.Filter = "(&(objectClass=user)(samaccountname=$User))"
    $ThisUser = $Searcher.FindOne()
    [datetime]$pwdLastSet = [datetime]::FromFileTime( $($ThisUser.Properties.pwdLastSet) )
    
    $PasswordAgeInDays = (New-TimeSpan -Start $pwdLastSet -End $Now).Days
    $PasswordExpiresAt = $pwdLastSet.AddDays($MaxPwdAge)
    $PasswordExpiresInDays = $MaxPwdAge - $PasswordAgeInDays

    if ($PasswordAgeInDays -le 7)
    {
        $SMTPTo = $UserHash.Item($User)
        $SMTPSubject = $SMTPSubject.Replace('#USER#', $ThisUser.Properties.DisplayName )
        $SMTPSubject = $SMTPSubject.Replace('#DAYS#', $PasswordExpiresInDays)
        $SMTPBody = $SMTPBody.Replace('#DAYS#', $PasswordExpiresInDays)
        $SMTPBody = $SMTPBody.Replace('#EXPIREDATE#', $PasswordExpiresAt)

        Send-MailMessage -To $SMTPTo -From $SMTPFrom -Subject $SMTPSubject -Body $SMTPBody -SmtpServer $SMTPServer
    }

}

Open in new window


Just save it off as a .ps1 file.. and you can create a scheduled task with a regular user that will run it on whatever schedule you want.. the action would be:
powershell.exe -noprofile -noninteractive -file c:\<path>\<scriptname>.ps1

Coralon

Author

Commented:
Hi Coralon, I have below script i need some logic to setup.
Please help in modification your script is bit difficult to understand

Import-Module ActiveDirectory;
#This contains the list of users to monitor password expiry
$userlist = Get-Content "C:\temp\user.txt";
foreach($user in $userlist)
{Get-ADUser -property * -ldapfilter "(samaccountname=$user)"| select sAMAccountName, Displayname, accountExpirationDate };
 $a = Get-Date
 If(accountexpirationdate - $a = 7)
 {
 ## Need some logic here where it will search for user whose password about to expire in 7 days and put those users in array and mail them individually
 }
 @smtpsetting
 {
 To = ""
 from = ""
 Body =""
 smtpserver = ""
 }
 Sendmail @smtpsetting

Open in new window

Qlemo"Batchelor", Developer and EE Topic Advisor
CERTIFIED EXPERT
Top Expert 2015

Commented:
Using the ActiveDirectory cmdlets is a much more modern way, so good you got that already.
There are some "flaws" in your syntax, though. Don't use a semi-colon, unless you write several commands on the same line.
Get-ADUser already checks for samAccountName (and the display name and some more) by default, you do not need a LDAP filter for that.
Get-ADUser -Property * should only be used for debugging or at the interactive prompt. In scripts you should be specific about the properties, because AD objects are very reach, and that costs memory and performance. So since samAccountName and DisplayName are always part of the result, you only need to add AccountExpirationDate and EmailAddress.
Import-Module ActiveDirectory;

$dt = (get-date).AddDays(7)

#This contains the list of users to monitor password expiry
Get-Content 'C:\temp\user.txt' |
  Get-ADUser -property AcountExpirationDate, EMailAddress |
  ? { $_.AccountExpirationDate -lt $dt } |
  % {
    Send-MailMessage -SmtpServer smtp.company.com -From admin@company.com -To $_.EmailAddress `
                     -Subject "Password will expire at $($_.AccountExpirationDate)"
  }

Open in new window

Note: This code will send repeated notifications each day if the password does not get changed. And since the date includes a time portion, you might want to add one day to $dt to be safe.

Author

Commented:
Hi Qlemo, getting error

Get-ADUser : One or more properties are invalid.
Parameter name: AcountExpirationDate
At line:7 char:3
+   Get-ADUser -property AcountExpirationDate, EMailAddress
Also i didnt get  "since the date includes a time portion, you might want to add one day to $dt to be safe."
Qlemo"Batchelor", Developer and EE Topic Advisor
CERTIFIED EXPERT
Top Expert 2015

Commented:
The last first: Checking against a date is always a bit tricky. If you say "in 7 days", what exactly do you mean? Expiring after today + 7 days at midnight? Or in exactly 7 days = 7*24 hours from now? And because of that, you always need to set up a lower and upper boundary, say expiring between 14-Apr 0:00 and 15-Apr 0:00.
Example:
Now = 07-Apr 10:00
in 7 days = 14-Apr 10:00 or 14-Apr 0:00 or 15-Apr 0:00 or ???

Correcting the error is simple, just use AccountExpirationDate instead. I was missing a "c".

Author

Commented:
Hi Qlemo, its working.

what if i want to add the user name in body and include its manager(this should be from ad propert of that user) in cc how to proceed..

-Body "Hi Uername, <br><br> this is a test line1 <br><br> this is test line 2"
Qlemo"Batchelor", Developer and EE Topic Advisor
CERTIFIED EXPERT
Top Expert 2015

Commented:
How you find out yourself:

Run Get-ADUser WellKnownUser -property * | fl *, and find the desired properties' names. If you know a common name part to search for (we search for manager, so:), use Get-ADUser WellKnownUser -property * | fl *man*, *name*. This shows us that we can use Manager and one or more of DisplayName, GivenName, SurName, SamAccountName.
But Manager is a Distinguished Name, and we should use the display name instead, which we have to find out separately (see code below).

Then using those properties, and doing some more processing plus formatting:
Import-Module ActiveDirectory;

$dt = (get-date).AddDays(7)

#This contains the list of users to monitor password expiry
Get-Content 'C:\temp\user.txt' |
  Get-ADUser -property AccountExpirationDate, EMailAddress, Manager, DisplayName |
  ? { $_.AccountExpirationDate -lt $dt } |
  % {
    $mgr = (Get-ADUser $_.Manager -Property DisplayName).DisplayName
    Send-MailMessage -SmtpServer smtp.company.com -From admin@company.com -To $_.EmailAddress `
                     -Subject "Password will expire at $($_.AccountExpirationDate)" `
                     -BodyAsHTML -Body @"
Hi $($_.DisplayName),<br>
<br>
this is a test line 1<br>
<br>
this is test line 2.<br>
<br>
Please consult $mgr for more details<br>
"@
  }

Open in new window

I chose DisplayName for display, obviously.

Author

Commented:
Thanks Qlemo..
Last query what is happening here why do we use  ?, % . I tried this using foreach.

? { $_.AccountExpirationDate -lt $dt } |
  % {

Open in new window

Qlemo"Batchelor", Developer and EE Topic Advisor
CERTIFIED EXPERT
Top Expert 2015

Commented:
? is short for where-object, % for foreach-object. Those two cmdlets are used that often that they got a very short abbreviation.

Long story

Whenever you need to apply a filter on objects pushed thru the pipeline, first try to apply the filter to the generating cmdlet itself (here it would be Get-ADUser).
If that is not feasible, like here (because you would have to use the inconvenient ticks count as Coralon showed), you filter with where-object.
Only if that is not possible either, e.g. because you have to apply some special logic to some of the objects, but still need to get all of them, you use a switch or if or similar inside a loop.

The reason for that is to get rid of as much as possible of data as early as possible. If the AD has to send over only 10 out of 1000 user obejcts, processing is much faster and needs much less resources (memory, network bandwidth, ...).
A where-object however gets all objects, and then removes those which do not meet the condition. It would be too late for minimizing network traffic, but still local memory is used less, and much less of logic needs to get applied after the where-object.

Author

Commented:
Thanks, but its still not copying the manager in the mail i tried below.. it showing me to supply a parameter for Subject:
-Cc $_.Manager

Open in new window

cmdlet Send-MailMessage at command pipeline position 1
Supply values for the following parameters:
Subject: 

Open in new window


Also what is this symbol for  `  used in the script
Qlemo"Batchelor", Developer and EE Topic Advisor
CERTIFIED EXPERT
Top Expert 2015

Commented:
Can you show the complete Send-MailMessage command? Probably you didn't add the backquote - which is required if you break one command over multiple lines, and it isn't obvious for the parser.

The be precise, backquote is the escape characterr - as last character on a line it is "line continuation", inside of double quotes it starts special characters like `r for "carriage return". And it escapes the meaning of e.g. the dollar sign, making it literal.

Regarding the manager, remember that the AD property is a DN, and you need to make it an email address for CC ....

Author

Commented:
I have changed the get-content. Now m directly querying AD and filtering users there itself to maintain dynamic content.
Filtering users based on their company. all the users with Test company should be notified for this.

$dt = (get-date).AddDays(7)

#This contains the list of users to monitor password expiry
$a = Get-ADUser -Filter {Company -like "Test Company"}  | Select SamAccountName |
  Get-ADUser -property AccountExpirationDate, EMailAddress, Manager, DisplayName |
  ? { $_.AccountExpirationDate -lt $dt } |
  % {
    $mgr = (Get-ADUser $_.Manager -Property DisplayName).DisplayName
    Send-MailMessage -SmtpServer smtp.company.com -Cc cc@test.com -From admin@company.com -To $_.EmailAddress `
                     -Subject "Password will expire at $($_.AccountExpirationDate)" `
                     -BodyAsHTML -Body @"
Hi $($_.DisplayName),<br>
<br>
this is a test line 1<br>
<br>
this is test line 2.<br>
<br>
Please consult $mgr for more details<br>
"@
  }

Open in new window


Also wat is the use of $_.
Qlemo"Batchelor", Developer and EE Topic Advisor
CERTIFIED EXPERT
Top Expert 2015

Commented:
You don't use  Get-ADUser | Get-ADUser - the first one already retrieves the object, so put all properties you need into that.

$a is useless here. There is nothing here in the pipeline that will create any result you can collect, hence $a is always $null.

You changed the CC, but I assume you want to CC to the manager?
$dt = (get-date).AddDays(7)

#This contains the list of users to monitor password expiry
Get-ADUser -Filter {Company -like "Test Company"} -property AccountExpirationDate, EMailAddress, Manager, DisplayName |
  ? { $_.AccountExpirationDate -lt $dt } |
  % {
    $mgr = Get-ADUser $_.Manager -Property EMailAddress, DisplayName
    Send-MailMessage -SmtpServer smtp.company.com `
                     -Cc $mgr.EMailAddress -From admin@company.com -To $_.EmailAddress `
                     -Subject "Password will expire at $($_.AccountExpirationDate)" `
                     -BodyAsHTML -Body @"
Hi $($_.DisplayName),<br>
<br>
this is a test line 1<br>
<br>
this is test line 2.<br>
<br>
Please consult $($mgr.DisplayName) for more details<br>
"@
  }

Open in new window

$_ is "the current object" in loops, switch and some other commands. In Where-Object, for example, where it refers to the current AD user object passed thru the pipeline. It has that meaning in the ForEach-Object loop again, since the object has not been altered by an operation (Select-Object would alter it by adding and removing properties, or Group-Object change the result type completely).

Author

Commented:
Thanks Qlemo..
I thought to use $a and store users in it then iterate. Didnt knew we can directly use Get-ADuser since there can be more then 20k users and we are filtering very few out of 20k that is around 50-60 users whose company is test company.
Since im scheduling this, can we create a if else loop..
if it meets the condition mail to respective user
else mail to admin that everything looks fine.

Can you please suggest me some materials/source for learning ps
Qlemo"Batchelor", Developer and EE Topic Advisor
CERTIFIED EXPERT
Top Expert 2015

Commented:
This makes sense:
$a = Get-ADUser -Filter {Company -like "Test Company"} -property AccountExpirationDate, EMailAddress, Manager, DisplayName |
  ? { $_.AccountExpirationDate -lt $dt }

Open in new window

if you need to process the users more than once, e.g. having both a count and the users itself.
However, not having to store the user objects at all is the best option, as I have shown in above code. I would expand that just by setting a boolean inside the loop, so we know whether there was at least one user found:
$dt = (get-date).AddDays(7)
[bool] $found = $false

#This contains the list of users to monitor password expiry
Get-ADUser -Filter {Company -like "Test Company"} -property AccountExpirationDate, EMailAddress, Manager, DisplayName |
  ? { $_.AccountExpirationDate -lt $dt } |
  % {
    $found = $true
    $mgr = Get-ADUser $_.Manager -Property EMailAddress, DisplayName
    Send-MailMessage -SmtpServer smtp.company.com `
                     -Cc $mgr.EMailAddress -From admin@company.com -To $_.EmailAddress `
                     -Subject "Password will expire at $($_.AccountExpirationDate)" `
                     -BodyAsHTML -Body @"
Hi $($_.DisplayName),<br>
<br>
this is a test line 1<br>
<br>
this is test line 2.<br>
<br>
Please consult $($mgr.DisplayName) for more details<br>
"@
  }

if (!$found)
{
  Send-MailMessage -SmtpServer smtp.company.com -From admin@company.com -To admin@company.com `
                   -Subject 'No password to expire found'
}

Open in new window

Qlemo"Batchelor", Developer and EE Topic Advisor
CERTIFIED EXPERT
Top Expert 2015

Commented:
The best (free) book I can recommend is http://blogs.microsoft.co.il/scriptfanatic/2009/07/14/free-ebook-mastering-powershell-with-drtobias-weltner/. It is kind of outdated, but still helpful for learning the basics, in particular how to help yourself. This got me started enough to be able to answer questions on EE.

https://www.manning.com/books/windows-powershell-in-action-second-edition then showed me many of the more advanced inside stuff. If you want to know that (which is not required), it is a good read after you are familar with PS. The 3rd ediition is in the works, with early access to chapters already finished (and there is only one missing you won't need that soon, unless preparing installation images or the like), at https://www.manning.com/books/windows-powershell-in-action-third-edition .

There is also https://www.manning.com/books/learn-windows-powershell-in-a-month-of-lunches-third-edition, which is recommended often here, but I cannot tell anything about it.

Author

Commented:
Just to add to that logic..
It will still check all the Users with company property set to "Test Company", this will include the expired users as well correct?
and trigger a notification for expired users, instead of excluding them
Qlemo"Batchelor", Developer and EE Topic Advisor
CERTIFIED EXPERT
Top Expert 2015

Commented:
Correct.

Author

Commented:
Get-ADUser -Filter {Company -like "Test Company" -ne "Disabled"} -property AccountExpirationDate, EMailAddress, Manager, DisplayName |
  ? { $_.AccountExpirationDate -lt $dt } 

Open in new window

is this fine for excluding disabled
 users
"Batchelor", Developer and EE Topic Advisor
CERTIFIED EXPERT
Top Expert 2015
Commented:
Unlock this solution and get a sample of our free trial.
(No credit card required)
UNLOCK SOLUTION
Unlock the solution to this question.
Thanks for using Experts Exchange.

Please provide your email to receive a sample view!

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.