Solved

Bulk change date for user PW resets

Posted on 2016-08-05
11
45 Views
Last Modified: 2016-08-11
Active Directory Functionality level is at 2012 R.  I need to do two things

1. Run a report for the date all users will need to change their PW

2.  Take Users that are bulk together on one day and spread them out.  So if I have 300 users set to reset their pw on same day I need to spread them out a bit.
0
Comment
Question by:Twhite0909
  • 6
  • 4
11 Comments
 
LVL 12

Expert Comment

by:Dustin Saunders
ID: 41744684
Tech support getting flooded on reset day?  :)

What do you consider 'bulk'?
0
 

Author Comment

by:Twhite0909
ID: 41744795
LOL Ya helpdesk got absolutely blown up w 400 users on day.  I still say 100 is bulk.  so I wanna generate a powershell that pipes to csv for Users reset days.  Filter by reset day so I can see anyone over 100 then take that group/groups and somehow tell them disperse......?
0
 
LVL 12

Expert Comment

by:Dustin Saunders
ID: 41744883
Hmm..  this is trickier than I expected, but I'll work on something this afternoon.
0
 

Author Comment

by:Twhite0909
ID: 41744933
Dude you are the man Dustin! LOL Now I already have something that shows me the list of users with expiry date which is

#Get-ADUser -SearchBase "OU=Employees,OU=Birch,DC=birch,DC=com" -filter {Enabled -eq $True -and PasswordNeverExpires -eq $False} –Properties “DisplayName”, “msDS-UserPasswordExpiryTimeComputed” |

#Select-Object -Property “Displayname”,@{Name=“ExpiryDate”;Expression={[datetime]::FromFileTime($_.“msDS-UserPasswordExpiryTimeComputed”)}} | Export-Csv


That worked great and I found our helpdesk exaggerates greatly bc we do not have hundreds of people expiring in 1 day.  We have blocks of 20,30,40 who all expire on the same day.  However management still wants us to get that number down.  So do you think you have anything that that:

A.  Finds all Users in my OU, lists expiry date

and then

B.  adds on top of their current expiry date to be a randomized number between 30-60 days

Does that make sense
0
 
LVL 12

Expert Comment

by:Dustin Saunders
ID: 41744983
This expert suggested creating a Gigs project.
How many grand total users?  I don't think a random bump of 30-60 days is the way to go because potentially some users could get randomly reassigned expiry date and never have to reset password (making the reason for the expiry obsolete).

I think the idea would need to be something like:
Max expiry = 20
Find a day that is bad (40 expiry) or a weekend (40 on sat, 40 on sun).
Take the total number of users to be redistributed and crawl out each day until you can reassign them into days in the future keeping the expiry threshold down under 20.

ex.
40 expire Monday, 17 expire Tuesday, 15 expire Wed, 5 expire Thu.

Take the total on Monday and leave 20.  Then assign 3 users to Tuesday.  Then assign 5 users to Wed.  Then assign the remaining 12 to Thursday.

Now:
20 expire Monday, 20 expire Tuesday, 20 expire Wed, 17 expire Thu.

You could do that once per week.  Then people get pushed ahead, but not forgotten.  I can help with the code to set the password change date but the whole script (if done as above, which is what I'd recommend) is probably going to end up being an hour or so of work so you might consider posting a gig.

When I get a chance to test the pwdLastSet mod code I'll post that though.
1
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 78

Expert Comment

by:David Johnson, CD, MVP
ID: 41744997
You can't change the expiry date.  The days that a password is valid is set in the default domain policy. You can use fine grained password policies and separate OU's to change the time period/complexity requirements.
https://www.lepide.com/lepideauditor/password-expiration-notification.html
<#
.SYNOPSIS
	Create a CSV and HTML Report on Expiring Passwords
.DESCRIPTION
	This script will create a Password Expiration report.  It creates it in 2 formats,
	CSV and HTML.  Both are then emailed to the specified user.
	
	Make sure to edit and change the PARAM section to match your environment
.PARAMETER Path
	Specify the path where you want to save the CSV report.  The script does not save
	the HTML report, but emails it as the body of the email.
.PARAMETER From
	Tell the script who the script is coming "from".
.PARAMETER To
	Tell the script where to send the email
.PARAMETER SMTPServer
	This needs to be the IP address or name of your SMTP relay server.
.OUTPUTS
	CSV:	ExpirationReport.csv in the $Path location
	Email:	HTML version of the same report in the body of the email.  Also attaches
			the CSV to the email.
.EXAMPLE
	.\Report-PasswordExpiration.ps1
	Accepts all defaults as defined in the PARAM section
.EXAMPLE
	.\Report-PasswordExpiration.ps1 -Path d:\myreports -From script@yourdomain.com -To Administrator@yourdomain.com -SMTPServer 192.168.1.25
	Runs the report using D:\myreports as the path to save the CSV report.  Email will be sent
	from "script@yourdomain.com" and sent to "Administrator@yourdomain.com" using 192.168.1.25
	as the SMTP relay server.
.NOTES
	Script:				Report-PasswordExpiration.ps1
	Author:				Martin Pugh
	Function Author:	M. Ali
	Webpage:			www.thesurlyadmin.com
	Twitter:			@thesurlyadm1n
	Spiceworks:			Martin9700
	
	Changelog
        1.01            Added loading of RSAT tools (if they're installed)
		1.0				Initial Version
.LINK
	Blog:				
	Source code:		
#>
Param (
	[string]$Path = "c:\scripts",
	[string]$From = "admin@yourdomain.com",
	[string]$To = "you@yourdomain.com",
	[string]$SMTPServer = "SMTPServerName"
)

Function Get-XADUserPasswordExpirationDate() {
	# Function written by M.Ali
	# http://blogs.msdn.com/b/adpowershell/archive/2010/02/26/find-out-when-your-password-expires.aspx
	# Modified by Martin Pugh
    Param (
		[Parameter(Mandatory=$true,  Position=0,  ValueFromPipeline=$true, HelpMessage="Identity of the Account")]
		[Object] $accountObj
	)

    PROCESS {
        If ($accountObj.PasswordExpired) 
		{	Return "Expired"
        } 
		Else 
		{	If ($accountObj.PasswordNeverExpires) 
			{	Return "Password set to never expire"
            } 
			Else 
			{	$passwordSetDate = $accountObj.PasswordLastSet
                If ($passwordSetDate -eq $null) 
				{	Return "Password has never been set"
                }  
				Else 
				{	$maxPasswordAgeTimeSpan = $null
                    $dfl = (get-addomain).DomainMode
                    If ($dfl -ge 3) 
					{	## Greater than Windows2008 domain functional level
                        $accountFGPP = Get-ADUserResultantPasswordPolicy $accountObj
                        If ($accountFGPP -ne $null) 
						{	$maxPasswordAgeTimeSpan = $accountFGPP.MaxPasswordAge
                        } 
						Else 
						{	$maxPasswordAgeTimeSpan = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge
                        }
                    } 
					Else 
					{	$maxPasswordAgeTimeSpan = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge
                    }
                    If ($maxPasswordAgeTimeSpan -eq $null -or $maxPasswordAgeTimeSpan.TotalMilliseconds -eq 0) 
					{	Return "MaxPasswordAge is not set for the domain or is set to zero!"
                    } 
					Else 
					{	Return ($passwordSetDate + $maxPasswordAgeTimeSpan)
                    }
                }
            }
        }
    }
}

cls
Try { Import-Module ActiveDirectory -ErrorAction Stop }
Catch { Write-Host "Unable to load Active Directory module, is RSAT installed?" -ForegroundColor Red; Exit }

$Result = @()
$Users = Get-ADUser -Filter * -Properties GivenName,sn,PasswordExpired,PasswordLastSet,PasswordneverExpires
ForEach ($User in $Users)
{	$Result += New-Object PSObject -Property @{
		'Last Name' = $User.sn
		'First Name' = $User.GivenName
		UserName = $User.SamAccountName
		Expiration = $($User | Get-XADUserPasswordExpirationDate)
	}
}
$Result = $Result | Select 'Last Name','First Name',UserName,Expiration | Sort 'Last Name'

#Produce a CSV
$Result | Export-Csv $path\ExpirationReport.csv -NoTypeInformation

#Send HTML Email
$Header = @"
<style>
TABLE {border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}
TD {border-width: 1px;padding: 3px;border-style: solid;border-color: black;}
</style>
"@
$splat = @{
	From = $From
	To = $To
	SMTPServer = $SMTPServer
	Subject = "Password Expiration Report"
}
$Body = $Result | ConvertTo-Html -Head $Header | Out-String
Send-MailMessage @splat -Body $Body -BodyAsHTML -Attachments $Path\ExpirationReport.csv

Open in new window

https://community.spiceworks.com/scripts/show/1679-password-expiration-report
1
 
LVL 12

Expert Comment

by:Dustin Saunders
ID: 41745137
Looks like David is correct, that attribute can't be modified to anything other than 0 or -1 by the system (my plan was to take the default domain policy and set it to the appropriate pwsLastSet after math from there).

That doesn't mean you're SOL entirely, because you can set/reset the expiration meaning you can manually reset the expiry then have a database file that gets parsed each day and expires then at a later stamped time.

Route 2 would use precedented password policies in ADAC and then have the script reset the passwords and temporarily put them in an x days expire group so when you reset you set the lifespan.  So, i.e. if they get pushed out 4 days, put them in a 4 day expire group and then reset the pwd expiration so it pulls 4 days.  They'd immediately get pulled out of that group so when the actual reset day occurs they're back in the normal policy window.

But to have the whole thing created is going to fall into the realm of gigs (it's a bit of a project), would probably take a couple of hours.  But if it's a huge problem someone would probably quote it at like $100ish (I think it'd take about 2 hours to write and test).
0
 

Author Comment

by:Twhite0909
ID: 41745611
David/Dustin

Thank you both very much for your help and suggestions.  I appreciate it very much! I will create a gigs project to get this done.
0
 
LVL 12

Accepted Solution

by:
Dustin Saunders earned 500 total points
ID: 41749316
I saw no one had taken up the gig yet and your deadline is tomorrow.  I don't have time to see a gig through to completion, but here is some code to get you going.  I've tested small scale and it seems to be working fine, but set up a test environment or change $searchBase to small OU to test.

The variable $maxExtended is the total number of days you are willing to push out the expiration.  For each day here, you need to create a group called "x Day Expire" (i.e. 1 Day Expire, 2 Day Expire).  Then in ADAC, set a fine grained password policy, no min expiration and the expiration = to the number of days in X (see example for 2 days expiration).

$maxPerDay is the acceptable max per day that can have an expiration.

The script will get everyone's expiration day in the year (i.e. 1-9-2016 = 9, 2-1-2016 = 32) and look ahead for each $maxExtended days to distribute the resets.  If within the acceptable limit of days to push out there isn't room, it will retain it's default expiration date (recommend doing this so people don't have resets that potentially never expire).

Then for each move, we add the user into "x Day Expire" to get the password policy and then reset, and remove from the group.

I don't usually sink this much time into an EE problem, but you can take this and play with it (or open another question if there is an issue).

Import-Module ActiveDirectory

$searchBase = "DC=yourdomain,DC=local"  #root OU to search for users.
$maxPerDay = 20  #acceptable number of passwords to expire the same day.
$maxExtended = 7  #number of days maximum to push back expiration.

function ResetPasswordExpiry($days, $user)
{
    $ADUser = Get-ADUser -Filter {sAMAccountName -eq $user} -Properties pwdLastSet
    $expGroup = "$days Day Expire"
    Write-Host "Moving EXP for $user to $expGroup"
    Add-ADGroupMember $expGroup -Member $user -Confirm:$false
    Set-ADUser $ADUser -Replace @{pwdLastSet=0} -whatif
    Set-ADUser $ADUser -Replace @{pwdLastSet=-1} -whatif
    Remove-ADGroupMember $expGroup -Member $user -Confirm:$false
}

function ExpirationTable
{
    $dt = New-Object System.Data.DataTable
    $c1 = New-Object System.Data.DataColumn 'distinguishedName',([string])
    $c2 = New-Object System.Data.DataColumn 'sAMAccountName',([string])
    $c3 = New-Object System.Data.DataColumn 'expDOY',([int])
    $dt.Columns.Add($c1)
    $dt.Columns.Add($c2)
    $dt.Columns.Add($c3)
    return, $dt
}

function AddRow ($exp, $dn, $sam)
{
    $row = $expTable.NewRow()
    $row.sAMAccountName = $sam
    $row.distinguishedName = $dn
    $row.expDOY = $exp
    $expTable.Rows.Add($row)
}

$expTable = ExpirationTable

$expiringUsers = Get-ADUser -Properties msDS-UserPasswordExpiryTimeComputed -Filter * -SearchBase $searchBase | where {$_.Enabled -eq "True"}

foreach ($user in $expiringUsers)
{
    $expDate = [datetime]::FromFileTime($user."msDS-UserPasswordExpiryTimeComputed")
    AddRow $expDate.DayOfYear $user.DistinguishedName $user.SamAccountName
}

$expTable.DefaultView.Sort = "expDOY"
$expTable = $expTable.DefaultView.ToTable()

$doy = 1

while ($doy -lt 365)
{
    $thisExpires = $expTable.Select("expDOY = $doy")  #get expirations for this Day of Year

    if ($thisExpires.Length -gt $maxPerDay)
    {
        $overload = $thisExpires.Length - $maxPerDay
        $lookrow = $maxPerDay + 1
        $nextDay = $doy + 1
        while ($overload -gt 0 -and $nextDay -le ($doy + $maxExtended))
        {
            $expNextDay = $expTable.Select("expDOY = $nextDay")
            if ($expNextDay.Length -lt $maxPerDay)
            {
                $available = $maxPerDay - $expNextDay.Length
                while ($available -gt 0)
                {
                    $r = $thisExpires[$lookRow]
                    Write-Host $r.sAMAccountName
                    Write-Host $lookrow
                    try{
                    ResetPasswordExpiry ($nextDay - $doy) $r.sAMAccountName
                    $r.expDOY = $nextDay
                    }
                    catch {
                    Write-Host "Row $lookRow was empty"
                    }
                    $available = $available - 1
                    $overload = $overload - 1
                    $lookRow++
                }
            }
            $nextday++        
        }

    }
    $doy++
}

Open in new window


Note that due to this:
    Set-ADUser $ADUser -Replace @{pwdLastSet=0} -whatif
    Set-ADUser $ADUser -Replace @{pwdLastSet=-1} -whatif

Open in new window

it won't actually make any changes until you remove -whatif.  If you have issues you could also start a new gig to work on this existing code.
2dayExpire.png
1
 

Author Closing Comment

by:Twhite0909
ID: 41752578
if i could give more than an A I would.  Thanks alot for helping with this and spending so much of your time on it!
0
 
LVL 12

Expert Comment

by:Dustin Saunders
ID: 41752613
No worries, good luck with this project!
0

Featured Post

Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

In this previous article (https://oddytee.wordpress.com/2016/05/05/provision-new-office-365-user-and-mailbox-from-exchange-hybrid-via-powershell/), we made basic license assignments to users in O365. When I say basic, the method is the simplest way …
Resolve DNS query failed errors for Exchange
This tutorial will walk an individual through the steps necessary to join and promote the first Windows Server 2012 domain controller into an Active Directory environment running on Windows Server 2008. Determine the location of the FSMO roles by lo…
This tutorial will walk an individual through the process of transferring the five major, necessary Active Directory Roles, commonly referred to as the FSMO roles from a Windows Server 2008 domain controller to a Windows Server 2012 domain controlle…

705 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now