Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people, just like you, are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
Solved

Can I use powershell to generate list of users who logged in to computers in AD

Posted on 2016-11-01
14
128 Views
Last Modified: 2016-11-23
hello, I need to gather a list of computers, users who log into the computers and service tags(dell tags). Last logged on time would be helpful as well.

I am trying to clean up my asset management system as well as AD while trying to avoid going around all 200+ computers

It is ok if the dell service/system tag is not possible to obtain the service tag, but the computers and their respective users will be needed.

The users do NOT use roaming profiles. They all log on to their dedicated workstations.

Is there any script I am over looking out there that can help me out here?

Many thanks.
t
0
Comment
Question by:tobe1424
14 Comments
 
LVL 6

Expert Comment

by:Luis Moura
ID: 41869270
0
 
LVL 6

Assisted Solution

by:Niten Kumar
Niten Kumar earned 125 total points
ID: 41869313
Try the powershell script below.



# Applies to: Computers
#
# Description: This script searches for a specific, logged on user on all or
# specific Computers by checking the process "explorer.exe" and its owner.
#
# ********************************************************************************

#Set variables
$progress = 0

#Get Admin Credentials
Function Get-Login {
Clear-Host
Write-Host "Please provide admin credentials (for example DOMAIN\admin.user and your password)"
$Global:Credential = Get-Credential
}
Get-Login

#Get Username to search for
Function Get-Username {
      Clear-Host
      $Global:Username = Read-Host "Enter username you want to search for"
      if ($Username -eq $null){
            Write-Host "Username cannot be blank, please re-enter username!"
            Get-Username
      }
      $UserCheck = Get-ADUser $Username
      if ($UserCheck -eq $null){
            Write-Host "Invalid username, please verify this is the logon id for the account!"
            Get-Username
      }
}
Get-Username

#Get Computername Prefix for large environments
Function Get-Prefix {
      Clear-Host
      $Global:Prefix = Read-Host "Enter a prefix of Computernames to search on (CXX*) use * as a wildcard or enter * to search on all computers"
      Clear-Host
}
Get-Prefix

#Start search
$computers = Get-ADComputer -Filter {Enabled -eq 'true' -and SamAccountName -like $Prefix}
$CompCount = $Computers.Count
Write-Host "Searching for $Username on $Prefix on $CompCount Computers`n"

#Start main foreach loop, search processes on all computers
foreach ($comp in $computers){
      $Computer = $comp.Name
      $Reply = $null
        $Reply = test-connection $Computer -count 1 -quiet
        if($Reply -eq 'True'){
            if($Computer -eq $env:COMPUTERNAME){
                  #Get explorer.exe processes without credentials parameter if the query is executed on the localhost
                  $proc = gwmi win32_process -ErrorAction SilentlyContinue -computer $Computer -Filter "Name = 'explorer.exe'"
            }
            else{
                  #Get explorer.exe processes with credentials for remote hosts
                  $proc = gwmi win32_process -ErrorAction SilentlyContinue -Credential $Credential -computer $Computer -Filter "Name = 'explorer.exe'"
            }                  
                  #If $proc is empty return msg else search collection of processes for username
            if([string]::IsNullOrEmpty($proc)){
                  write-host "Failed to check $Computer!"
            }
            else{      
                  $progress++                  
                  ForEach ($p in $proc) {                        
                        $temp = ($p.GetOwner()).User
                        Write-Progress -activity "Working..." -status "Status: $progress of $CompCount Computers checked" -PercentComplete (($progress/$Computers.Count)*100)
                        if ($temp -eq $Username){
                        write-host "$Username is logged on $Computer"
                        }
                  }
            }      
      }
}
write-host "Search done!"
1
 
LVL 4

Accepted Solution

by:
get-ADuser -F ($_.Name -eq "Todd") earned 250 total points
ID: 41869359
Copy below and place in Windows Powershell ISE.  I just did it and it works.  Read my remarks. Replace in caps the name of your domain controller.  I have it in caps where you change it.   Also rem $Comps pipeline if you decide to use the parameter of this script and replace the loop from $comps to $Computername.





<#Run this funcion under Windows Powershell ISE.  Depending on your environment.  This goes through  every
 computer, and takes a while.  If you decide to use the paramenter Computername, then remove the remarks of
the paramenter.  And just type in an array of machines.  That goes faster...E.g. Get-loggedOnUser -computername -server1,server2,computer1
#>


# Pipeline version otherwise use parameter and rem this out
$Comps =  Get-ADComputer -Filter * -Server YOURDOMAINCONTROLLER -Properties * |select -ExpandProperty  name

function Get-LoggedOnUser {
#Requires -Version 2.0            
 <# [CmdletBinding()]            
 Param            
   (                      
    [Parameter(Mandatory=$true,
               Position=0,                          
               ValueFromPipeline=$true,            
               ValueFromPipelineByPropertyName=$true)]            
    [String[]$ComputerName
   )#End Param
#>  

Begin            
{            
 Write-Host "`n Checking Users . . . "
 $i = 0            
}#Begin          
Process            
{
    $Comps | Foreach-object {
    $Computer = $_
    try
        {
            $processinfo = @(Get-WmiObject -class win32_process -ComputerName $Computer -EA "Stop")
                if ($processinfo)
                {    
                    $processinfo | Foreach-Object {$_.GetOwner().User} |
                    Where-Object {$_ -ne "NETWORK SERVICE" -and $_ -ne "LOCAL SERVICE" -and $_ -ne "SYSTEM"} |
                    Sort-Object -Unique |
                    ForEach-Object { New-Object psobject -Property @{Computer=$Computer;LoggedOn=$_} } |
                    Select-Object Computer,LoggedOn
                }#If
        }
    catch
        {
            "Cannot find any processes running on $computer" | Out-Host
        }
     }#Forech-object(Comptuters)      
           
}#Process
End
{

}#End

}#Get-LoggedOnUser
1
Optimizing Cloud Backup for Low Bandwidth

With cloud storage prices going down a growing number of SMBs start to use it for backup storage. Unfortunately, business data volume rarely fits the average Internet speed. This article provides an overview of main Internet speed challenges and reveals backup best practices.

 
LVL 80

Assisted Solution

by:David Johnson, CD, MVP
David Johnson, CD, MVP earned 125 total points
ID: 41869465
work in progress script
function Get-ComputerUser {
#Requires -Version 3.0            
 [CmdletBinding()]            
 Param            
   (                      
    [Parameter(Mandatory=$true,
               Position=0,                          
               ValueFromPipeline=$true,            
               ValueFromPipelineByPropertyName=$true)]            
    [String[]]$ComputerName
   )#End Param
 Write-Host ("`n Checking Users . . . on Computer:" + $ComputerName)
 $path = '\\'+$ComputerName + '\c$\Users\'
  Get-ChildItem -Path $path -Directory | Select-Object -Property Name, LastWriteTime
 }#Foreach-object(Computers)      
}
}#Get-LoggedOnUser 
function get-staleADusers
{
 [CmdletBinding()]            
 Param            
   (                      
    [Parameter(Mandatory=$true,
               Position=0,                          
               ValueFromPipeline=$true,            
               ValueFromPipelineByPropertyName=$true)]            
    [String]$domain = 'domain.mydom.com',
   [Parameter(Mandatory=$true,
               Position=0,                          
               ValueFromPipeline=$true,            
               ValueFromPipelineByPropertyName=$true)]            
 [int]$DaysInactive = 90,
   [Parameter(Mandatory=$true,
               Position=0,                          
               ValueFromPipeline=$true,            
               ValueFromPipelineByPropertyName=$true)]            
 [string]$csvoutputfile = 'OLD_User.csv'
)
    $time = (Get-Date).Adddays(-($DaysInactive))
    # Get all AD User with lastLogonTimestamp less than our time and set to enable
    Get-ADUser -Filter {LastLogonTimeStamp -lt $time -and enabled -eq $true} -Properties LastLogonTimeStamp |
    select-object -Property Name,@{Name="Stamp"; Expression={[DateTime]::FromFileTime($_.lastLogonTimestamp)}} | export-csv -Path $csvoutputfile -NoTypeInformation
}
function get-stalecomputers{

            
 [CmdletBinding()]            
 Param            
   (                      
    [Parameter(Mandatory=$true,
               Position=0,                          
               ValueFromPipeline=$true,            
               ValueFromPipelineByPropertyName=$true)]            
    [String]$domain = 'domain.mydom.com',
   [Parameter(Mandatory=$true,
               Position=0,                          
               ValueFromPipeline=$true,            
               ValueFromPipelineByPropertyName=$true)]            
 [int]$DaysInactive = 90,
   [Parameter(Mandatory=$true,
               Position=0,                          
               ValueFromPipeline=$true,            
               ValueFromPipelineByPropertyName=$true)]            
 [string]$csvoutputfile = 'Stale_Computers.csv'
)
    $time = (Get-Date).Adddays(-($DaysInactive))
    # Get all AD User with lastLogonTimestamp less than our time and set to enable
    Get-ADComputer -Filter {LastLogonTimeStamp -lt $time -and enabled -eq $true} -Properties LastLogonTimeStamp |
    select-object -Property Name,@{Name="Stamp"; Expression={[DateTime]::FromFileTime($_.lastLogonTimestamp)}} | export-csv -Path $csvoutputfile -NoTypeInformation
}


$DCS = get-ADDomainController
foreach($dc in $DCS){
$Comps =  Get-ADComputer -Filter * -Server $dc.name -Properties * |select -ExpandProperty  name
foreach ($computer in $Comps) {
$userlist = Get-ComputerUser -ComputerName $computer
}
get-staleAdUsers
get-stalecomputers

Open in new window


what I intend to do is having the computer and checking the users directory which is updated everytime the user logs on and export a list of old users.  The previous scripts depended upon the 'user' actually being logged into the machine under test or the machine being online at the time of the test
1
 
LVL 4
ID: 41869522
Yes, that is true, My script output is based on if logged in at the time,.
0
 

Author Comment

by:tobe1424
ID: 41870062
Thanks for the feedback! I will give this a shot a soon as I get to the office
0
 

Author Comment

by:tobe1424
ID: 41879721
Apologies for the late reply folks.

I edit the script as per your instructions. However, when I run the script via dos or powershell, it seems to process but nothing is generated. I simply end up at another dos/power shell prompt
0
 

Author Comment

by:tobe1424
ID: 41879725
Am I missing something? Is it possible to generate a csv or at least list the results?

Many thanks
0
 
LVL 4
ID: 41879737
I'm sure I confused you on trying to show to different types of accessing data.  My fault.  This one is to search your domain of anyone logged in.  (At the time you run it).  Otherwise it will fail.  Place this script in ISE.  Run once to load the function.  Then type the function name.  Get-LoggedOnUser.  Example:   PS C:\powershell\Get-LoggedOnUser

It will search and write to the host.  If you want to go to csv, change "Out-Host to Export-Csv C:\yourpath\YourFile.csv. Also change your DC name to your own.  Its in CAPS where to change.  



<#Run this funcion under Windows Powershell ISE.  Depending on your environment.  This goes through  every
  computer, and takes a while.  If you decide to use the paramenter Computername, then remove the remarks of
 the paramenter.  And just type in an array of machines.  That goes faster...E.g. Get-loggedOnUser -computername -server1,server2,computer1
 #>


 # Pipeline version otherwise use parameter and rem this out
 $Comps =  Get-ADComputer -Filter * -Server -YOURDCNAME -Properties * |select -ExpandProperty  name

 function Get-LoggedOnUser {
 #Requires -Version 2.0            
  <# [CmdletBinding()]            
  Param             
    (                       
     [Parameter(Mandatory=$true,
                Position=0,                          
                ValueFromPipeline=$true,            
                ValueFromPipelineByPropertyName=$true)]            
     [String[]$ComputerName
    )#End Param
 #>   

 Begin            
 {            
  Write-Host "`n Checking Users . . . "
  $i = 0            
 }#Begin          
 Process            
 {
     $Comps | Foreach-object {
     $Computer = $_
     try
         {
             $processinfo = @(Get-WmiObject -class win32_process -ComputerName $Computer -EA "Stop")
                 if ($processinfo)
                 {    
                     $processinfo | Foreach-Object {$_.GetOwner().User} | 
                     Where-Object {$_ -ne "NETWORK SERVICE" -and $_ -ne "LOCAL SERVICE" -and $_ -ne "SYSTEM"} |
                     Sort-Object -Unique |
                     ForEach-Object { New-Object psobject -Property @{Computer=$Computer;LoggedOn=$_} } | 
                     Select-Object Computer,LoggedOn
                 }#If
         }
     catch
         {
             "Cannot find any processes running on $computer" | Out-Host
         }
      }#Forech-object(Comptuters)       
             
 }#Process
 End
 {

 }#End

 }#Get-LoggedOnUser 

Open in new window

0
 
LVL 4
ID: 41879740
This one is for an array, that you can just type computer names in, because the parameter is -computername.  Run this script once to load the function,  they type Get-LoggedOnUser and then it will ask for a computer name.  Type as many as you like.  Once finished just push enter and it will find the computers you typed in.   (If they are logged in).   This is by computername so nothing to change.  

function Get-LoggedOnUser {
#Requires -Version 2.0            
[CmdletBinding()]            
 Param             
   (                       
    [Parameter(Mandatory=$true,
               Position=0,                          
               ValueFromPipeline=$true,            
               ValueFromPipelineByPropertyName=$true)]            
    [String[]]$ComputerName
   )#End Param

Begin            
{            
 Write-Host "`n Checking Users . . . "
 $i = 0            
}#Begin          
Process            
{
    $ComputerName | Foreach-object {
    $Computer = $_
    try
        {
            $processinfo = @(Get-WmiObject -class win32_process -ComputerName $Computer -EA "Stop")
                if ($processinfo)
                {    
                    $processinfo | Foreach-Object {$_.GetOwner().User} | 
                    Where-Object {$_ -ne "NETWORK SERVICE" -and $_ -ne "LOCAL SERVICE" -and $_ -ne "SYSTEM"} |
                    Sort-Object -Unique |
                    ForEach-Object { New-Object psobject -Property @{Computer=$Computer;LoggedOn=$_} } | 
                    Select-Object Computer,LoggedOn
                }#If
        }
    catch
        {
            "Cannot find any processes running on $computer" | Out-Host
        }
     }#Forech-object(Computers)       
            
}#Process
End
{

}#End

}#Get-LoggedOnUser

Open in new window

0
 
LVL 4
ID: 41879745
On the 1st script to search whole domain, what I mean by fail is that is will just report back "Cannot find any processes running on PCNAME".  It will continue to search until it has searched all ADcomputers.
0
 

Author Comment

by:tobe1424
ID: 41879747
Thanks again!

Searching the whole domain seems more like what I need to do. But I will try both of them.

Cheers
0
 
LVL 4
ID: 41879762
Lastly I wrote this for a person that needed who was last logged in for a certain amount of days.  This is from the LastLogon attribute in the ADUser Object.  I deleted the filter of a date -10 days last logged in.  This is in HTML format.  So it will email it to you.  Change the information under "This information you need about your SMTP server" that I have remarked out.  I capitalized what to change.  But notice I had to filter it a different way, otherwise it gives every user.  (Even disabled, ExchangeHealth, etc)  We have our EmployeeID entered for all active users.  Take the Where-Object {$_.EmployeeID -ne $null} if you don't have their ID entered.  Or change to -eq, doesn't matter.  This will search Active Directory of the last successful logon in AD.  Change the selection of what you need as well.  He needed manager, EmployeeID, office, etc.  If you want in a CSV, read instruction in the remarks at the bottom of the script.  



# Get date values
 $Date = Get-Date
 [String]$Year = $Date.Year
 [String]$Month = $Date.Month
 [String]$Day = $Date.Day
 
 # Pad the date parts with leading 0's
 If ( $Month.ToString().Length -lt 2 ) { $Month = '0' + $Month }
 If ( $Day.ToString().Length -lt 2 ) { $Day = '0' + $Day }



# This is information you need about your SMTP server

$SMTPServer = "MAIL.DOMAIN.COM"
$messageSubject = "All Domain Users"
$smtpfrom = "ANYNAME.DOMAIN.COM"
$smtpto = "VALIDEMAILADDRESS@DOMAIN.COM"
$message = New-Object System.Net.Mail.MailMessage $smtpfrom, $smtpto
$message.Subject = $messageSubject
$message.IsBodyHTML = $true

<# 
    Convert to HTML  You can go to this site to find more color names.
    I use what I can see well. http://www.w3schools.com/colors/colors_names.asp
#>

$a = "<style>"
$a = $a + "BODY{background-color:peachpuff;}"
$a = $a + "TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}"
$a = $a + "TH{border-width: 2px;padding: 1px;border-style: solid;border-color: black;background-color:lightgray: text-align;right}"
$a = $a + "TD{border-width: 2px;padding: 1px;border-style: solid;border-color: black;background-color:whitesmoke}"
$a = $a + "</style>"

<# 
    Here is the amount of days.  I picked 10.  You can change the -gt to whatever you want.
    Keep in mind I get a lot of dates of the year 1600.  I think thats because they never logged in.
    Also, because of Exchange I get all the Exchange HealthMailboxes, etc.  But my Domain is 
    in theprocess of cleaning up bad accounts.
    You can limit it by OU and use -searchbase for a specific OU
    Or what I did, and only
#> 

$AllUsers = get-aduser -filter *  -properties DisplayName,lastlogon,Manager,EmployeeID,office | Where-Object {$_.EmployeeID -ne $null} `
             |sort-object lastlogon `
             |select name,DisplayName,@{Exp={([datetime]::FromFileTime($_.lastlogon))};label="Last logon time"} `
             ,@{n="Manager Name";e={(Get-ADUser -Identity $_.Manager -properties DisplayName).DisplayName}},employeeID,office


            
            
<#  
    If you need to export to CSV by the date of the file place what I have below to the $allUsers variable
    Then put -attacments at the end of H1> and the path you exported it too
  
    |export-csv "C:\powershell\Scripts\lastlogon-audit_$Year$Month$Day.csv" -NoTypeInformation -Delimiter ";"
#>


$message.Body = $AllUsers | ConvertTo-HTML -head $a -body "<H1>Users Logged In Times From AD</H1>"

$smtp = New-Object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($message)

Open in new window

1
 

Author Closing Comment

by:tobe1424
ID: 41899537
Thanks guys
0

Featured Post

Microsoft Certification Exam 74-409

Veeam® is happy to provide the Microsoft community with a study guide prepared by MVP and MCT, Orin Thomas. This guide will take you through each of the exam objectives, helping you to prepare for and pass the examination.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article will help you understand what HashTables are and how to use them in PowerShell.
Restoring deleted objects in Active Directory has been a standard feature in Active Directory for many years, yet some admins may not know what is available.
This tutorial will walk an individual through the process of installing of Data Protection Manager on a server running Windows Server 2012 R2, including the prerequisites. Microsoft .Net 3.5 is required. To install this feature, go to Server Manager…
This Micro Tutorial will teach you how to the overview of Microsoft Security Essentials. This is a free anti-virus software that guards your PC against viruses, spyware, worms, and other malicious software. This will be demonstrated using Windows…

790 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