Powershell Script outputs are merged instead of separated individually - powershell

I made this script to ping a computer name and if it's pingable, to return the username of the logged on user and if the user of the script so desires, a list of all the installed programs and a list of the installed printers the user is using.
When I run the script it returns the username and requires the users' input if they want to see the installed programs, the user answers Y or N. It asks the user afterwards if they want to the list of printers.
The script then gives an output of both the list of programs & printers as one answer.
My problem is that I would like the script to ask if the user wants the programs list, then to output the programs list, then prompt if the user wants the printers list, then output the printer list.
I have no idea how to go about this and have searched and experimented and have found no solution.
Any help or advice would be greatly appreciated :)
Apologies for the long post
# This script shows who is currently logged on to a machine
$PCNAME = Read-Host "Please enter computer name"
Write-Host "Pinging computer name..."
# If the computer responds to ping then the user name will be displayed
If (Test-Connection -ComputerName $PCNAME -Quiet)
{
$User = Get-WmiObject Win32_ComputerSystem -ComputerName $PCNAME | Select-Object -ExpandProperty UserName
$Time = Get-Date -DisplayHint Time
Write-Host ""
$CurUser = Write-Host "The current user logged in to $PCNAME is $User at $Time" -ForegroundColor Green
Write-Host ""
}
#PROGRAMS
$Programs = Read-Host "Would you like to see what programs $User has installed? Enter Y or N"
If ($Programs -eq "Y") {
Write-Host ""
Write-Host "Retrieving list of installed programs..."
Write-Host ""
Get-WmiObject -ComputerName $PCNAME -Class Win32_Product | sort-object Name | select Name
}
ElseIf ($Programs -ne "Y" -and $Programs -eq "N") {
Write-Host ""
Write-Host "Will not retrieve list of installed programs."
}
#PRINTERS
$Printers = Read-Host "Would you like to see the pinters that $User is using? Enter Y or N"
If ($Printers -eq "Y") {
Write-Host ""
Write-Host "Getting printers..."
Write-Host ""
# Collect port names and host addresses into hash table
$hostAddresses = #{}
Get-WmiObject Win32_TCPIPPrinterPort -ComputerName $PCNAME | ForEach-Object {
$hostAddresses.Add($_.Name, $_.HostAddress)
}
Get-WmiObject Win32_Printer -ComputerName $PCNAME | ForEach-Object {
New-Object PSObject -Property #{
"Name" = $_.Name
"DriverName" = $_.DriverName
"HostAddress" = $hostAddresses[$_.PortName]
}
}
}
ElseIf ($Printers -ne "Y" -and $Printers -eq "N") {
Write-Host ""
Write-Host "Could not get printers"
Write-Host ""
}
Else
{
Write-Host ""
Write-Host "Could not ping $PCNAME at $Time" -ForegroundColor DarkCyan
}
Write-Host ""
#$EndPrompt = ( Read-Host -Prompt "Press Enter to finish" )

There's better ways of doing this overall, but the simplest way to add this is:
#PROGRAMS
$Programs = Read-Host "Would you like to see what programs $User has installed? Enter Y or N"
If ($Programs -eq "Y") {
Write-Host ""
Write-Host "Retrieving list of installed programs..."
Write-Host ""
$Installed = Get-WmiObject -ComputerName $PCNAME -Class Win32_Product | sort-object Name | select Name
$Installed | Out-Host
}
ElseIf ($Programs -ne "Y" -and $Programs -eq "N") {
Write-Host ""
Write-Host "Will not retrieve list of installed programs."
}

Related

Get-WmiObject delay outcome when check C disk storage

Get-WmiObject delay outcome when check C disk storage.
When I input my computer name and type '1' to check the C disk storage, the first time won't return the outcome, and I need to type '1' again it will return both the first and second outcome.
However, if I test the function or the line Get-WMIObject separately, it works perfect.
Anyone have any idea what's going on here?
$ComputerNumber = (read-host "Provide computer number").trim()
function Show-Storage{
Get-WmiObject -Class win32_logicaldisk -Filter "DeviceID='C:'" -ComputerName $ComputerNumber|select PSComputerName,DeviceID,#{n='size(GB)';e={$_.size/1gb -as [int]}},#{n='Free(GB)';e={$_.freespace/1gb -as [int]}}
}
function Show-Menu
{
param (
[string]$Title = 'Computer Info'
)
Write-Host "================ $Title ================"
Write-Host ""
Write-Host "1: Press '1' to get Current Computer Usage"
Write-Host "2: Press '2' to delete Local User Profile"
Write-Host "Q: Press 'Q' to quit."
}
$a=1
While ($a -eq 1)
{
write-host ""
Show-Menu
write-host ""
if ($ComputerNumber -ne $null){
write-host "Selected Computer '$ComputerNumber'"
}
else{write-host "No Computer selected"}
$selection = Read-Host "Please make a selection"
switch($selection)
{
'1'{
write-host ""
Show-Storage
write-host ""
}
'2'{
delete-profile
}
'Q'{$a=0}
}
}
I just found another interesting thing, if I leave the line $result, the result will come before the "WMI end", but if I remove that line, the result will still comes after "WMI end"
Write-Host "WMI Start"
$result = Get-WmiObject -query "SELECT * FROM Win32_logicalDisk WHERE DeviceID = 'C:'" -ComputerName $PC
$result
$result |select PSComputerName,DeviceID,#{n='size(GB)';e={$_.size/1gb -as [int]}},#{n='Free(GB)';e={$_.freespace/1gb -as [int]}}
Write-Host "WMI End"
enter image description here
Found solution finally, use | Out-Host can fix this issue.
Reference:
Get-WMIObject returning multiple responses in a script, only one when run alone

Subexpression printing out same strings? Powershell

I have this code which deletes User Profiles off a remote machine. The removal of profiles work just fine but, the Aesthetic of doing so doesn't. What do i mean?
I'm passing the user display names to an index and making a selection out of it, and that works fine in regards to assigning the proper names to the appropriate Index Number its associated to in C:\users.
The next line of code is it grabbing the selections i made, and running through them displaying the same name i did for the index, and then it goes off to delete the CIM instance.
So my question is, why is it not passing the subexpression $userinfo1 that is already made and not putting it into the next block of code, for example, the following works as in grabbing the proper Display Name and assigning it to the proper Number:
$menu = (get-childitem "\\$cn\c$\users" | sort LastWriteTime -Descending).Name
$userinfo1 = foreach ($user in $menu) {
Start-Sleep -Milliseconds 2
$userinfo = (net user $user /domain | Select-String "Full Name" -ErrorAction SilentlyContinue) -replace "Full Name ", "" 2>&1 | Out-String -Stream
if ($userinfo.Length -lt 4) {
"$user - NO DISPLAY NAME in ADUC" # output
}
else {
if ($LASTEXITCODE -eq 2) {
"$user - account not in ADUC" # output
}
else {
if ($LASTEXITCODE -eq 0){
$userinfo # output
}
}
}
}
Write-Warning "Ensure user profiles are no longer active and/or, have profiles be backed-up!"
Write-Host "RESULTS:" -BackgroundColor Black -ForegroundColor White
for ($i=0; $i -lt $userinfo1.Count; $i++) {
Write-Host "$($i): $($userinfo1[$i])"
} #END LIST OF POSSIBLE NAMES
Write-Host ""
Write-Host "For multiple users, seperate using a SPACE(1 2 3)"
$selection = Read-Host "ENTER THE NUMBER of the user(s) or Q to quit"
$selection = $selection -split " "
but, the next block doesn't associate the display name (that was captured in $userinfo1) with the number i select and it just continues to display the first display name with the rest of the profiles its reiterating through:
foreach($Profile in $menu[$selection]){
Write-Host "Deleting user: $(,$userinfo1[$selection]) `
ID:$Profile "}
Hopefully this makes sense, and if anyone can point me in the right direction id greatly appreciate it!
Heres the rest of the script, please feel free to use it as it does work for deleting the actual profile off the system and not just the files.
#Deletes a profile properly off remote machine. WARNING: DOES NOT BACK UP DATA! Use at your own peril. Delprofile
$cn = Read-Host -Prompt "Enter Computer Name"
$ping = Test-Connection -ComputerName $cn -Count 1 -Quiet
If($ping -eq $false){ Write-Host "Computer seems to be offline, please check name spelling." -ForegroundColor DarkYellow; Write-Host ""; &PFL-Delete } else {
$menu = (get-childitem "\\$cn\c$\users" | sort LastWriteTime -Descending).Name
$userinfo1 = foreach ($user in $menu) {
Start-Sleep -Milliseconds 2
$userinfo = (net user $user /domain | Select-String "Full Name" -ErrorAction SilentlyContinue) -replace "Full Name ", "" 2>&1 | Out-String -Stream
if ($userinfo.Length -lt 4) {
"$user - NO DISPLAY NAME in ADUC" # output
}
else {
if ($LASTEXITCODE -eq 2) {
"$user - account not in ADUC" # output
}
else {
if ($LASTEXITCODE -eq 0){
$userinfo # output
}
}
}
}
Write-Warning "Ensure user profiles are no longer active and/or, have profiles be backed-up!"
Write-Host "RESULTS:" -BackgroundColor Black -ForegroundColor White
for ($i=0; $i -lt $userinfo1.Count; $i++) {
Write-Host "$($i): $($userinfo1[$i])"
} #END LIST OF POSSIBLE NAMES
Write-Host ""
Write-Host "For multiple users, seperate using a SPACE(1 2 3)"
$selection = Read-Host "ENTER THE NUMBER of the user(s) or Q to quit"
$selection = $selection -split " "
foreach($Profile in $menu[$selection]){
Write-Host "Deleting user: $(,$userinfo1[$selection]) `
ID:$Profile "
$del = Get-CimInstance -ComputerName $cn -Class Win32_UserProfile | Where-Object { $_.LocalPath.split('\')[-1] -eq $Profile }
If($del -eq $null){Write-Warning "No CIM instance found on system, profile has been deleted but files persist. Delete manually!"} else{
Get-CimInstance -ComputerName $cn -Class Win32_UserProfile | Where-Object { $_.LocalPath.split('\')[-1] -eq $Profile } | Remove-CimInstance -WhatIf
Write-Host "user profile has been deleted" -ForegroundColor Red
Write-Host ""}
}
}
#CountPs $cn
12/31/2020 - EDIT:
Here is the finished result:
Function Delete-PFL{
#Deletes a profile properly off remote machine. WARNING: DOES NOT BACK UP DATA! Use at your own peril. Delprofile
$cn = Read-Host -Prompt "Enter Computer Name"
$ping = Test-Connection -ComputerName $cn -Count 1 -Quiet
If($ping -eq $false){ Write-Host "Computer seems to be offline, please check name spelling." -ForegroundColor DarkYellow; Write-Host ""; &Delete-PFL } else {
$menu = (get-childitem "\\$cn\c$\users" | sort LastWriteTime -Descending).Name
$userinfo1 = foreach ($user in $menu) {
Start-Sleep -Milliseconds 2
$userinfo = (net user $user /domain | Select-String "Full Name" -ErrorAction SilentlyContinue) -replace "Full Name ", "" 2>&1 | Out-String -Stream
if ($userinfo.Length -lt 4) {
"$user - NO DISPLAY NAME in ADUC" # output
}
else {
if ($LASTEXITCODE -eq 2) {
"$user - ACCOUNT NOT in ADUC" # output
}
else {
if ($LASTEXITCODE -eq 0){
$userinfo # output
}
}
}
}
Write-Warning "Ensure user profiles are no longer active and/or, have profiles be backed-up!"
Write-Host "RESULTS:" -BackgroundColor Black -ForegroundColor White
for ($i=0; $i -lt $userinfo1.Count; $i++) {
Write-Host "$($i): $($userinfo1[$i])"
} #END LIST OF POSSIBLE NAMES
Write-Host ""
Write-Host "For multiple users, seperate using a SPACE(1 2 3)"
$selection = Read-Host "ENTER THE NUMBER of the user(s) or Q to quit"
$selection = $selection -split " "
foreach($index in $selection) {
$Profile = $menu[$index]
Write-Host "Deleting user: $($userinfo1[$index]) `
ID:$Profile "
$del = Get-CimInstance -ComputerName $cn -Class Win32_UserProfile | Where-Object { $_.LocalPath.split('\')[-1] -eq $Profile }
If($del -eq $null){Write-Warning "No CIM instance found on system, profile has been deleted but files persist."
Write-Host "Attempting to delete files, please wait. . ."
Remove-Item -Path "\\$cn\c$\users\$Profile" -Force -WhatIf
Write-Host ""
Start-Sleep -Seconds 2
Write-Host "Checking if Files are still there. . ."
$TestPath = Test-Path -Path "\\$cn\c$\users\$Profile"
If($TestPath -eq $false){ Write-Host "Profile Files have been deleted. `
Continuing. . . ." -ForegroundColor Green
}
} else{
Get-CimInstance -ComputerName $cn -Class Win32_UserProfile | Where-Object { $_.LocalPath.split('\')[-1] -eq $Profile } | Remove-CimInstance -WhatIf
Write-Host "user profile has been deleted" -ForegroundColor Red
Write-Host ""
}
}
}
#CountPs $cn
}
Remember to remove the -whatif parameter. Enjoy!
$selection is an array of indices, so in your foreach loop you must refer to the single index at hand, not to $selection as a whole, to get the desired display output.
The conceptually clearest approach is probably to iterate over the indices contained in $selection:
foreach($index in $selection) {
$Profile = $menu[$index]
Write-Host "Deleting user: $($userinfo1[$index]) `
EDIPI:$Profile "
# ...
}

Shutting down multiple PCs remotely

i want to shut down almost all PCs at my workplace (if they run more than 2 days)
I've worked the last and this week on a Script and trying to get rid of Errors on the way.
$days = -0
$date = (get-date).adddays($days)
$lastboot = (Get-WmiObject Win32_OperatingSystem).LastBootUpTime
$Computer = Get-ADComputer -SearchBase 'OU=______,OU=______,DC=______,DC=______' ` -Filter '*' | Select -EXP Name
$lastbootconverted = ([WMI]'').ConvertToDateTime($lastboot)
write-host $date
write-host $lastboot
write-host $lastbootconverted
if($date -gt $lastbootconverted)
{
write-host Need to reboot
(Stop-Computer -$Computer -Force)
}
else
{
write-host no need to reboot
}
When I run it it says
"The RPC-Server isn't available. (Exception HRESULT: 0x800706BA)"
But if I just put a PC Name instead of the "$Computer", it shuts the PC down like I want. What is this RPC-Server Error? I don't have a firewall activated, so I'm clueless...
The OU=_____ and DC=______ are private company names
I've got not AD environment to test your Get-ADComputer query, but this worked for me with just an array of computer so should be fine for you.
function Get-LastBootUpTime {
param (
$ComputerName
)
$OperatingSystem = Get-WmiObject Win32_OperatingSystem -ComputerName $ComputerName
[Management.ManagementDateTimeConverter]::ToDateTime($OperatingSystem.LastBootUpTime)
}
$Days = -2
$ShutdownDate = (Get-Date).adddays($days)
$ComputerList = Get-ADComputer -SearchBase 'OU=______,OU=______,DC=______,DC=______' ` -Filter '*' | Select -EXP Name
$ComputerList | foreach {
$Bootup = Get-LastBootUpTime -ComputerName $_
Write-Host "$_ last booted: $Bootup"
if ($ShutdownDate -gt $Bootup) {
Write-Host "Rebooting Computer: $_" -ForegroundColor Red
Restart-Computer $_ -Force
}
else {
Write-Host "No need to reboot: $_" -ForegroundColor Green
}
}

Powershell 2.0 - Memory Leaking

So here's the scope of what I'm trying to do:
Get remote computer information for Windows computers in multiple sites and write the information found to the .Description property of each computer object in Active Directory. If the script can't connect to the remote machine, log that information into a text file and don't make any changes to the computer object that can't be connected to.
In order to time how long the script is taking to run, I have a second script that measures the execution time.
I have this setup as a scheduled task to run the second script (which calls the first) that is executed via a batch file on a Windows 7 Pro virtual machine.
My problem is I believe the script may be running into memory problems based on the information I see in my log. Any help on possible diagnosing the root cause would be appreciated to the extreme. Without further adieu, here's my code for both scripts as well as a sample of the strange log output.
Main Script (script 1):
set-location \\myscriptcomputer\c$\somefolder\PSScripts
enter code here`function Measure-Latest {
BEGIN { $latestlogon = $null }
PROCESS {
if (($_ -ne $null) -and (($latestlogon -eq $null) -or ($_ -gt $latestlogon))) {
$latestlogon = $_
}
}
END { $latestlogon }
}
Function CreateLog {
#Create a log file
$global:path = "C:\Somefolder\PSScripts\WriteComputerDescriptions"
$global:LogTime = Get-Date -Format "MM-dd-yyyy_hh-mm-ss"
$global:LogName = 'CompDescriptions'
$global:LogFile = 'C:\Somefolder\PSScripts\WriteComputerDescriptions\'+$LogName+$LogTime+'.txt'
Write-Host "Creating log file" -foregroundcolor yellow
if([IO.Directory]::Exists($global:path))
{
#Do Nothing
}
else
{
New-Item -ItemType directory -Path C:\Somefolder\PSScripts\WriteComputerDescriptions
}
cd C:\Somefolder\PSScripts\WriteComputerDescriptions
echo "WriteComputerDescriptions Script Log" >> $global:logfile
}
Function WriteDescription {
Write-Host "Gathering Computer information..." -foregroundcolor yellow
$UserWorkstations = get-qadcomputer -sizelimit 0 -includeallproperties -searchroot my.domain.com/MyUserWorkstations
$IPv4Regex = "^(\d{1,3}\.){3}\d{1,3}$"
foreach ($computerobject in $UserWorkstations) {
$computerIP = $NULL
$computerIP2 = $NULL
$computerIP3 = $NULL
$computerserial = $NULL
$computerserial2 = $NULL
$findlastuser = $NULL
$findlastuser2 = $NULL
$lastlogontime = $NULL
$findlastuserFname = $NULL
$findlastuserFname2 = $NULL
$findlastuserLname = $NULL
$findlastuserLname2 = $NULL
$fullname = $NULL
$userlogon = $NULL
$computerName = $computerobject.name
$oldcomputerdescription = $computerobject.description
Write-Host " "
Write-Host "Testing connection to $computerName ..."
$testConnection = test-connection -computername $computerName -count 2 -quiet
Write-Host "Connection is $testconnection"
if ($testConnection -eq $True) {
$Connect = $testConnection
#get IP address(es)
try {
$computerIP = get-wmiobject -class win32_networkadapterconfiguration -filter IPEnabled=TRUE -computername $computerName
$computerIP2 = $computerIP.ipaddress[0]
$computerIP3 = $computerIP.ipaddress[1]
Write-Host = $computerIP2
if ($computerIP3 -match $IPv4Regex){
Write-Host = $computerIP3
}
}
catch [system.exception]{
$connect = $False
Write-Host "Could not connect to $computerName. No IP collected."
}
#get computer serial
try {
$computerSerial = gwmi win32_bios -computername $computerName | select serialnumber
$computerserial2 = $computerSerial.serialnumber.tostring()
}
catch [system.exception]{
Write-Host "Could not get serial for $computerName."
$computerSerial = "Unavailable"
$computerSerial2 = "Unavailable"
}
#get username of currently logged in user
try {
$findlastUser = gwmi win32_computersystem -computer $computerName | select username
$findlastuser2 = ($findlastUser.username).replace("mydomain\","")
}
catch [system.exception]{
Write-Host "Could not get username of logged in user on $computerName"
$findlastUser = "Unavailable"
$findlastUser2 = "Unavailable"
}
#get last logon time of user
try {
if($findlastuser2 -ne $NULL -and $findlastuser2 -notlike "Unavailable") {
#ignore domain controllers in a datacenter due to connectivity stuff
$lastlogontime = get-qadcomputer -computerrole domaincontroller | where { $_.name -notmatch "-COLO"} | foreach {(get-qaduser -service $_.name -samaccountname $findlastuser2).LastLogon } | Measure-Latest
}
}
catch {
if ($lastlogontime -eq $NULL -and $findlastuser2 -eq $NULL){
Write-Host "Could not find a last logon time"
Write-Host "No username available to query"
$lastlogontime = "Unavailable"
}
if ($lastlogontime -eq $NULL -and $findlastuser2 -ne $NULL){
Write-Host "Could not find a last logon time for user $findlastuser"
$lastlogontime = "Unavailable"
}
}
#search AD for the user identified, select first name
try {
$findlastuserFname = get-qaduser $findlastuser2 | select firstname
$findlastuserFname2 = $findlastuserFname.firstname.tostring()
}
catch [system.exception]{
if ($findlastuserFname2 -eq $NULL) {
Write-Host "No first name for user found"
}
}
#search AD for the user identified, select last name
try {
$findlastuserLname = get-qaduser $findlastuser2 | select lastname
$findlastuserLname2 = $findlastuserLname.lastname
}
catch [system.exception] {
if ($findlastuserLname2 -eq $NULL) {
Write-Host "No last name for user found"
}
}
#join the first and last names together if both properties are available
if ($findlastuserFname2 -ne $NULL -and $findlastuserLname2 -ne $NULL){
$fullname = "$findlastuserFname2" + " $findlastuserLname2"
}
elseif ($findlastuserFname2 -eq $NULL -and $findlastuserLname -ne $NULL){
$fullname = $findlastuserLname2
}
elseif ($findlastuserFname2 -ne $NULL -and $findlastuserLname -eq $NULL){
$fullname = $findlastuserFname2
}
else {
$fullname = "Unavailable"
}
#Set the description data format
#With only 1 IPv4 Address
if ($computerIP3 -notmatch $IPv4Regex -or $computerIP3 -eq $NULL){
$newcomputerdescription = "$fullname | $computerIP2 | $computerSerial2 | $lastlogontime"
}
#With 2 IPv4 Addresses
if ($computerIP3 -match $IPv4Regex) {
$newcomputerdescription = "$fullname | $computerIP2, $computerIP3 | $computerSerial2 | $lastlogontime"
}
#If the description data is the same, leave it as it is
if ($newcomputerdescription -eq $oldcomputerdescription){
Write-Host " "
Write-Host "Information for $computerName has not" -foregroundcolor yellow
Write-Host "changed. No edits were made on this object." -foregroundcolor yellow
}
if ($newcomputerdescription -ne $oldcomputerdescription -and $Connect -eq $TRUE) {
set-qadcomputer -identity $computerName -Description $newcomputerdescription
Write-Host " "
Write-Host "Computer description updated for object $computerName" -foregroundcolor yellow
Write-Host "New host information:"
Write-Host "$newcomputerdescription"
}
}
else {
Write-Host "Could not connect to computer $computerName"
Write-Host "No changes made to description for $computerName"
$noconnecterror = "Could not connect to computer $computerName"
$noconnecterror | Out-File $global:logfile -Append -Force
}
}
Write-Host "Processing complete!"
}
CreateLog -erroraction silentlycontinue
WriteDescription -erroraction silentlycontinue
start-sleep -s 3
##END OF SCRIPT
Second Script:
set-location \\myscriptcomputer\c$\somefolder\PSScripts
Add-PSSnapin Quest.ActiveRoles.ADManagement -erroraction SilentlyContinue
$timeoutput = Measure-Command {\\myscriptcomputer\c$\Somefolder\PSScripts\WriteComputerDescriptions.ps1}
cd \\myscriptcomputer\c$\Somefolder\PSScripts\WriteComputerDescriptions
$scriptlog = get-childitem | sort creationtime | select -last 1
$logname = $scriptlog.name
Add-Content c:\somefolder\PSScripts\WriteComputerDescriptions\$logname "`nExecution Time: $timeoutput"
Write-Host "Script complete!"
Start-sleep -s 3
exit
In the results in my environments Active Directory, this works effectively for several hundred objects, but here's a sample of what I see in my log file:
Could not connect to computer computer391
Could not connect to computer computer392
Could not connect to computer computer393
Could not connect to computer computer394
䔊數畣楴湯吠浩㩥ㄠ㨱㘰㈺⸱㜵㤵㐰ഷ
The very last line with the garbled text is what made me think there's a memory-related issue perhaps. If I run my scripts against a container/OU with a much smaller amount of computers, the last line in my log is a time, which is what I would normally expect.
If any seasoned Powershell pros could offer some advice here, I'd really appreciate the help.
Thanks!
I don't know why my comments are not getting added. Anyways, let me just post it here.
In order to track the free memory, you just look at its the performance counter.
Here is the powershell command:
Get-Counter -Counter "\Memory\Available MBytes"

User Logon/Logoff Information using Powershell

I want to be able to check a remote computer's user logon/logoff sessions and times and I have the following code that I got from stackoverflow, but I cannot figure out how to tell the script to check a remote computer:
$UserProperty = #{n="User";e={(New-Object System.Security.Principal.SecurityIdentifier
$_.ReplacementStrings[1]).Translate([System.Security.Principal.NTAccount])}}
$TypeProperty = #{n="Action";e={if($_.EventID -eq 7001) {"Logon"} else {"Logoff"}}}
$TimeProeprty = #{n="Time";e={$_.TimeGenerated}}
Get-EventLog System -Source Microsoft-Windows-Winlogon | select $UserProperty,$TypeProperty,$TimeProeprty
I did do throw in a $Computername variable and a Foreach loop statment like in the following to try and get it to run on a remote computer, but it keeps checking the local system that I am on, not the remote system:
$Computername = Read-Host "Enter Computername Here"
Foreach $Computer in $Computername
{
$UserProperty = #{n="User";e={(New-Object System.Security.Principal.SecurityIdentifier $_.ReplacementStrings[1]).Translate([System.Security.Principal.NTAccount])}}
$TypeProperty = #{n="Action";e={if($_.EventID -eq 7001) {"Logon"} else {"Logoff"}}}
$TimeProeprty = #{n="Time";e={$_.TimeGenerated}}
Get-EventLog System -Source Microsoft-Windows-Winlogon | select $UserProperty,$TypeProperty,$TimeProeprty
}
I know this is an old question, but no answer was ever accepted. One of the problems is that the script doesn't show which machine the user was logged into. Anyway, I fixed it up (including the typo).
Get-LogonHistory.ps1:
param(
[alias("CN")]
$ComputerName="localhost"
)
$UserProperty = #{n="User";e={(New-Object System.Security.Principal.SecurityIdentifier $_.ReplacementStrings[1]).Translate([System.Security.Principal.NTAccount])}}
$TypeProperty = #{n="Action";e={if($_.EventID -eq 7001) {"Logon"} else {"Logoff"}}}
$TimeProperty = #{n="Time";e={$_.TimeGenerated}}
$MachineNameProperty = #{n="MachinenName";e={$_.MachineName}}
foreach ($computer in $ComputerName) {
Get-EventLog System -Source Microsoft-Windows-Winlogon -ComputerName $computer | select $UserProperty,$TypeProperty,$TimeProperty,$MachineNameProperty
}
With this, it will show which machine the user logged into. Multiple remote computers can be passed into the command line with commas between each (no spaces).
You need to use the Get-EventLog cmdlet's ComputerName parameter:
Get-EventLog -ComputerName $Computer System -Source Microsoft-Windows-Winlogon `
| select $UserProperty,$TypeProperty,$TimeProeprty
Also, it looks like you have a typo in your $TimeProeprty variable.
A bit modified and its working
# Specify the location you want the report to be saved
$filelocation = "C:\report.csv"
[void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic')
[string]$Computer = [Microsoft.VisualBasic.Interaction]::InputBox("Enter ComputerName", "Computer Name", "Computer Name")
[int]$DayPrompt = [Microsoft.VisualBasic.Interaction]::InputBox("Enter Number of Days to check", "Days to Check", "15")
$Days = $DayPrompt
cls
$Result = #()
Write-Host "Gathering Event Logs, this can take awhile..."
$ELogs = Get-EventLog System -Source Microsoft-Windows-WinLogon -After (Get-Date).AddDays(- $Days) -ComputerName $Computer
If ($ELogs)
{ Write-Host "Processing..."
ForEach ($Log in $ELogs)
{ If ($Log.InstanceId -eq 7001)
{ $ET = "Logon"
}
ElseIf ($Log.InstanceId -eq 7002)
{ $ET = "Logoff"
}
Else
{ Continue
}
$Result += New-Object PSObject -Property #{
Time = $Log.TimeWritten
'Event Type' = $ET
User = (New-Object System.Security.Principal.SecurityIdentifier $Log.ReplacementStrings[1]).Translate([System.Security.Principal.NTAccount])
}
}
$Result | Select Time,"Event Type",User | Sort Time -Descending | Export-CSV $filelocation
Write-Host "Done look at $filelocation"
}
Else
{ Write-Host "Problem with $Computer."
Write-Host "If you see a 'Network Path not found' error, try starting the Remote Registry service on that computer."
Write-Host "Or there are no logon/logoff events (XP requires auditing be turned on)"
}
You're not passing the computer name to any command in the loop. So it's just looping through the same command for as many objects are in $computerName Try changing the last line to this:
Get-EventLog System -Source Microsoft-Windows-Winlogon -ComputerName $computer | select $UserProperty,$TypeProperty,$TimeProperty
If that doesn't work, make sure that your foreach loop is passing the right data:
$computerName | Foreach-Object{Write-Host $_}
That should display the computer name of each of the machine's you're trying to run this on.
But it looks like you're trying to run it for one computer, so remove the Foreach loop and just add -ComputerName $computername to the end of the Get-Eventlog command before your select statement
Based on https://gallery.technet.microsoft.com/Log-Parser-to-Identify-8aac36bd
Get-Eventlog -LogName Security | where {$_.EventId -eq "4624"} | select-object #{Name="User"
;Expression={$_.ReplacementStrings[5]}} | sort-object User -unique
You can grab other info from ReplacementStrings. You can also specify a remote computer in the Get-Eventlog command.
# Specify the location you want the report to be saved
$filelocation = "C:\Path\report.csv"
[void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic')
[string]$Computer = [Microsoft.VisualBasic.Interaction]::InputBox("Enter ComputerName", "Computer Name", "Computer Name")
[int]$DayPrompt = [Microsoft.VisualBasic.Interaction]::InputBox("Enter Number of Days to check", "Days to Check", "15")
$Days = $DayPrompt
cls
$Result = #()
Write-Host "Gathering Event Logs, this can take awhile..."
$ELogs = Get-EventLog System -Source Microsoft-Windows-WinLogon -After (Get-Date).AddDays(- $Days) -ComputerName $Computer
If ($ELogs)
{ Write-Host "Processing..."
ForEach ($Log in $ELogs)
{ If ($Log.InstanceId -eq 7001)
{ $ET = "Logon"
}
ElseIf ($Log.InstanceId -eq 7002)
{ $ET = "Logoff"
}
Else
{ Continue
}
$Result += New-Object PSObject -Property #{
Time = $Log.TimeWritten
'Event Type' = $ET
User = (New-Object System.Security.Principal.SecurityIdentifier $Log.ReplacementStrings[1]).Translate([System.Security.Principal.NTAccount])
}
}
$Result | Select Time,"Event Type",User | Sort Time -Descending | Export-CSV $filelocation - TypeInformation
Write-Host "Done."
}
Else
{ Write-Host "Problem with $Computer."
Write-Host "If you see a 'Network Path not found' error, try starting the Remote Registry service on that computer."
Write-Host "Or there are no logon/logoff events (XP requires auditing be turned on)"
}