How do I get a list of computers in a particular OU along with the Description and Last logged on user in a .csv?
$userName = (Get-CimInstance -ClassName Win32_ComputerSystem -ComputerName $machine -ErrorAction:SilentlyContinue).UserName
$DisComp = Get-ADComputer -LDAPFilter "(Name=LN-*)" -SearchBase "OU=Computers - Disabled,DC=XXXXX,DC=com" | Select-Object Name
$results = foreach ($Machine in $DisComp) {
$Description = Get-AdComputer -Identity $Machine -Properties * | Select-Object Description
$UserName
$Machine
$Description
}
$results | Export-Csv -Path C:\XXXXX
Define the OU and CSV file paths
$ouPath = "OU=Workstations,DC=contoso,DC=com"
$csvPath = "C:\temp\computer-list.csv"
Use the Get-ADComputer cmdlet to get a list of computers in the OU
$computers = Get-ADComputer -SearchBase $ouPath -Filter * -Properties lastlogondate,description
Loop through each computer and get the description and last logged on user
foreach ($computer in $computers) {
$description = $computer.Description
$lastLoggedOnUser = $computer.LastLogonUser
$data = [PSCustomObject]#{
"Computer Name" = $computer.Name
"Description" = $description
"Last Logged On User" = $lastLoggedOnUser
}
Add the computer data to the CSV file
$data | Export-Csv -Path $csvPath -Append -NoTypeInformation
}
AFAIK there is no AD computer property called LastLogonUser or any other property that holds this information.
To get the user that last logged on, you need to query the windows Eventlog on that computer and search for events with ID 4672
As aside, don't use -Properties * if all you want on top of the default properties returned is the Description property.
Try:
$searchBase = "OU=Computers - Disabled,DC=XXXXX,DC=com"
$Computers = Get-ADComputer -LDAPFilter "(Name=LN-*)" -SearchBase $searchBase -Properties Description
$results = foreach ($machine in $Computers) {
# to get the username who last logged on, you need to query the Security log
$events = Get-WinEvent -ComputerName $machine.Name -FilterHashtable #{Logname='Security';ID=4672} -MaxEvents 50 -ErrorAction SilentlyContinue
$lastLogon = if ($events) {
(($events | Where-Object {$_.Properties[1].Value -notmatch 'SYSTEM|NETWORK SERVICE|LOCAL SERVICE'})[0]).Properties[1].Value
}
else {
"Unknown"
}
# output an object
[PsCustomObject]#{
ComputerName = $machine.Name
Description = $machine.Description
LastLoggedOnUser = $lastLogon
CurrentUser = (Get-CimInstance -ClassName Win32_ComputerSystem -ComputerName $machine.Name -ErrorAction SilentlyContinue).UserName
}
}
$results | Export-Csv -Path 'C:\Somewhere\Computers.csv' -NoTypeInformation
P.S. You of course need admin permissions to query the eventlog, so perhaps (if you are not a domain admin) you need to use the -Credential parameter on the Get-WinEvent line aswell.
Related
I am trying to query multiple computers from the Domain using Get-ADComputer. I would like to append the pc name I queryed to the array with the word "error" or a nonsensical date or even a blank value in that spot.
Import-Module ActiveDirectory
$PCNames = "laptop-namea", "laptop-nameb", "laptop-badname"
$Output = #()
$Output = foreach ($PC in $PCNames) {
try {
Get-ADComputer -Identity $PC -Properties * |
Select-Object Name, LastLogonDate
} catch {
$Output += ($PC)
}
}
Current output:
Name LastLogonDate
---- -------------
LAPTOP-NAMEA 1/27/2019 10:37:13 AM
LAPTOP-NAMEB 1/22/2019 8:23:02 AM
Wanted/expected output:
Name LastLogonDate
---- -------------
LAPTOP-NAMEA 1/27/2019 10:37:13 AM
LAPTOP-NAMEB 1/22/2019 8:23:02 AM
LAPTOP-BADNAME
Use -Filter instead of -Identity to avoid throwing errors in case of invalid names.
$Output = foreach ($PC in $PCNames) {
New-Object -Type PSObject -Property #{
'Name' = $PC
'LastLogon' = Get-ADComputer -Filter "Name -eq '$PC'" -Property LastLogonDate |
Select-Object -Expand LastLogonDate
}
}
Beware that querying AD for each individual computer is time-consuming. If the number of queries grows beyond a certain point it's better to query all computers, put them into an appropriate data structure (usually a hashtable), and then look up the desired information in that data structure.
$computers = #{}
Get-ADComputer -Filter '*' -Property LastLogonDate | ForEach-Object {
$computers[$_.Name] = $_.LastLogonDate
}
$Output = foreach ($PC in $PCNames) {
New-Object -Type PSObject -Property #{
'Name' = $PC
'LastLogon' = $computers[$PC].LastLogonDate
}
}
Try - Catch - Finally blocks handle terminating errors. Apply the common parameter -ErrorAction -Stop as follows:
Import-Module ActiveDirectory
$PCNames = "laptop-namea","laptop-nameb","laptop-badname"
$Output = ForEach ($PC in $PCNames)
{
try{
Get-ADComputer -Identity $PC -Properties * -ErrorAction Stop |
Select-Object Name, LastLogonDate
}
catch{
[PSCustomObject]#{Name=$PC;LastLogonDate=$null}
}
}
Would anybody have any suggestions? I need to generate a list of users and the computers they're logging into, from Active Directory. I'm hoping to get something like this:
Username Hostname
user.lastname ComputerA1
So far, I've gotten:
Enter-PSSession Import-Module ActiveDirectory Get-ADComputer
-Filter * -Properties Name Get-ADuser -filter * -Properties * | export-csv '\\\AD_UserLists.csv'
This works, kinda. I can generate a list of computers from AD and I can generate a list of ADUsers (albeit with ALL the users information). Unfortunately, I can't generate the data into a single CSV.
Suggestions/Advice????
Thanx,
David
Here is a way to get what you want. You will have to run this against AD-Computer objects when the machines are online, and catch the names of the computers you could not reach. Something like this...
#grab the DN of the OU where your computer objects are located...
$OU = ("OU=Computers,DC=domain,DC=com")
#put your filtered results in $computers (I filtered for Enabled objects)...
$computers = #()
ForEach ($O in $OU) {
$computers += Get-ADComputer -SearchBase $O -filter 'Enabled -eq "True"' -Properties CN,distinguishedname,lastLogonTimeStamp | Select-Object CN,distinguishedname,lastLogonTimeStamp
}
#instantiate some arrays to catch your results
#collected user info
$userInfo = #()
#computers you cannot ping
$offline = #()
#computers you can ping but cannot establish WinRM connection
$winRmIssue = #()
#iterate over $computers list to get user info on each...
ForEach ($computer in $computers) {
#filter out System account SIDs
$WQLFilter = "NOT SID = 'S-1-5-18' AND NOT SID = 'S-1-5-19' AND NOT SID = 'S-1-5-20'"
$WQLFilter = $WQLFilter + " AND NOT SID = `'$FilterSID`'"
#set number of login events to grab
$newest = 20
#attempt to ping computer once by name. return 'true' is success...
if (Test-Connection -ComputerName $computer.CN -Count 1 -ErrorAction Stop -Quiet) {
#if ping is true, try to get some info...
Try {
#currently logged in user...
$user = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $computer.CN | select -ExpandProperty username
#the most commonly logged in user, based on the past 20 log-ins...
$UserProperty = #{n="User";e={((New-Object System.Security.Principal.SecurityIdentifier $_.ReplacementStrings[1]).Translate([System.Security.Principal.NTAccount])).ToString()}}
$logs = Get-EventLog System -Source Microsoft-Windows-Winlogon -ComputerName $computer.CN -newest $newest | select $UserProperty
$freqent = $logs | Group User | Sort-Object Count | Select -First 1 | Select-Object -ExpandProperty Name
}
#catch any connection issues...
Catch {
$cantInvoke = [pscustomobject][ordered]#{
'Computer' = $computer.CN
'Message' = "Could not Invoke-Command. Probably a WinRM issue."
}
$winRMIssue += $cantInvoke
}
#custom psobject of gathered user info...
$userInfoObj = New-Object psobject -Property ([ordered]#{
'Computer' = $computer.CN
'LoggedInUser' = $user
'mostCommonUser' = $frequent
})
$userInfo += $userInfoObj
}
#if you could not ping the computer, gather that info here in a custom object...
else {
$noPing = [pscustomobject][ordered]#{
'Computer' = $computer.CN
'DN' = $computer.distinguishedname
'lastLogonDate' = [datetime]::FromFileTime($computer.lastLogonTimeStamp).toShortDateString()
}
$offline += $noPing
}
#then kick out the results to csv
$userInfo | Sort-Object Computer | export-csv -Path c:\path\file.csv -NoTypeInformation
$offline | Sort-Object lastLogonDate | export-csv -Path c:\path.file2csv -NoTypeInformation
$winRmIssue | Sort-Object Computer | export-csv -Path c:\path\file3.csv -NoTypeInformation
You could use the wmi function
Get-WmiObject -Class Win32_ComputerSystem -ComputerName "computersname" | Select-Object Name,Username
I need to generate a list of users and the computers they're logging into, from Active Directory.
This information is not stored in Active Directory. You may be able to retrieve this information with Active Directory auditing. Otherwise, you'll need to poll each individual workstation.
I am putting together a script that will check all of my web servers certificates so that I can monitor when they are set to expire. When the script goes to execute the Invoke-Command I am getting this error:
Here is my code:
Import-Module WebAdministration
$results = #()
$ou = 'OU=test,OU=test,OU=Servers,DC=contoso,DC=com'
$subtree = Get-ADOrganizationalUnit -SearchBase $ou -SearchScope Subtree -filter * | Select-Object -ExpandProperty DistinguishedName
ForEach($dn in $subtree){
$servers = Get-ADComputer -Filter * -SearchBase $dn | select Name
$results += $servers
}#ForEach($dn in $subtree)
$scriptBlock = [scriptblock]::Create({
Import-Module WebAdministration; Get-ChildItem -Path IIS:SSLBindings | ForEach-Object -Process `
{
If($_.Sites){
$certificate = Get-ChildItem -Path CERT:LocalMachine/My |
Where-Object -Property Thumbprint -EQ -Value $_.Thumbprint
[PSCustomObject]#{
Sites = $_.Sites.Value
CertificateDNSNameList = $certificate.DnsNameList
CertificateNotAfter = $certificate.NotAfter
}#[PSCustomObject]
}#If($_.Sites)
}#Import-Module
})#ScriptBlock
ForEach($server in $results){
Invoke-Command -ComputerName $server -ScriptBlock $scriptBlock | Select Sites,CertificateDNSNameList,CertificateNotAfter,PSComputerName #ScriptBlock
}#ForEach($server in $results)
Now, If I take the following line out of the loop and replace $server with an actual server name, I get the results I am looking for:
Invoke-Command -ComputerName ServerName -ScriptBlock $scriptBlock | Select Sites,CertificateDNSNameList,CertificateNotAfter,PSComputerName
Any ideas what I am doing wrong?
Your Results-Array is not an array of string, it is an array of object. Each object have the attribute name. Change your last ForEach-Loop into this and your script will work:
ForEach($server in $results){
Invoke-Command -ComputerName $server.name -ScriptBlock $scriptBlock | Select Sites,CertificateDNSNameList,CertificateNotAfter,PSComputerName #ScriptBlock
}#ForEach($server in $results)
It looks to me like "select Name" is returning an object rather than a string. Try changing the line that fetches the servers to this:
$servers = Get-ADComputer -Filter * -SearchBase $dn | select -ExpandProperty Name
I have a text file that contains called MY-OU.txt
OU=offic-Computers,OU=comp-computers,DC=my,DC=company,DC=com
I can get a list of all the computers in that OU:
ForEach ($OU in Get-Content "C:\temp\MY-OU.txt")
{
Get-ADComputer -SearchBase $OU -Filter '*' | Select -Exp Name | Out-File -filepath "C:\temp\MY-OU-computers.txt"
}
And I can get the current logged on users from the output of the code above:
$content = Get-Content C:\temp\MY-OU-computers.txt
$output = foreach ($PC in $content) {Get-WmiObject –ComputerName $PC –Class Win32_ComputerSystem | Select-Object UserName}
$output | Out-File -filepath "C:\temp\CAD-OU-computers-users.txt"
How to combine both parts and get the current logged on users in a specific OU?
Instead of outputting the results from Get-ADComputer to a file, you can assign them to a variable ($Computers), then use that as the input for your next foreach loop:
foreach ($OU in Get-Content "C:\temp\MY-OU.txt"){$Computers += Get-ADComputer -SearchBase $OU -Filter '*' | Select -ExpandProperty Name}
$output = foreach ($PC in $Computers) {Get-WmiObject –ComputerName $PC –Class Win32_ComputerSystem | Select-Object UserName}
$output | Out-File -filepath "C:\temp\CAD-OU-computers-users.txt"
I am using Powershell on Windows Server 2008 R2 and I need any regular user get owner of the process with name and surname from AD.
Here's script wrote with help of Mathias R. Jessen
Import-Module ActiveDirectory
$ProcessWithOwners = Get-WmiObject Win32_Process -Filter 'Name LIKE "vr[0-9]%"' |Select *,#{Name="Owner";Expression={$_.GetOwner().User}}
foreach($Process in $ProcessWithOwners)
{
$Username = $Process.Owner
$ADUser = Get-ADUser -Filter "SamAccountName -eq '$Username'" -properties *
$a = new-object -comobject wscript.shell
$b = $a.popup(“Process name $($Process.Name) is run by user $($ADUser.DisplayName)“,0,“Warning”,1)
}
But regular user can't get access to process's owner.
Is there any way to solve it?
* Final Update *
For running this script as regular user I made some changes:
Import-Module ActiveDirectory
Add-Type -Name win -MemberDefinition '[DllImport("user32.dll")] public static extern bool ShowWindow(int handle, int state);' -Namespace native
[native.win]::ShowWindow(([System.Diagnostics.Process]::GetCurrentProcess() | Get-Process).MainWindowHandle,0)
$ConsProcess = Get-WmiObject Win32_Process -Filter 'Name LIKE "vr[0-9]%"' | Select *,#{Name="Owner";Expression={$_.GetOwner().User}}
if ($ConsProcess -ne $null) {
foreach($Process in $ConsProcess)
{
$QueryProcess = query process $Process.ProcessName
$Id = ($QueryProcess[1] -replace ' +',' ').Trim().Split(' ')[2]
$QueryUser = query session $Id
$User = ($QueryUser[1] -replace ' +',' ').Trim().Split(' ')[1]
$ADUser = (Get-ADUser -Identity $User -Properties *).DisplayName
$a = new-object -comobject wscript.shell
$b = $a.popup(“Консультант запущен с именем $($Process.Name) пользователем $($ADUser)“,0,“Внимание”,1)
}
}
else
{
& 'D:\consultantplus\cons.exe' /adm /inet
}
There's no reason to call both gwmi Win32_Process and Get-Process when all you need is just the Owner.
You can do with just:
$CmdProcessWithOwners = Get-WmiObject Win32_Process -Filter 'Name LIKE "cmd%"' |Select *,#{Name="Owner";Expression={$_.GetOwner()}}
Since you could potentially find more than one process matching your criteria, you might want to iterate over the process(es) in a loop:
foreach($Process in $CmdProcessWithOwners)
{
# Grab the user name we added earlier
$Username = $Process.Owner.User
$Domain = $Process.Owner.Domain
$DisplayName = switch($Domain)
{
'DOMAINNAME' {
# Process owned by AD User, grab name from AD
Get-ADUser -Filter "SamAccountName -eq '$Username'" -Properties DisplayName |Select-Object -ExpandProperty DisplayName
}
$env:ComputerName {
# Process owned by local user, look up local account full name
[adsi]"WinNT://$($env:ComputerName)/$Username,user" |Select-Object -ExpandProperty FullName
}
default {
# Likely system or virtual account, default to DOMAIN\Username
"$Domain\$Username"
}
}
# Print it
Write-Host "Process $($Process.Name) with ID $($Process.Id) belongs to $DisplayName"
}