Find Active Accounts No Longer Employed - powershell

I want to create a backstop for lack of notification to IT when an employee leaves. We receive an active employee roster in csv format including the employeeID field monthly. I plan to request for this daily which they can provide via their HRIS system. I'd like to eventually automate via task scheduler a copy from where HR puts the file to a location where we run our scripts - then kick off the below script to run against the copied csv. For now I just need help getting the Powershell script right.
What I would like to do is this:
Search AD for employees with an employeeID (no blanks to be returned)
Import a csv that has a column of employeeIDs
Perform a search against the csv from the AD results
For any employee IDs that exist in AD but not in the csv, send an email address to an address, "user $_.Name not an employee
EmployeeID is our most reliable field as HR doesn't have a list of SamAccountNames and people get married and names and email addresses change. I do not want to automate the process of disabling accounts because that would enable a mechanism for a rogue actor in HR to disable everyone's account.
My script is all wrong but here is the thought process I started with:
# Return employees with an employeeID field populated - we're not concerned with service accounts, consultants, etc
#
$adusers = Get-ADUser -searchbase "OU=MyOU,DC=MyCompany,DC=COM" -Filter {employeeID -like "*" -and enabled -eq $true} -Properties employeeID
#
# Import active roster
#
$csv = Import-Csv C:\temp\activeroster-test.csv
foreach($emp in $csv)
{
$csvID = $csv.employeeID
$csvName = $csv.Name
if($adusers.EmployeeID -notlike $csvID)
{
echo '**not found in roster**'
echo $ADusers.Name
}
}
I haven't got to the email notification part because I can't seem to even get this. It just returns the people in my roster to the tune of the amount of people in the roster. It's backwards. Help!
Edit - updated with email notification:
# Return employees with an employeeID field populated - we're not concerned with service accounts, consultants, etc
$adUsers = Get-ADUser -searchbase "OU=MyOU,DC=Example,DC=COM" -Filter {employeeID -like "*" -and enabled -eq $true} -Properties employeeID
# Email Server info
$SmtpServer = "emailserver.example.com"
$NotificationEmailAddress = "myemail#example.com"
#
# Import active roster
#
$csv = Import-Csv C:\temp\activeroster.csv
foreach ($emp in $adUsers) {
$csvIDList = $csv.EmployeeID
if ($emp.EmployeeID -notin $csvIDList) {
$Body = "The following users are still enabled in Active Directory however not found in the active employee roster " + ($($emp.Name) | out-string)
Send-MailMessage -From $NotificationEmailAddress -To $NotificationEmailAddress -Subject "Active Accounts Not In Employee Roster" -Priority High -dno onFailure -SmtpServer $SmtpServer -Body $Body
}
}
I get an email for each user. Thankfully in my test I am doing a small OU and a sample subset of the roster. Heh! Any advise? I think I may need to create another variable that encompasses all the results, yeah?

Your script isn't all wrong, you're actually pretty close. You're printing all of the returned users from your AD query with each iteration as the affected user, when you just mean to return the one you're iterating over. And you can use -notin on the list you have from the HR csv when comparing to compare to the array of IDs.
# Return employees with an employeeID field populated - we're not concerned with service accounts, consultants, etc
#
$adUsers = Get-ADUser -searchbase "OU=MyOU,DC=MyCompany,DC=COM" -Filter {employeeID -like "*" -and enabled -eq $true} -Properties employeeID
#
# Import active roster
#
$csv = Import-Csv C:\temp\activeroster-test.csv
# Email Server info
$smtpServer = "emailserver.example.com"
$notificationEmailAddress = "myemail#example.com"
[System.Collections.ArrayList]$listOfMissing = #()
foreach ($emp in $adUsers) {
$csvIDList = $csv.EmployeeID
if ($emp.EmployeeID -notin $csvIDList) {
Write-Output "**Employee $($emp.Name) not found in roster**"
$listOfMissing.Add($emp.Name)
}
}
if ($listOfMissing) {
$subject = "Active Accounts Not In Employee Roster"
$body = #"
The following users are still enabled in Active Directory however not found in the active employee roster:
$($listOfMissing | Out-String)
"#
$emailParameters = #{
From = $notificationEmailAddress
To = $notificationEmailAddress
Subject = $subject
Priority = 'High'
Dno = 'onFailure'
SmtpServer = $smtpServer
Body = $body
}
Send-MailMessage #emailParameters
} else {
Write-Output 'Email not sent, no users found'
}
I may also suggest pursuing some regular automated update of that csv with another script, or integration to the HR system, which is usually Workday or something similar, though I know in our positions we sometimes don't have that access. :)
For the email you can check out this SO post.

Related

Exporting last logon date for inactive users via PowerShell

I have a command that will export a list of users who have logged in for 12 months but I am struggling to export the last login date and time.
The command is as follows:
Search-ADAccount –AccountInActive -UsersOnly –TimeSpan 365:00:00:00 –ResultPageSize 2000 –ResultSetSize $null |?{$_.Enabled –eq $True} | Select-Object Name, SamAccountName, DistinguishedName, lastLogon| Export-CSV “C:\Users\Me\Desktop\InactiveUsers.CSV” –NoTypeInformation
But lastLogon is showing a blank in the CSV file.
I am new to PowerShell I understand the command can be made much smoother.
Any help on this is much appreciated.
Search-ADAccount doesn't have an option to pull other attributes from the AD Objects than the default ones, you can use Get-ADUser with an elaborate filter to query the users who haven't logged on for the past year. One option is to query the user's lastLogonTimeStamp attribute however by doing so you're risking not getting accurate results because this attribute is not replicated in real time. To get accurate one must query the user's lastLogon attribute but, since this attribute is not replicated across the Domain, one must query all Domain Controllers to get the latest logon from the user.
For more information on this topic, please check this excellent TechNet Article: Understanding the AD Account attributes - LastLogon, LastLogonTimeStamp and LastLogonDate.
$dateLimit = [datetime]::UtcNow.AddYears(-1).ToFileTimeUtc()
$AllDCs = Get-ADDomainController -Filter *
$logons = #{}
$params = #{
LDAPFilter = -join #(
"(&" # AND, all conditions must be met
"(!samAccountName=krbtgt)" # exclude krbtgt from this query
"(!samAccountName=Guest)" # exclude Guest from this query
"(userAccountControl:1.2.840.113556.1.4.803:=2)" # object is Disabled
"(lastLogon<=$dateLimit)" # lastLogon is below the limit
")" # close AND clause
)
Properties = 'lastLogon'
}
foreach($DC in $AllDCs) {
$params['Server'] = $DC
foreach($user in Get-ADUser #params) {
# this condition is always met on first loop iteration due to ldap filtering condition
if($logons[$user.samAccountName].LastLogon -lt $user.LastLogon) {
$logons[$user.samAccountName] = $user
}
}
}
$logons.Values | ForEach-Object {
[PSCustomObject]#{
Name = $_.Name
SamAccountName = $_.SamAccountName
DistinguishedName = $_.DistinguishedName
lastLogon = [datetime]::FromFileTimeUtc($_.lastLogon).ToString('u')
}
} | Export-CSV "C:\Users\Me\Desktop\InactiveUsers.CSV" -NoTypeInformation

Powershell Get Users that are NOT in multiple Azure AD Group

I am trying to get a list of users that are NOT in multiple Azure AD Groups. I tried different scripts from the internet but then find out that the list is not complete. The code that i tried:
$users = Get-AzureADUser -All $true -Filter "accountEnabled eq true" | Where UserPrincipalName -like '*#company.com'
#list of groups by ObjectID that you want to check if users are NOT a member of
$groupids = #("662627b7-f1bd-4683-819d-36d299e19308", "9080a490-481b-4f7f-9d26-ecf7a186b00d", "3c3a6682-91bf-4afc-8634-7b54999e98b8")
#create hashtable that will contain users
$userht = #{}
Get-AzureADUser -Filter "accountEnabled eq true" | Where UserPrincipalName -like '*#irdeto.com' | ForEach-Object { $userht.Add($_.ObjectId, $_) } #add all AzureAD users to hashtable with ObjectID as unique key
ForEach ($id in $groupids) {
#if user is member of group, remove them from hashtable
Get-AzureADGroupMember -ObjectId $id | foreach-object { $userht.Remove($_.ObjectId) }
}
#return remaining users that are not in specified groups
$userht.Values
When I run this i found a few users that are non in the groups but i also still found a lot of users manually that are not in the group and was not in the outcome of the script.
Tried with your Powershell script getting all the user which have unique DIsplayName and Mail .
Note : Make Sure Every User should have unique DisplayName and Mail. If two user have same DisplayName or Mail it will count as one.
Example : I have total 44 users in AAD and 6 user added in 3 group (each having 2)
After running above script i am getting 36 count but expected is 38. But due to two user have same DisplayName and Mail it counted as one.

Pulling a specific proxyaddress from AD using powershell

I have a list of users in a csv file. This list contains users whose primary SMTP address is not internal to our organization. These are mail users who are having email forwarded elsewhere.
They have a proxyaddress listed in AD that is on their AD account that points to the organization and this is what I am trying to get to. The problem is that the proxyaddresses does not put the email in the same location so I need to somehow extrapolate the email(s). that match a certain criteria.
What I would really like to get at is the first.last#example.com or first_last.example.com without the {smtp: } formatting.
I have been able to produce a list of proxyaddresses but again it is just a list.
$users = import-csv $BadEmailList | % {Get-ADUser $_.LoginID -Properties proxyaddresses}
Foreach ($u in $users) {
$proxyAddress = [ordered]#{}
$proxyAddress.add(“User”,$u.name)
For ($i = 0; $i -le $u.proxyaddresses.count; $i++)
{
$proxyAddress.add(“ProxyAddress_$i”,$u.proxyaddresses[$i])
} #end for
[pscustomobject]$proxyAddress |
Export-Csv -Path $ProxyAddressList -NoTypeInformation –Append -Force
Remove-Variable -Name proxyAddress } #end foreach
What I am trying to get is the something similar to the following:
User ProxyAddress_0
---- -----
User1 first.last#example.com
If you just want to find the specific AD user with a given proxyaddress, as the header implies, you should be able to use a LDAP filter like this:
Get-ADUser -LDAPFilter "(&(objectCategory=person)(objectClass=user)(|(proxyAddresses=*:first.last#example.com)))"

move AD group by file

I'm new with PS and doing my first steps..
I have a file named "C:\temp\used_groups.csv".
The file has email address of AD Groups populated by Powershell script to check which distributions group are being used in 365.
Now I want to be able to move them to different OU.
the file has some AD group's email address as follow:
"RecipientAddress"
"test#test.com"
"test1#test.com"
"test2#test.com"
is it possible to move the AD group only by their email address attribute?
how can I resolve the group's sAMAccountName attribute by their email address attribute?
this is what I tried with no success:
$Groups=import-csv "C:\temp\used_groups.csv"
ForEach ($Group in $Groups){
Get-ADGroup -Filter "mail -like $Group "
# rest of script.. not done yet.
}
I would do something like this:
# put the DistinghuishedName of the destination OU here
$destinationOU = "OU=Test,DC=Fabrikam,DC=COM"
# read the CSV and grab the 'RecipientAddress' fields in an array
$emailAddresses = (Import-Csv "C:\temp\used_groups.csv").RecipientAddress
foreach ($email in $emailAddresses){
$GroupToMove = Get-ADGroup -Filter "mail -like '$email'"
if ($GroupToMove) {
# Move-ADObject takes the 'DistinghuishedName' or the 'objectGUID' as Identity parameter
# but it also works when piping the group object itself to it.
$GroupToMove | Move-ADObject -TargetPath $destinationOU
Write-Host "Moved group '$($GroupToMove.Name)'."
}
else {
Write-Warning "Could not find group with email address '$email'"
}
}
When using a CSV you have to specify the fieldname. Another problem in your script is the absence of quotes in the AD filter.
Try this:
$Groups=import-csv "C:\temp\used_groups.csv"
ForEach ($Group in $Groups){
(Get-ADGroup -Filter "mail -like '$($Group.RecipientAddress)'").samaccountname
# rest of script.. not done yet.
}
Cheers,
Gert Jan

Filter Get-MessageTrace log on EAC with Multiple Subjects

We use an Office365 email to send emails to multiple recipients. I was told to get the sent message logs and export them into a CSV format so our team can study which messages were delivered and which ones failed. Below I have put together a PowerShell script that connects to EAC and pulls the Sent Message logs for a particular account for the past 2 days. The script is working great however, I wanted to know if there is a way to only get emails with particular subjects. For example, I could use the code below to get the logs but it only filters the list for one subject. How can I filter this using multiple subjects such subject 1, 2, and 3.
$Subject = "Thank you for your business"
$Messages = $null
$Page = 1
do
{
Write-Host "Collecting Messages - Page $Page..."
$Messages = Get-MessageTrace -SenderAddress "no_reply#myCompany.com" -StartDate (Get-Date).AddDays(-2) -EndDate (Get-Date) -PageSize 5000 -Page $Page| Select Received,SenderAddress,RecipientAddress,Subject,Status
if ($myMessages -ne $null)
{
$myMessages | Where {$_.Subject -like $Subject} |Select-Object -Property Received, SenderAddress, RecipientAddress, Subject, Status | Export-Csv C:\Logs\Logs-$PAGE.csv -NoTypeInformation
}
$Page++
$Messages += $myMessages
}
until ($myMessages -eq $null)
Add the multiple subjects into your $subject variable to create a string array.
$Subject = #("Thank you for your business","hello","123","etc")
You can then use the Contains method of the array in the Where block like so.
Where {$Subject.Contains($_.Subject)}
This will filter the emails to only those match the subject exactly.