First script I have tried to put together. Im trying to get a new variable with ad user name and ad computer by comparing user name property and description properties. I don't know how to pull the properties I want into the new variables based on a compare-object or match. The description property has a setup of username - ######## numbers very.
Variables used (date tell expire)
$SevenDayWarnDate, $ThreeDayWarnDate, $OneDayWarnDate
AD user
$7, $3, $1 -properties "Name", "PasswordExpiry
AD computer
$comp "Name", "Description"
I was then going to make a pop up on user computer based on expiring passwords.
Below is what I was trying to do but im not sure if the needed information was passed as computer filed comes back empty.
$SevenDayWarnDate = (get-date).adddays(7).ToLongDateString()
$7= Get-ADUser -filter {Enabled -eq $True -and PasswordNeverExpires -eq $False -and PasswordLastSet -gt 0 } `
-Properties "Name", "msDS-UserPasswordExpiryTimeComputed" | Select-Object -Property "Name", `
#{Name = "PasswordExpiry"; Expression = {[datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed").tolongdatestring() }} `
|Where-object -Property PasswordExpiry -EQ $SevenDayWarnDate
$comp = Get-Adcomputer -Filter {Enabled -eq $True} -SearchBase "OU=,DC=" -properties "Name", "Description" `
| Select-Object -Property "Name", "Description"
Compare-Object -ReferenceObject $7 -DifferenceObject $comp -IncludeEqual -ExcludeDifferent -PassThru |
ForEach-Object {
[PSCustomObject]#{
Name = $_.name
Computer = ($comp.name | Where-Object Description -match $_.name).Directory
}
}
Working code based on Santiago Squarzon below.
$dayArray= #()
$dayArray=#(7,3,1)
foreach ($day in $dayArray)
{
$SevenDayWarnDate = (get-date).adddays($day).ToLongDateString()
$filter = "Enabled -eq '$True' -and PasswordNeverExpires -eq '$False' -and PasswordLastSet -gt '0'"
$computerArray= #()
$users = Get-ADUser -Filter $filter -Properties "Name", "msDS-UserPasswordExpiryTimeComputed" |
Select-Object Name, #{
Name = "PasswordExpiry"
Expression =
{
[datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed").tolongdatestring()
}
} | Where-object -Property PasswordExpiry -EQ $SevenDayWarnDate
# => It might be better to use:
# PasswordExpiry -ge [datetime]::Now -and PasswordExpiry -le $sevenDayWarnDate
# Find the computers each user is using
$result = foreach($user in $users)
{
$temp=$user.Name
if ($comp = Get-ADComputer -Filter "Description -like '*$temp*'" -Properties Description)
{
[PSCustomObject]#{
Name = $user.Name
PasswordExpiry = $user.PasswordExpiry
ComputerName = $comp.Name
ComputerDescription = $comp.Description
}
$tmpArray= #()
$tmpArray= $comp.Name.Split(" ")
foreach($item in $tmparray)
{
$computerArray += $item
}
$tmpArray = $Null
# }
}
continue
}
foreach($computer in $computerArray)
$tmpMessage =
$tmpMessageTitle =
{Send-RDUserMessage -HostServer $env:COMPUTERNAME -UnifiedSessionID 1 -MessageTitle $tmpMessageTitle -MessageBody $tmpMessage
}
$result | Format-Table
}
Based on the comments and the code in question, I'm guessing this is what you're looking for. There is no need to use Compare-Object, you can simply query Active Directory to get the user's computer based on the Description property.
$SevenDayWarnDate = [datetime]::Now.AddDays(7)
$filter = "Enabled -eq '$True' -and PasswordNeverExpires -eq '$False' -and PasswordLastSet -gt '0'"
$users = Get-ADUser -Filter $filter -Properties "Name", "msDS-UserPasswordExpiryTimeComputed" |
Select-Object Name, #{
Name = "PasswordExpiry"
Expression = {
[datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")
}
} | Where-object -Property PasswordExpiry -EQ $SevenDayWarnDate
# => It might be better to use:
# {$_.PasswordExpiry -ge [datetime]::Now -and $_.PasswordExpiry -le $sevenDayWarnDate}
# Find the computers each user is using
$result = foreach($user in $users)
{
if($comp = Get-ADComputer -LDAPFilter "(description=$($user.Name))" -Properties Description)
{
[PSCustomObject]#{
Name = $user.Name
PasswordExpiry = $user.PasswordExpiry
ComputerName = $comp.Name
ComputerDescription = $comp.Description
}
continue
}
Write-Host "No computer was found for User: $($user.Name)"
}
$result | Format-Table
Related
I'm working on script to pull the users' name, email address, and their manager info. I need some help. I have this so far
$requestedUsers = Import-Csv "ADUserlist.csv"
$allUsers = Get-ADUser -filter 'Enabled -eq $true' -Properties name, EmailAddress, Manager
$filterdUsers = $allUsers | Where-Object { $_.SamAccountName -in$requestedUsers.SamAccountName }
$report = foreach ($user in $filterdUsers) {
$managerEmail = $allUsers |
Where-Object DistinguishedName -eq $user.Manager |
Select-Object -ExpandProperty EmailAddress
[PSCustomObject][ordered]#{
DisplayName = $user.Name
EmailAddress = $user.EmailAddress
ManagerEmail = $managerEmail
}
}
$report | Out-GridView
there is no output I don't know where exactly I made a mistake. So I need help if there is any changes to be made.
It should be something like this
Import-Module -name 'ActiveDirectory'
$requestedUsers = Import-Csv -path "ADUserlist.csv" #Full path required
$allUsers = Get-ADUser -filter 'Enabled -eq $true' -Properties 'name', 'EmailAddress', 'Manager'
$filterdUsers = $allUsers | Where-Object { $_.SamAccountName -in $requestedUsers.SamAccountName }
$report = #()
foreach ( $user in $filterdUsers ) {
$managerEmail = ( $allUsers | Where-Object { $_.DistinguishedName -eq $user.Manager } ).EmailAddress
$PSO = [PSCustomObject]#{
DisplayName = $user.Name
EmailAddress = $user.EmailAddress
ManagerEmail = $managerEmail
}
$report += $PSO
}
$report | Out-GridView
If you run this script on AD controller, first you must import module ActiveDirectory.
If you run this script on non AD controller you must use powershell remoting to connect to AD controller and run the script. And of course you must have privilege to run such scripts.
I did some PowerShell script to find inactive users in AD that are 90 days old looping through all DCs to also get LastLogon attribute. I also need some extra attributes that only ADUser can bring out. I've got an error when running my script with the piping in the $users = Search-ADAccount line.
Import-Module ActiveDirectory
function Get-ADUsersLastLogon() {
$dcs = Get-ADDomainController -Filter {Name -like "*"}
$OUs = #()
$OU += "ou=Users-A,ou=Users,ou=Items,dc=mydc,dc=com"
$OU += "ou=Users-B,ou=Users,ou=Items,dc=mydc,dc=com"
$time = 0
$exportFilePath = "c:\tmp\lastLogon-test $(get-date -f dd-MM-yyyy).csv"
$columns = "name;username;whencreated;whenchanged;DNname;datetime"
#InactiveTest
$InactiveFilter = #{
UsersOnly = $true
AccountInactive = $true
TimeSpan = New-Timespan -Days 90
}
#EndInactiveTest
Out-File -FilePath $exportFilePath -Force -InputObject $columns
foreach ($OU in $OUs) {
$users = Search-ADAccount #InactiveFilter |
Get-ADUser -Filter * -SearchBase $OUs -Property displayName, whenCreated, whenChanged
foreach ($user in $users) {
foreach($dc in $dcs) {
$hostname = $dc.HostName
$currentUser = Get-ADUser $user.SamAccountName |
Get-ADObject -Server $hostname -Properties lastLogon
if ($currentUser.LastLogon -gt $time) {
$time = $currentUser.LastLogon
}
}
$dt = [DateTime]::FromFileTime($time)
$row = $user.displayName + ";" + $user.SamAccountName + ";" +
$user.whenCreated + ";" + $user.whenChanged + ";" +
$user.distinguishedName + ";" + $dt
Out-File -FilePath $exportFilePath -Append -NoClobber -InputObject $row
$time = 0
}
}
}
Get-ADUsersLastLogon
I think iterating through DC's and OU's and then collecting only the inactive users last logon dates could best be done using a Hashtable object as intermediate storage.
This helps avoiding duplicate entries and gives the opportunity to compare the LastLogonDate properties.
For the final output, it uses one single cmdlet called Export-Csv.
Below my (untested) code:
function Get-ADUsersLastLogon {
# get your ad domain
$DomainName = (Get-ADDomain).DNSRoot
# get all DC hostnames as string array
$DCs = Get-ADDomainController -Filter * -Server $DomainName | Select-Object -ExpandProperty Hostname
# create an array of OU distinghuished names used as SearchBase
$OUs = "OU=Users-A,OU=Users,OU=Items,DC=mydc,DC=com", "OU=Users-B,OU=Users,OU=Items,DC=mydc,DC=com"
$exportFilePath = "c:\tmp\lastLogon-test $(Get-Date -Format dd-MM-yyyy).csv"
$InactiveFilter = #{
UsersOnly = $true
AccountInactive = $true
TimeSpan = New-Timespan -Days 90
}
# use a lookup Hashtable to eliminate duplicates and collect only the latest logon dates
$lookup = #{}
# loop through the list of dc's
foreach ($dc in $DCs) {
# loop through the list of OU's
foreach ($ou in $OUs) {
$users = Search-ADAccount #InactiveFilter -SearchBase $ou -Server $dc
foreach($user in $users) {
# get the properties we want from the AD User.
# using the PowerShell property names, we get the dates already converted into DateTime objects.
$usr = Get-ADUser -Identity $user.DistinguishedName -Server $dc -Properties DisplayName, Created, Modified, LastLogonDate |
Select-Object #{Name = 'Name'; Expression = {$_.DisplayName}},
SamAccountName,
#{Name = 'WhenCreated'; Expression = {$_.Created}},
#{Name = 'WhenChanged'; Expression = {$_.Modified}},
#{Name = 'DistinguishedName'; Expression = {$_.DistinguishedName}},
#{Name = 'LastLogon'; Expression = {$_.LastLogonDate}}
if ($usr) {
if ($lookup.ContainsKey($($user.DistinguishedName))) {
# we have collected this user before
$lastLogon = $lookup[$($user.DistinguishedName)].LastLogon
if ($lastLogon) {
if (($usr.LastLogon) -and $lastLogon -lt $usr.LastLogon) {
# only store this new instance if the $user.LastLogon property is of a later date
$lookup[$($user.DistinguishedName)] = $usr
}
}
}
else {
# this is a new user, so add the object to the HashTable
$lookup[$($user.DistinguishedName)] = $usr
}
}
else {
# should never happen..
Write-Warning "User $($user.SamAccountName) not found."
}
}
}
}
# export the objects contained in the $lookup Hashtable as CSV
($output = foreach ($key in $lookup.Keys) {
$lookup.$key
}) | Export-Csv -Path $exportFilePath -NoTypeInformation -Delimiter ';' -Encoding UTF8 -Force
}
Hope that helps
#voilier Sorry, I don't understand how it works for you. Pasted your code and Get-ADUser cmdlet expects filter value. If you use get-help get-aduser -full you will see that searchbase parameter can only be used with Filter or LDAPFilter parameters. More than that neither of them accept pipeline input. Identity parameter accepts pipeline input by value only. so you need to use the distinguishedname property from Search-ADAccount #InactiveFilter for example distinguishedname and pass it to filter
$users = Search-ADAccount #InactiveFilter | %{Get-ADUser -filter {distinguishedname -eq $_.distinguishedname} -SearchBase $OU -Property displayName, whenCreated, whenChanged}
I replaced your $users=... part with the code above and now I see no errors and CSV file created successfully.
Replace your foreach $ou in $ous with this and check the csv file. it works on my computer
Foreach ($ou in $ous){
$users = (Search-ADAccount #InactiveFilter | %{Get-ADUser -filter {distinguishedname -eq $_.distinguishedname} -SearchBase $OU -Property displayName, whenCreated, whenChanged})
foreach ($user in $users) {
foreach($dc in $dcs) {
$hostname = $dc.Name
$last_logon_time=((Get-ADUser $user.SamAccountName | Get-ADObject -Server "$hostname" -Properties lastLogon) |?{$_.lastlogon -gt $time}) | select -ExpandProperty lastlogon
}
$dt = [DateTime]::FromFileTime("$last_logon_time")
$row = $user.displayName + ";" + $user.SamAccountName + ";" +
$user.whenCreated + ";" + $user.whenChanged + ";" +
$user.distinguishedName + ";" + $dt
Out-File -FilePath $exportFilePath -Append -NoClobber -InputObject $row
$last_logon_time = 0
}
}
I hope it helps you
I'm trying to combine the output of two functions with the output of the default Get-ADUser-cmdlet. I'm interested in when an account was created, if it's locked and what it's name is. I also want to know when the user logged on for the last time (using multiple DC's) and if the account is being used as a shared mailbox.
I've written two custom functions Get-ADUserLastLogon and isSharedMailbox, both functions use the Write-Output function to output their output. In case of Get-ADUserLastLogon this will be Lastlogon: time and in case of isSharedMailbox this will be shared: yes/no. I'm also using a standard Get-ADUser call in a foreach loop
Now, the default output of Get-ADUser is:
SAMAccountName LockedOut Created
-------------- --------- -------
ACC False 23-10-2015 8:20:20
Output of the custom functions is as following:
Lastlogon : 1-1-1601 1:00:00
Shared: yes
What I would like is to combine the LastLogon and Shared 'headers' to be combined into the Get-ADUser. So the output would become:
SAMAccountName LockedOut Created LastLogon Shared
Code of current code, where the accounts get imported from an Excel sheet:
foreach($username in $usernameWithTld){
if ($username -eq $NULL){
break
}
$usernameWithoutTld = $username.split('\')
Get-ADUser $usernameWithoutTld[1] -Properties LockedOut, SamAccountName,
Created -ErrorAction Stop | Select-Object SAMAccountName, LockedOut,
Created
Get-ADUserLastLogon -UserName $usernameWithoutTld[1]
# Shared mailbox?
isSharedMailbox -mailboxname $usernameWithoutTld[1]
}
Function code:
function isSharedMailbox([string]$mailboxname){
$isObject = Get-ADUser -Filter {name -eq $mailboxname} -SearchBase "..." | Select-Object DistinguishedName,Name
if ($isObject -match "DistinguishedName"){
$output = "Shared: no"
Write-Output $output
} else {
$output = "Shared: No"
Write-Output $output
}
}
function Get-ADUserLastLogon([string]$userName){
$dcs = Get-ADDomainController -Filter {Name -like "*"}
$time = 0
foreach($dc in $dcs)
{
$hostname = $dc.HostName
$user = Get-ADUser $userName | Get-ADObject -Properties lastLogon
if($user.LastLogon -gt $time)
{
$time = $user.LastLogon
}
}
$dt = [DateTime]::FromFileTime($time)
Write-Output "LastLogon : $dt"
}
I'm sure there are lots of improvements that can be made, I'm still learning how to write (proper) PowerShell. I hope someone can answer my question.
You could use a Calculated Property in your Select-Object. Have a look at example 4 for the MSDN page.
In your case this would be:
Get-ADUser $usernameWithoutTld[1] -Properties LockedOut, SamAccountName, Created -ErrorAction Stop | `
Select-Object SAMAccountName, LockedOut, Created, #{Name='LastLogon';Expression={Get-ADUserLastLogon -UserName $usernameWithoutTld[1]}}, #{Name='IsSharedMailbox';Expression={isSharedMailbox -mailboxname $usernameWithoutTld[1]}}
Or even better, you can use the object(s) that Get-ADUser puts in the pipeline to in turn call your functions for that specific object, and can be useful in case your query returns multiple results:
Get-ADUser $usernameWithoutTld[1] -Properties LockedOut, SamAccountName, Created -ErrorAction Stop | `
Select-Object SAMAccountName, LockedOut, Created, #{Name='LastLogon';Expression={Get-ADUserLastLogon -UserName $_.sAMAccountName}}, #{Name='IsSharedMailbox';Expression={isSharedMailbox -mailboxname $_.sAMAccountName}}
One way to do this is to get your functions to return the values you are interested in, store them in variables, and combine everything together afterwards into a PSObject containing the properties you are interested.
The benefits of storing as an object are many. For example, you can use Select-Object, Sort-Object etc in the pipeline, or Export-CSV and other Cmdlets that expect InputObject
foreach($username in $usernameWithTld){
if ($username -eq $NULL){
break
}
$usernameWithoutTld = $username.split('\')
$adDetails = Get-ADUser $usernameWithoutTld[1] -Properties LockedOut, SamAccountName,
Created -ErrorAction Stop | Select-Object SAMAccountName, LockedOut,
Created
$lastlogin = Get-ADUserLastLogon -UserName $usernameWithoutTld[1]
# Shared mailbox?
$isshared = isSharedMailbox -mailboxname $usernameWithoutTld[1]
# putting together the PSobject
[array]$myResults += New-Object psobject -Property #{
SAMAccountName = $adDetails.SAMAccountName
LockedOut = $adDetails.LockedOut
Created = $adDetails.Created
LastLogon = $lastlogin
Shared = $shared # true/false or yes/no, depending on function
#Shared = if($shared){"yes"}else{"no"} # yes/no, based on true/false from function
}
}
Functions:
function isSharedMailbox([string]$mailboxname){
$isObject = Get-ADUser -Filter {name -eq $mailboxname} -SearchBase "..." | Select-Object DistinguishedName,Name
return ($isObject -match "DistinguishedName") # returns true/false
<# if you prefer to keep yes/no
if ($isObject -match "DistinguishedName"){
return "Yes" # no in original code
} else {
return "No"
}
#>
}
function Get-ADUserLastLogon([string]$userName){
$dcs = Get-ADDomainController -Filter {Name -like "*"}
$time = 0
foreach($dc in $dcs)
{
$hostname = $dc.HostName
$user = Get-ADUser $userName | Get-ADObject -Properties lastLogon
if($user.LastLogon -gt $time)
{
$time = $user.LastLogon
}
}
$dt = [DateTime]::FromFileTime($time)
return $dt
#Write-Output "LastLogon : $dt"
}
You can store the result of the functions in global variables and finally concatenate them is one way.
Else you can use return the output from the function and use the value later or like : $value= functionname then $value will hold the return value of the function and later you can combine the results.
function isSharedMailbox([string]$mailboxname){
$isObject = Get-ADUser -Filter {name -eq $mailboxname} -SearchBase "..." | Select-Object DistinguishedName,Name
if ($isObject -match "DistinguishedName"){
$output = "Shared: no"
$Global:result1= $output
} else {
$output = "Shared: No"
$Global:result1= $output
}
}
function Get-ADUserLastLogon([string]$userName){
$dcs = Get-ADDomainController -Filter {Name -like "*"}
$time = 0
foreach($dc in $dcs)
{
$hostname = $dc.HostName
$user = Get-ADUser $userName | Get-ADObject -Properties lastLogon
if($user.LastLogon -gt $time)
{
$time = $user.LastLogon
}
}
$dt = [DateTime]::FromFileTime($time)
$Global:result2= "LastLogon : $dt"
}
## Calling the function . Change the placeholders accordingly
Get-ADUserLastLogon -UserName $usernameWithoutTld[1]
isSharedMailbox -mailboxname $usernameWithoutTld[1]
$FinalResult = "result1" + "result2"
$FinalResult
Hope it helps you better understanding.
I put together the below, which does the job. However, the output isn't very workable. So I wanted to output this all to a CSV using Export-Csv. Im aware I can do this by moving to a ForEach-Object query, but im not entirely sure how to achieve that.
I have added an attempt to convert it in hopes of a little help. I'm not sure how to specify the variable for each object. For example the first section calls all domains in the forest. How do i use each response in the next piped query? and so on.
$domains = (Get-ADForest).Domains
$controllers = #()
$worked = $false
foreach ($domain in $domains) {
$controller = Get-ADDomainController -Discover -ForceDiscover -DomainName $domain |
Select-Object HostName
$controllers += $controller
}
while (-not $worked) {
try {
foreach ($item in $controllers) {
$value = $item.HostName.Value
Write-Host $value
Write-Host 'Domain Admins'
Get-ADGroupMember -Identity 'Domain Admins' -Server $value |
Get-ADUser -Properties name, samaccountname, Description, EmailAddress |
Where {$_.Enabled -eq $true} |
Format-Table Name, SamAccountName, Description, EmailAddress -AutoSize
}
$worked = $true
} catch {}
}
Conversion Attempt
ForEach-Object{
(Get-ADForest).domains | Get-ADDomainController -Discover -ForceDiscover -DomainName $domain |Select-Object HostName | Get-ADGroupMember -identity 'Domain Admins' -Server $value | Get-ADUser -Properties samaccountname, Description, EmailAddress | Where {$_.Enabled -eq $true}
}| Export-Csv -Path "$HOME/Desktop/DomainAdmins.csv" samaccountname, Description, EmailAddress -AutoSize
If you can get the values from your Get-ADUser call and put them in an object, you can then pipe to convertto-csv.
Here's an example:
$arr = #([pscustomobject]#{name="name"; sam="samaccountname"}, [pscustomobject]#{name="name2"; sam="samaccountname2"});
$arr | ConvertTo-Csv -NoTypeInformation
"name","sam"
"name","samaccountname"
"name2","samaccountname2"
You could get rid of the Format-Table call. The code I've shown in the example pipes and array of objects into the convertto-csv cmdlet. So if Get-ADUser returns objects, you should be able to pipe right into ConvertTo-CSV or Export-Csv -append
The objects are hashtables that are cast to pscustomobjects, it's a nice quick way to illustrate the technique.
The result, as shown, will be csv headers that match your hashtable keys, and the hastable values will be the CSV values.
This is working fine in my local environment and storing the result in D:\Test_File.csv
$domains = (Get-ADForest).Domains
$controllers = #()
$worked = $false
foreach ($domain in $domains) {
$controller = Get-ADDomainController -Discover -ForceDiscover -DomainName $domain | Select-Object HostName
$controllers += $controller
}
while (-not $worked) {
try
{
foreach ($item in $controllers)
{
$value = $item.HostName.Value
Write-Host $value
Write-Host 'Domain Admins'
Get-ADGroupMember -Identity 'Domain Admins' -Server $value |
Get-ADUser -Properties name, samaccountname, Description, EmailAddress |?{$_.Enabled -eq $true}|Export-Csv -Append "D:\Test_File.csv"
}
#$worked = $true
}
catch
{
$Error_Message=$_.Exception.Message
}
}
I have a list of users full name that I'd like to get their SAMaccount name but when I run my code I get no results. Anyone have any ideas?
$users = Get-Content C:\users\admin\Desktop\move.txt
foreach ($user in $users){
Get-ADUser -Filter {Name -eq "$user"} |Select-Object name, samaccountname
}
It could be that $user is null inside the script block. Try to use double quotes instead of braces and put the variable in single quotes to make the valid query (name contain spaces):
Get-ADUser -Filter "Name -eq '$user'" | Select-Object name, samaccountname
Here is a powershell script that should work for you. You'll want to go to the link to read the fine details, http://wbarena.com/2015/01/powershell-find-ad-user-full-name.html.
Import-Module ActiveDirectory
$aResults = #()
$List = Get-Content “.\List.txt”
ForEach($Item in $List){
$Item = $Item.Trim()
$User = Get-ADUser -Filter{displayName -like $Item -and SamAccountName -notlike “a-*” -and Enabled -eq $True} -Properties SamAccountName, GivenName, Surname, telephoneNumber, mail
$hItemDetails = New-Object -TypeName psobject -Property #{
FullName = $Item
UserName = $User.SamAccountName
Email = $User.mail
Tel = $User.telephoneNumber
}
#Add data to array
$aResults += $hItemDetails
}
$aResults | Export-CSV “.\Results.csv”
Use first and last name separately.
foreach ($user in $users){
$SplitName = -split $user
Get-ADUser -Filter {(GivenName -eq $SplitName[0]) -and (Surname -eq $splitName[1])} |Select-Object name, samaccountname
}