The I wrote the below script to return the name of all the Organisational Units in the domain that are empty.
I would like the script to also tell me the total number of empty OU's at the end, by counting the lines returned. I've tried assigning an $array to the New-Object, and adding various versions of $array.count or | Measure-Object at the end of the script, but all return 0.
Get-ADOrganizationalUnit -Filter * |
foreach {
$o = Get-ADObject -filter * -searchbase $_.distinguishedname -searchscope 1
$total = ($o | Measure-Object).Count
New-Object psobject -Property #{
Name=$_.distinguishedname
} |
where-object {$total -le "0"}
}
$ou = Get-ADOrganizationalUnit -Filter * |
Where-Object { -not (Get-ADObject -SearchBase $_.DistinguishedName -Filter * -SearchScope OneLevel) }
# get the count
$ou|measure
$list OUs
$ou
Related
In the code below, the userprinciplename will output strings like "LLL_John.Smith#email.com" and XXXX_Jane.Doe#email.com" but i am hoping to extract a substring from that, that being the codes before the "_" character, so LLL and XXXX.
There may be some which do not have an underscore character however and so these would need to be ignored / have the original string it would have returned.
##Check bottom of script for setting specific OU
Function Get-LastLogon {
param(
[string]$OUName
)
# Get all matching OUs on any level
$OUs = Get-ADOrganizationalUnit -Filter "Name -like '$OUName'"
$DCs = Get-ADDomainController -Filter *
# Get all users from each OU from each DC
$ADUsers = Foreach ($OU in $OUs) {
Foreach ($DC in $DCs.HostName) {
Get-ADUser -SearchBase $OU.DistinguishedName -Filter * -Properties LastLogon -server $dc |
Select-Object Name,userPrincipalName, #{n='LastLogon';e={[DateTime]::FromFileTime($_.LastLogon)}}
}
}
# return most recent LastLogon date for each user
$ADUsers |
Group Name,userPrincipalName |
Select Name,userprinciplename, #{n='LastLogon';e={$_.Group.LastLogon | sort -desc | select -First 1}}
} ## End function
##Enter the OU here
Get-LastLogon -OUName 'Clients'
##Un-comment below to export to csv
## | Export-Csv -Path 'C:\temp\UserExport.csv'
Here is what the script looks like now in full:
##Check bottom of script for setting specific OU
Function Get-LastLogon {
param(
[string]$OUName
)
# Get all matching OUs on any level
$OUs = Get-ADOrganizationalUnit -Filter "Name -like '$OUName'"
$DCs = Get-ADDomainController -Filter *
$ADUsers = foreach ($OU in $OUs) {
foreach ($dc in $DCs.HostName) {
Get-ADUser -SearchBase $OU.DistinguishedName -Filter * -Properties lastLogonTimeStamp -Server $dc |
Select-Object Name,UserPrincipalName,
#{Name = 'LastLogon';Expression = {[DateTime]::FromFileTime($_.lastLogonTimeStamp)}},
#{Name = 'UserCode'; Expression = {([regex]'^([^_]+)_.*').Match($_.UserPrincipalName).Groups[1].Value}}
}
} }
# return the most recent LastLogon date for each user
# (only the users with a code prefix in the UserPrincipalName)
$ADUsers | Where-Object { ![string]::IsNullOrWhiteSpace($_.UserCode) } |
Group-Object UserPrincipalName | ForEach-Object {
[PsCustomObject]#{
Name = $_.Group[0].Name
UserCode = $_.Group[0].UserCode
LastLogon = $_.Group.LastLogon | Sort-Object -Descending | Select-Object -First 1
}
}
## End function
$OUcustom = Read-Host -prompt 'Enter OU here or "Clients" for all'
##Enter the OU here
Get-LastLogon -OUName $OUcustom |
##export csv
Export-Csv -path "C:\temp\UserExport_$((Get-Date).ToString("ddMM_HHmm")).csv" -NoTypeInformation
".csv extracted to C:\temp"
pause
Just add another calculated property to the code you have to get the array of user objects:
$ADUsers = foreach ($OU in $OUs) {
foreach ($dc in $DCs.HostName) {
Get-ADUser -SearchBase $OU.DistinguishedName -Filter * -Properties lastLogonTimeStamp -Server $dc |
Select-Object Name,UserPrincipalName,
#{Name = 'LastLogon';Expression = {[DateTime]::FromFileTime($_.lastLogonTimeStamp)}},
#{Name = 'UserCode'; Expression = {if ($_.UserPrincipalName -like '*_*#*') {($_.UserPrincipalName -split '_')[0]} else { $null }}}
# or use regex like:
# #{Name = 'UserCode'; Expression = {([regex]'^([^_]+)_.*').Match($_.UserPrincipalName).Groups[1].Value}}
}
}
Then you can filter out the users that do have such a code:
# return the most recent LastLogon date for each user
# (only the users with a code prefix in the UserPrincipalName)
$ADUsers | Where-Object { ![string]::IsNullOrWhiteSpace($_.UserCode) } |
Group-Object UserPrincipalName | ForEach-Object {
[PsCustomObject]#{
Name = $_.Group[0].Name
UserCode = $_.Group[0].UserCode
LastLogon = $_.Group.LastLogon | Sort-Object -Descending | Select-Object -First 1
}
}
If you want to rule out all users that do not have some code followed by an underscore in their UserPrincipalName property straight away, you can use the filter parameter:
Get-ADUser -SearchBase $OU.DistinguishedName -Filter "UserPrincipalName -like '*_*#*'" -Properties lastLogonTimeStamp -Server $dc
This however will not give you the opportunity to use the collected users for some other purpose, like outputting users who do not have a code prefixed, as would be easy to do with the code above.
P.S. Did you know PowerShell also provides an attribute LastLogonDate, which is the LDAP property lastLogonTimeStamp, converted to local time.
So this was actually more simple than I realised.
I just needed to add in:
#{N='userPrincipalName';E={$_.userPrincipalName.Split("_")[0]}}
To the first and second blocks where UserPrincipleName was being selected.
Will post the full working code below for relevance.
##Check bottom of script for setting specific OU
Function Get-LastLogon {
param(
[string]$OUName
)
# Get all matching OUs on any level
$OUs = Get-ADOrganizationalUnit -Filter "Name -like '$OUName'"
$DCs = Get-ADDomainController -Filter *
# Get all users from each OU from each DC
$ADUsers = Foreach ($OU in $OUs) {
Foreach ($DC in $DCs.HostName) {
Get-ADUser -SearchBase $OU.DistinguishedName -Filter * -Properties LastLogon -server $dc |
Select-Object Name,#{N='userPrincipalName';E={$_.userPrincipalName.Split("_")[0]}}, #{n='LastLogon';e={[DateTime]::FromFileTime($_.LastLogon)}}
}
}
# return most recent LastLogon date for each user
$ADUsers |
Group Name,userPrincipalName |
Select Name,#{N='userprinciplename';E={$_.userprinciplename.Split("_")[0]}}, #{n='LastLogon';e={$_.Group.LastLogon | sort -desc | select -First 1}}
} ## End function
$OUcustom = Read-Host -prompt 'Enter OU here or "Clients" for all'
##Enter the OU here
Get-LastLogon -OUName $OUcustom |
##export csv
Export-Csv -path "C:\temp\UserExport_$((Get-Date).ToString("ddMM_HHmm")).csv" -NoTypeInformation
".csv extracted to C:\temp"
pause
We are using a good script that we would like to extend to search for users everywhere except one OU. How can I do this?
Thanks in advance for your help!
PasswordChangeNotification
How to instert this code?
Get-ADOrganizationalUnit -filter * -SearchBase 'OU=test,DC=test,DC=com' | foreach {
if($_.distinguishedname -ne "OU=not,OU=that,OU=orgUnit,OU=test,DC=test,DC=com"){
$users=Get-ADUser -filter * -searchbase $_.distinguishedname -ResultPageSize 2000 -resultSetSize 500 -searchscope Onelevel | where-object enabled -eq true
$total=($users | measure-object).count
New-Object psobject -Property #{
OU=$_.Name;
A=$Total
}
}
}
On line 132 of the file you've linked to, you'll find the statement that actually queries Active Directory for the users:
$users = get-aduser -filter {(Enabled -eq $true) -and (PasswordNeverExpires -eq $false)} -properties Name, PasswordNeverExpires, PasswordExpired, PasswordLastSet, EmailAddress | where { $_.passwordexpired -eq $false }
Add the following statement to the next line:
$users = $users |Where-Object distinguishedname -notlike "*,OU=not,OU=that,OU=orgUnit,OU=test,DC=test,DC=com"
... and leave the rest of the script as-is
I am trying to exclude an OU (sub-OU) from a search with PowerShell.
This is my code:
Get-ADOrganizationalUnit -filter * -SearchBase 'OU=test,DC=test,DC=com' |
foreach {
$users=Get-ADUser -filter * -searchbase $_.distinguishedname -ResultPageSize 2000 -resultSetSize 500 -searchscope Onelevel | where-object enabled -eq true
$total=($users | measure-object).count
New-Object psobject -Property #{
OU=$_.Name;
A=$Total
}
}
I'm a PowerShell beginner. Can you please help me to exclude an OU from the search results?
Thanks
By adding an if statement:
Get-ADOrganizationalUnit -filter * -SearchBase 'OU=test,DC=test,DC=com' | foreach {
if($_.distinguishedname -ne "OU=not,OU=that,OU=orgUnit,OU=test,DC=test,DC=com"){
$users=Get-ADUser -filter * -searchbase $_.distinguishedname -ResultPageSize 2000 -resultSetSize 500 -searchscope Onelevel | where-object enabled -eq true
$total=($users | measure-object).count
New-Object psobject -Property #{
OU=$_.Name;
A=$Total
}
}
}
I am trying to get AD Users with a last login greater than X number of days, and also retrieve their managers email.
I have searched a lot but not getting what I need, right now below is what I have. It does not filter users with latest login timestamp or not logged on in more than X days.
Once I am able to filter both these attributes I can add the email syntax for each user to send email to their manager.
$dcs = Get-ADDomainController -Filter * | Select-Object -Property Name
foreach ($dc in $dcs)
{
$lastlogon = Get-ADUser -Filter * -SearchBase "OU=Sandbox_Users,DC=twi-test,DC=com" -Properties * | Select-Object -Property displayname,lastlogon,samaccountname,EmailAddress,#{Name = "Manager" ; Expression = {(Get-ADUser $_.Manager).Name}},#{Name = "ManagerEmail" ;`
Expression = {(Get-ADUser $_.manager -Properties emailaddress).EmailAddress}
}}
You should (in theory) be able to do this by filtering on the lastlogondate field as part of the get-aduser command (with a $days variable of the X days you want, or just define an integer directly instead):
-filter { lastlogondate -lt ((get-date).adddays($days)) }
Full code:
$days = 7
$dcs = Get-ADDomainController -Filter * | Select-Object -Property Name
foreach ($dc in $dcs) {
$lastlogon = Get-ADUser -Filter { lastlogondate -lt ((get-date).adddays($days)) } -SearchBase "OU=Sandbox_Users,DC=twi-test,DC=com" -Properties * |
Select-Object -Property displayname,lastlogon,samaccountname,EmailAddress,`
#{Name = "Manager";Expression = {(Get-ADUser $_.Manager).Name}},`
#{Name = "ManagerEmail"; Expression = {(Get-ADUser $_.manager -Properties emailaddress).EmailAddress}}
}
Per your comment regarding how to get the only the result with the most recent lastlogon result, i'd suggest switching to a ForEach-Object loop as you can then pipe the results for further filtering:
$days = 7
$dcs | foreach-object {
Get-ADUser -Server $_ -SearchBase "OU=Sandbox_Users,DC=twi-test,DC=com" -Properties * |
Where-Object { $_.LastLogonDate -le (Get-Date).AddDays(-$days) } |
Select-Object -Property displayname,lastlogon,samaccountname,EmailAddress,`
#{Name = "Manager";Expression = {(Get-ADUser $_.Manager).Name}},`
#{Name = "ManagerEmail"; Expression = {(Get-ADUser $_.manager -Properties emailaddress).EmailAddress}}
} | sort lastlogon -desc | select -first 1
I just noticed also that in your original code you weren't actually making use of the $dcs collection. In this revision i've added a -server switch on the first get-aduser to make use of it.
Trying to run a command in a foreach loop that contains different search locations, e.g.:
$ous ='ou=Staff,dc=example,dc=local', 'ou=Managers,dc=example,dc=local'
$colItems = $ous | ForEach { Get-ADUser -Filter * -SearchBase "ou=Example,dc=example,dc=local" -Properties whenCreated | select -Property Enabled,Name,SamAccountName,whenCreated }
I want to replace the OU in the search query each time
"ou=Example,dc=example,dc=local"
If you use a pipeline with a ForEach-Object loop, you must use the current object variable ($_) to refer to the current object from the pipeline. Change this:
$ous | ForEach { Get-ADUser -Filter * -SearchBase "ou=Example,dc=example,dc=local" -Properties ... }
into this:
$ous | ForEach-Object { Get-ADUser -Filter * -SearchBase $_ -Properties ... }
See about_Automatic_Variables for more information.
I think this was my answer.
$colItems = ForEach ($ou in $ous) { Get-ADUser -Filter * -SearchBase $ou -Properties whenCreated | select -Property Enabled,Name,SamAccountName,whenCreated }