Today Date and Password Expirary Date less than 14 Days? - powershell

I want to determine all users from several Domains where the difference between Password expiration date and today date is less than 14 days. Later I want to write a notification mail to all users with powershell. How do I write the Content from Surname to a variable?
Here is my given Code:
$datacoll =#()
$domains = "domain1","domain2","domain3"
$expindays = 14
$today = Get-Date
foreach($domain in $domains){
$datacoll += Get-ADUser -Server $domain -filter {Enabled -eq $True -and PasswordNeverExpires -eq $False} `
–Properties “SamAccountName”,”mail”,"GivenName","Surname",”pwdLastSet”,”msDS-UserPasswordExpiryTimeComputed” | where{$_.mail -ne $null} |
Select-Object -Property “SamAccountName”,”mail”,"GivenName","Surname",#{Name=”Password Last Set”;`
Expression={[datetime]::FromFileTime($_.”pwdLastSet”)}}, #{Name=”Password Expiry Date”;`
Expression={[datetime]::FromFileTime($_.”msDS-UserPasswordExpiryTimeComputed”)}}
}
#iterate surname?
# foreach($user in $Surname){
# }
#datacoll output
$datacoll | Export-Csv "C:\pw.csv" -NoTypeInformation -Encoding utf8

What you can do is use DirectoryServices and DirectoryServices.AccountManagement which are part of the .NET framework
You can load .Net assemblies like this using system.reflection.assembly
[system.reflection.assembly]::LoadWithPartialName("System.DirectoryServices") | out-null
You then will create a domain context (Connection to AD) and a searcher object that will go through the domain looking for user objects.
I wrote a function to return users that match a range (How close to expiration date) and a Domain. It will then return a array of directory entries so you can get the information you prefer.
function Get-ADPasswordExpirationDates(){
param(
[Parameter(Mandatory=$True)]
[string]$Domain,
[int]$Range = 14
)
#Call .Net Active Directory Assembies
[system.reflection.assembly]::LoadWithPartialName("System.DirectoryServices") | out-null
[system.reflection.assembly]::LoadWithPartialName("System.DirectoryServices.AccountManagement") | out-null
#Get Domain Context
$Context = new-object System.DirectoryServices.AccountManagement.PrincipalContext(`
[System.DirectoryServices.AccountManagement.ContextType]::Domain,`
$domain
)
#Create Searcher Object looking for User Objects
$Search = new-object System.DirectoryServices.AccountManagement.PrincipalSearcher(`
new-object System.DirectoryServices.AccountManagement.UserPrincipal(`
$Context
)
)
#Arry Varable for holding return reply
$Reply = #()
#For Each User in search
foreach($Result in $Search.FindAll()){
#Turn Result into a Directory Entry
[System.DirectoryServices.DirectoryEntry] $Entry = $Result.GetUnderlyingObject()
#Get Expiration Date
$Expiration = $Entry.InvokeGet("PasswordExpirationDate")
#Create a Timespan from Todays date to Expiration date
$Timespan = $(New-TimeSpan -Start $(get-date) -End $Expiration).Days
#If days are less then $RANGE add Directory Entry to array
if($Timespan -lt $Range -and $Timespan -gt -1 ){
$Reply += $Entry
}
}
return $Reply
}
Here is a quick example of how to use it to get information form the directory object.
$AlmostExpiredUsers = Get-ADPasswordExpirationDates -domain MyAwesomeDomainName -Range 14
foreach($User in $AlmostExpiredUsers){
Write-Output "Username : $($user.SamAccountName)`r`nFull Name : $($user.Properties.name)`r`nEmail : $($user.Properties.mail)`r`nExpires : $($user.InvokeGet("PasswordExpirationDate"))`r`n`r`n"
}

I use this script to send email to user about there password expiration
$MaxPasswordAgeTimeSpan = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge
Get-ADUser -Filter * -SearchBase "OU=Admin user,DC=test,DC=test" -properties PasswordLastSet,
PasswordExpired,
PasswordNeverExpires,
EmailAddress,
DisplayName,
GivenName,
SN |
foreach {
$UserName = $_.DisplayName
$SN = $_.SN
$Email = $_.EmailAddress
$today = Get-Date
$enc = New-Object System.Text.utf8encoding
$ExpiryDate = $_.PasswordLastSet + $maxPasswordAgeTimeSpan
$DaysLeft = ($ExpiryDate-$today).days
If ($DaysLeft -lt 7 -and $DaysLeft -gt 0)
{
Send-mailmessage -to $Email -from noreply#noreply.com -Subject "enter subject" -body "enter body here" -smtpserver x.x.x.x -BodyAsHtml -Encoding $enc
}

Here is a minimal solution. The 14 days timespan, you achieve with:
foreach($user in $datacoll){
$StartDate=(Get-Date).Date
$EndDate=([datetime] $user."Password Expiry Date").Date
$diff = (New-TimeSpan -Start $StartDate -End $EndDate).Days
$firstname = $user.GivenName
$surname = $user.Surname
#body content in variable for parameter in Send-MailMessage
$body = #"
Dear Sir or Madam,
Your Password will expire in $diff days,
please change your password.
Sincerely
IT
"#
}
Than you can check the $diff variable with if, else Statement and send mail to all users with:
if($diff -le 14){
Send-MailMessage -from "Helpdesk <helpdesk#noreply.com>" -to "$firstname $surname<$user.mail>" -subject "Password Expiration in $diff days" -smtp "smtpserver" -body $body
}
To check multiple Domain Controllers you can define multiple Domain Controller with:
$dcs = "domaincontroller1","domaincontroller2","domaincontroller3"
And inlcude a foreach Loop.
With the Option -Server you can Transfer the Parameter $i to achieve all users from all Domain Controllers.
Hint: You can only perform this script from your root Domain Controller.
foreach($i in $dcs){
$datacoll += Get-ADUser -Server $i -filter {Enabled -eq $True -and PasswordNeverExpires -eq $False -and mail -like '*'} `
–Properties “SamAccountName”,"GivenName","Surname",”mail”,”pwdLastSet”,”msDS-UserPasswordExpiryTimeComputed” |
Select-Object -Property “SamAccountName”,"GivenName","Surname",”mail”,#{Name=”Password Last Set”;`
Expression={[datetime]::FromFileTime($_.”pwdLastSet”)}}, #{Name=”Password Expiry Date”;`
Expression={[datetime]::FromFileTime($_.”msDS-UserPasswordExpiryTimeComputed”)}}
}
}

Related

How can I sent email once when user was disabled in AD (PowerShell)

I want to improve the script that automatically blocks users via TaskScheduler. I added an email sending function but I want to loop it once and I don't know how to implement something like that. Can you help me a little with this?
My code:
#Importowanie obecnej daty do zmiennej b
$b = (Get-Date).ToString('M"/"d"/"yyyy')
$groups = 'PLKAT-G-ORG-NON Block Users'
$groupClient1 = 'PLKAT-G-ORG-Client1 Block Users Only'
#Function to Send Email Message
function sendMail($Message) {
#SMTP server name
$SmtpServer = "mailserver"
#Creating a mail Object
$msg = new-object Net.Mail.MailMessage
#Credential
$username = "mailserver"
$password = "*************" | ConvertTo-SecureString -AsPlainText -Force
$emailCredential = New-Object System.Net.NetworkCredential($username, $password)
#Add Attachment
#$Attachments = "attached file"
#Creating SMTP server object
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$smtp.Port = 587
$smtp.EnableSsl = $true
$smtp.Credentials = $emailCredential
#Email structure
$msg.From = "my#domain.com"
$msg.To.add("domain#my.com")
$msg.CC.Add("do#my.com")
#$msg.CC.Add("user3#domain.com")
$msg.subject = "***TEST***"
$msg.body = $Message
#$msg.Attachments.Add($Attachments)
#Sending email
$smtp.Send($msg)
"Email Sent"
};
#################### Client1 ############################
Import-Csv "I:\Clients\Client1\Block Accounts\Accounts Deactivation Test.csv" | ForEach-Object {
$SamAccountName = $_."SamAccountName"
$dateDisable = $_."dateDisable"
$dateEnable = $_."dateEnable"
foreach ($group in $groups) {
$members = Get-ADGroupMember -Identity $group -Recursive | Select -ExpandProperty SamAccountName
If ($members -contains $SamAccountName ) {
$Message = " $SamAccountName is a member of NON Block User Group"
sendMail $Message
}
foreach ($group in $groupClient1) {
$members = Get-ADGroupMember -Identity $group -Recursive | Select -ExpandProperty SamAccountName
if ($members -contains $SamAccountName){
$dateDisable -eq $b
Get-ADUser -Identity $SamAccountName | Disable-ADAccount
$Message = "Account: $SamAccountName was disabled"
#I want to send email once when user account is disabled by script, but script run every hour
for($i=1; $i -eq 1; $i++) {
if($i -eq 1){
sendMail $Message
}
}
}
$dateEnable = $_."dateEnable"
if ( $dateEnable -eq $b) {
Get-ADUser -Identity $SamAccountName | Enable-ADAccount
Write-Host "-User "$SamAccountName" Enable"
}
}
}
}

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:
List item
Import one column cvs with just "samAccountName"
reset password of the accounts listed in the csv with a random generated 20 character long password
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 = "it-operation#example.com"
$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
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 = "it-operation#example.com"
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
}
Don't accept my answer. I'm just building off what #Theo did. (I'd love an up-vote though)
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 = "it-operation#example.com"
$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! But, I hope it's helpful...

Powershell script from task scheduler unable to work as intended

I have a script file that when ran from within ISE or from the powershell window it works fine. But when ran trough the task scheduler with "runas" the same account it only outputs half the data.
After some troubleshooting i have concluded that the part of the code that is not working is:
Get-ADOrganizationalUnit -SearchBase $OU -Filter {objectclass -eq "organizationalunit"} -SearchScope onelevel -Properties description |
% {
$mailbox = #( get-user -OrganizationalUnit $_.distinguishedName -resultsize unlimited |
? { $_.title -ne "xxx" -and $_.RecipientType -eq "usermailbox" -and $_.RecipientTypeDetails -ne "RoomMailbox" } )
if ($mailbox.count -gt 0) {
$name = $_.name
$mail_customer = $body
$mail_customer += "<h3>" + $name + "</h3>"
$mail_customer += $mailbox | get-MailboxStatistics |
select displayname, lastlogontime, #{ label = "Mailbox Size(MB)"; expression = {
if ($_.totalitemsize.value.toMB() -gt 25600) { "!!!" + $_.TotalItemSize.Value.ToMB() }
else { $_.TotalItemSize.Value.ToMB() }
} } | Sort-Object displayname | ConvertTo-Html -Fragment | Out-String
$count += $mailbox.count
$stattable += #{$name = $mailbox.count}
$mail_customer = $mail_customer | ForEach-Object { $_ -replace '!!!', '<font color="#FF0000">' }
if ( $_.description -ne $null ) {
$mailaddresses = $_.description.split(",")
Send-MailMessage `
-SmtpServer $SMTPserver `
-Encoding $encoding `
-From "$company <noreply#$companylower.se>" `
-To $mailaddresses `
-Subject "$company report - $name - $date" `
-BAH `
-Body $mail_customer
}
$mail += $mail_customer
$mailaddresses = "";
}
}
I get the email but it does not include the information that these lines should output, any ideas?
Script is called like this: powershell -file c:\temp\scriptname.ps1
This is a classic case of not running something elevated. Nothing in the code was wrong.
In Task Scheduler, make sure "Run with highest privileges" is ticked in for the task or it will not work.

Compare AD against CSV

I am trying to create a script that will compare our AD against all the users in a CSV. Our HR department has the master database for all staff but when they make changes they rarely inform us so they now export all users from the HR database to a CSV.
I need to compare this against our AD and amend anyone that has been found to have changed or any new staff.
I have the below script but it just outputs all staff, I only want the staff that have changed or new staff that are not in AD to be emailed.
write-host "Using default CSV file or C:\scripts\csv\StaffChanges.csv"
$StaffCSVUPath = "C:\scripts\csv\StaffChanges.csv"
$logfile = "C:\scripts\logs\ADvsCMIS.csv"
if(test-path $logfile) {
remove-item $logfile -force
}
function Email {
#Send an email, called with recipient email address and message body
param(
[string] $emailaddress="",
[string] $bodymsg=""
)
$bodymsg += "<p>"
$bodymsg += Get-Content($logfile)
Send-MailMessage -To $emailaddress -From "email#domain.co.uk" -Subject "(AD-CMIS_errors) Errors found between Active Directory and CMIS" -Body $bodymsg -BodyAsHTML -SMTPServer "exchserver"
}
function CheckOutputFile {
#Called with folder\filename and type of file
param(
[string]$outputfilename = "",
[string]$type = ""
)
if(test-path($outputfilename)) {
} else {
write-host "Creating $outputfilename"
$msg = "Forename,Surname,Username,ID"
$msg | out-file($outputfilename)
}
}
#Snap-ins needed to use the commands within the script
if((Get-pssnapin -Name Microsoft.Exchange.Management.Powershell.E2010 -ErrorAction SilentlyContinue) -eq $null){Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010}
if((Get-pssnapin -Name Quest.activeroles.admanagement -ErrorAction SilentlyContinue)-eq $null){Add-pssnapin Quest.activeroles.admanagement}
#import users from csv file
$users = (import-Csv $StaffCSVUpath)
$count=0
$countAD=0
Get-QADUser -searchroot "domain/Users/Staff" -SizeLimit 0 -includedproperties employeeid,displayname | ForEach-Object ($_.samaccountname) {
$found = 0
$countAD+=1
ForEach ($user in $users) {
$count+=1
$inital = $user.forename.substring(0,1)
$name = $user.forename+" "+$user.surname
$dispname = $inital+" "+$user.surname
if ($user.id -eq $_.employeeid) {
if ($user.surname -eq $_.lastname) {
if ($inital -eq $_.firstname) {
if ($name -eq $_.name) {
if ($dispname -eq $_.displayname) {
$found = 1
}
}
}
}
}
if ($found -eq 1){break}
}
if ($found -eq 0) {
if(($_.company -ne "testing") -band ($_.company -ne "service")) {
CheckOutputFile $logfile "LOG"
$msg = "<p>" + $_.firstname +" " + $_.lastname + " " + $_.samaccountname + " "+$_.employeeid +"<p>"
$msg | Out-File $logfile -append
}
}
}
if (test-path $logfile) {
#If there is anything to report
write-host "Emailing Log file to ict"
#Email file if $outputB exists
$email = "email#domain.co.uk"
$body = "Action Required: The users below do not exist within HR. Contact HR Data manager to resolve issue, delete users manually if required."
#email ict
Email $email $body
}
I don't use the Quest AD cmdlets, so my answer will be based on the built-in ones. Also, I'm going to assume that the (unique) employee ID for any given employee is not going to change and that there are no user accounts with an empty employeeId attribute.
First, prepare your data like this:
Import-Module ActiveDirectory
$hrUsers = #{}
Import-Csv 'C:\path\to\your.csv' |
select id, firstname, surname,
#{n='inital';e={$_.forename.substring(0,1)}},
#{n='name';e={$_.forename+" "+$_.surname}},
#{n='dispname';e={$_.forename.substring(0,1)+" "+$_.surname}} |
% { $hrUsers[$_.id] = $_ }
$adUsers = Get-ADUser -Filter * -Property employeeid |
? { 'testing', 'service' -notcontains $_.company }
This creates a hashtable mapping each employee ID to the object with the respective user's attributes (including the derivative attributes initial, name, and dispname) and a list of AD users (excluding service and test accounts).
With the above you can determine new users like this:
$employeeIDs = #($adUsers | select -Expand employeeId)
$hrUsers.Values | ? { $employeeIDs -notcontains $_.id }
obsolete accounts like this:
$adUsers | ? { $hrUsers.Keys -notcontains $_.employeeId }
and modified users like this:
$adUsers | ? {
$hrUsers[$_.employeeid].surname -ne $_.lastname -or
$hrUsers[$_.employeeid].inital -ne $_.firstname -or
$hrUsers[$_.employeeid].name -ne $_.name -or
$hrUsers[$_.employeeid].dispname -ne $_.displayname
}
I have managed to get it working by changing the search fields
if($user.firstname -eq $_.firstname)
if($user.surname -eq $_.sn)
if($user.ID -eq $_.employeeID)
This now checks AD against the CSV, emails any discrepancies and excludes any emails with the firstname "test" or "careers"
write-host "Using default CSV file or C:\scripts\csv\StaffChanges.csv"
$StaffCSVUPath = "C:\scripts\csv\StaffChanges.csv"
$logfile = "C:\scripts\logs\ADvsHR.csv"
if(test-path $logfile) {
remove-item $logfile -force
}
function Email {
#Send an email, called with recipient email address and message body
param(
[string] $emailaddress="",
[string] $bodymsg=""
)
$bodymsg += "<p>"
$bodymsg += Get-Content($logfile)
Send-MailMessage -To $emailaddress -From "email#domain.co.uk" -Subject "(AD-CMIS_errors) Errors found between Active Directory and CMIS" -Body $bodymsg -BodyAsHTML -SMTPServer "exchserver"
}
function CheckOutputFile {
#Called with folder\filename and type of file
param(
[string]$outputfilename = "",
[string]$type = ""
)
if(test-path($outputfilename)) {
} else {
write-host "Creating $outputfilename"
$msg = "Forename,Surname,Username,ID"
$msg | out-file($outputfilename)
}
}
#Snap-ins needed to use the commands within the script
if((Get-pssnapin -Name Microsoft.Exchange.Management.Powershell.E2010 -ErrorAction SilentlyContinue) -eq $null){Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010}
if((Get-pssnapin -Name Quest.activeroles.admanagement -ErrorAction SilentlyContinue)-eq $null){Add-pssnapin Quest.activeroles.admanagement}
#import users from csv file
$users = (import-Csv $StaffCSVUpath)
$count=0
$countAD=0
Get-QADUser -searchroot "domain/Users/Staff" -SizeLimit 0 -includedproperties employeeid,displayname | ForEach-Object ($_.samaccountname) {
$found = 0
$countAD+=1
ForEach ($user in $users) {
$count+=1
if ($user.firstname -eq $_.firstname) {
if ($user.surname -eq $_.sn) {
if ($user.ID -eq $_.employeeID) {
$found = 1
}
}
}
if ($found -eq 1){break}
}
if ($found -eq 0) {
if(($_.firstname -ne "careers") -band ($_.firstname -ne "test")) {
CheckOutputFile $logfile "LOG"
$msg = "<p>" + $_.firstname +" " + $_.lastname + " "+$_.employeeid +"<p>"
$msg | Out-File $logfile -append
}
}
}
if (test-path $logfile) {
#If there is anything to report
write-host "Emailing Log file to ict"
#Email file if $outputB exists
$email = "email#domain.co.uk"
$body = "Action Required: The users below do not exist within HR. Contact HR Data manager to resolve issue, delete users manually if required."
#email ict
Email $email $body
}

Powershell Script to Disable Inactive AD Users Create Log and Send E-mail

A colleague has reached out to me to create a PowerShell script to do the following:
The script would read the lastlogondate of an AD security group called “Temp Associates”, disable the accounts with lastlogondate > or = 29 days from current date and move to Disabled OU. When it disables it will also change the descripton to the date it was disabled on. Then create a report listing disabled users and email to our global helpdesk.
I've compiled some things together that seem like they should work, but do not. When I run the script I receive no error message and the log file is generated with no data populated. In order to remain SOX compliant I should be able to manipultate the value in $PasswordAge = (Get-Date).adddays(-29) for testing purposes as I'm not sure we have any accounts that meet the requirements currently.
E-mail is working now, just had to create PSCredential to use in send-mailmessage -credential parameter.
I am definitley new to PowerShell and can use all the help I can get. Any suggestions to either improve the existing code or use a different method are welcome, but I'd like to utilize what I already have if possible.
Code Below:
#import the ActiveDirectory Module
Import-Module ActiveDirectory
#Create a variable for the date stamp in the log file
$LogDate = get-date -f yyyyMMddhhmm
#Sets the OU to do the base search for all user accounts, change for your env.
$SearchBase = "CN=Temp Associates,OU=Res Accounts,DC=our,DC=domain,DC=org"
#Create an empty array for the log file
$LogArray = #()
#Sets the number of days to disable user accounts based on lastlogontimestamp and pwdlastset.
$PasswordAge = (Get-Date).adddays(-29)
#Use ForEach to loop through all users with pwdlastset and lastlogontimestamp greater than date set. Also added users with no lastlogon date set. Disables the accounts and adds to log array.
#Add the properties you will be using to ensure they are available.
$DisabledUsers = (Get-ADUser -searchbase $SearchBase -Properties samaccountname, name, distinguishedname -filter {((lastlogondate -notlike "*") -OR (lastlogondate -le $Passwordage)) -AND (enabled -eq $True) -AND (whencreated -le $Passwordage)} )
if ($DisabledUsers -ne $null -and $DisabledUsers.Count > 0) {
ForEach ($DisabledUser in $DisabledUsers) {
#Sets the user objects description attribute to a date stamp. Example "11/13/2011"
set-aduser $DisabledUser -Description ((get-date).toshortdatestring()) -whatif
#Disabled user object. To log only add "-whatif"
Disable-ADAccount $DisabledUser -whatif
#Create new object for logging
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $DisabledUser.name
$obj | Add-Member -MemberType NoteProperty -Name "samAccountName" -Value $DisabledUser.samaccountname
$obj | Add-Member -MemberType NoteProperty -Name "DistinguishedName" -Value $DisabledUser.DistinguishedName
$obj | Add-Member -MemberType NoteProperty -Name "Status" -Value 'Disabled'
#Adds object to the log array
$LogArray += $obj
}
# Move disabled users in Temp Associates group to Disabled OU
Search-ADAccount –AccountDisabled –UsersOnly –SearchBase “CN=Temp Associates,OU=Res Accounts,DC=our,DC=domain,DC=org” |
Move-ADObject –TargetPath “OU=Disabled,DC=our,DC=domain,DC=org” -WhatIf
#Exports log array to CSV file in the temp directory with a date and time stamp in the file name.
$logArray | Export-Csv "C:\Temp\User_Report_$logDate.csv" -NoTypeInformation
#Create PSCredential for use in e-mail -credential parameter
$secpasswd = ConvertTo-SecureString "PasswordHere" -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ("UserHere", $secpasswd)
#Send e-mail to Global Helpdesk with report generated
$emailFrom = "smtp#address.com"
$emailTo = "User#address.com"
$subject = "NA Disabled Temp Users to be deleted"
$smtpServer = "smtp.address.com"
$attachment = "C:\Temp\User_Report_$logDate.csv"
Send-MailMessage -To $emailTo -From $emailFrom -Subject $subject -SmtpServer $smtpServer -attachment $attachment -credential $mycreds
}else {
Write-Output "No disabled users to process for $PasswordAge."
#Create PSCredential for use in e-mail -credential parameter
$secpasswd = ConvertTo-SecureString "PasswordHere" -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ("UserHere", $secpasswd)
#Send e-mail to Global Helpdesk with report generated
$emailFrom = "smtp#address.com"
$emailTo = "User#address.com"
$subject = "NA Disabled Temp Users to be deleted"
$smtpServer = "smtp.address.com"
$attachment = "C:\Temp\User_Report_$logDate.csv"
Send-MailMessage -To $emailTo -From $emailFrom -Subject $subject -Body "No disabled users to process for $PasswordAge." -SmtpServer $smtpServer -credential $mycreds
}
Putting it as an answer, even though it is not a direct answer.
It is really hard to say what is wrong especially when you are not implementing any checks along the way. A basic debugging strategy would be to add a few outputs along the way to see if the script is hitting sections. Such was: write-output "Entering Foreach" and write-output "Looping user $($DisabledUser.samaccountname)" to ensure that your script is executing properly. This will help determine where your hiccup is.
Alternatively, where I would first look is in your Get-ADUser query. Run that alone and make sure it returns users. If not get it to where it returns expected results.
Here is a revised version of your code that has an error check if there are no users returned.
#import the ActiveDirectory Module
Import-Module ActiveDirectory
#Create a variable for the date stamp in the log file
$LogDate = get-date -f yyyyMMddhhmm
#Sets the OU to do the base search for all user accounts, change for your env.
$SearchBase = "CN=Temp Associates,OU=Res Accounts,DC=our,DC=domain,DC=org"
#Create an empty array for the log file
$LogArray = #()
#Sets the number of days to disable user accounts based on lastlogontimestamp and pwdlastset.
$PasswordAge = (Get-Date).adddays(-29)
#Use ForEach to loop through all users with pwdlastset and lastlogontimestamp greater than date set. Also added users with no lastlogon date set. Disables the accounts and adds to log array.
#Add the properties you will be using to ensure they are available.
$DisabledUsers = (Get-ADUser -searchbase $SearchBase -Properties samaccountname, name, distinguishedname -filter {((lastlogondate -notlike "*") -OR (lastlogondate -le $Passwordage)) -AND (enabled -eq $True) -AND (whencreated -le $Passwordage)} )
if ($DisabledUsers -ne $null -and $DisabledUsers.Count > 0) {
ForEach ($DisabledUser in $DisabledUsers) {
#Sets the user objects description attribute to a date stamp. Example "11/13/2011"
set-aduser $DisabledUser -Description ((get-date).toshortdatestring()) -whatif
#Disabled user object. To log only add "-whatif"
Disable-ADAccount $DisabledUser -whatif
#Create new object for logging
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $DisabledUser.name
$obj | Add-Member -MemberType NoteProperty -Name "samAccountName" -Value $DisabledUser.samaccountname
$obj | Add-Member -MemberType NoteProperty -Name "DistinguishedName" -Value $DisabledUser.DistinguishedName
$obj | Add-Member -MemberType NoteProperty -Name "Status" -Value 'Disabled'
#Adds object to the log array
$LogArray += $obj
}
# Move disabled users in Temp Associates group to Disabled OU
Search-ADAccount –AccountDisabled –UsersOnly –SearchBase “CN=Temp Associates,OU=Res Accounts,DC=our,DC=domain,DC=org” |
Move-ADObject –TargetPath “OU=Disabled,DC=our,DC=domain,DC=org” -WhatIf
#Exports log array to CSV file in the temp directory with a date and time stamp in the file name.
$logArray | Export-Csv "C:\Temp\User_Report_$logDate.csv" -NoTypeInformation
#Send e-mail to Global Helpdesk with report generated
$emailFrom = "sender#mail.com"
$emailTo = "recipient#mail.com"
$subject = "NA Disabled Temp Users to be deleted"
$smtpServer = "smtp.server.com"
$attachment = "C:\Temp\User_Report_$logDate.csv"
Send-MailMessage -To $emailTo -From $emailFrom -Subject $subject -SmtpServer $smtpServer -attachment $attachment
}else {
Write-Output "No disabled users to process for $PasswordAge."
}
i found that the code in the if is never executed.
You must replace $DisabledUsers.Count > 0 with $DisabledUsers.Count -gt 0