Task scheduler running power shell script - Username error? - powershell

So I have a powershell script that when a user logs into a PC it emails details of the login to me, for example username,IP,location etc. It runs from task scheduler and has a trigger on login.
The script runs fine through powershell my issue I am having that no matter who logs in it always runs the script on my account in task scheduler hence always inputting my name as the Username even tough a different user may be logged in. I've tried calling the username multiple ways but always shows my name as It runs off my account.
For calling the username I have used :
$username = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$env:UserName
$env:UserDomain
All return the user of who is running the scheduled task.
Any help to get it to print the true user who has logged in would be much appreciated.
Thanks.

It may be because your scheduled task is executing with your credentials. There's no way around this, sadly. That said you can kind of side-step the behaviour by getting the user account associated with the explorer.exe process:
$username = Get-WmiObject Win32_Process -Filter "Name='explorer.exe'" | ForEach-Object { $_.GetOwner() } | Select-Object -Unique -Expand User
This should work fine, as long as you aren't ever in a situation where multiple users could be logged into a machine at the same time (fast user switching, terminal services etc.)
EDIT: To get what you're after, you might need something more like this
$op = #()
$owners = Get-WmiObject Win32_Process -Filter "Name='explorer.exe'" | ForEach-Object { $_.GetOwner() } | Select-Object -Unique -Expand User
foreach($owner in $owners)
{
$wmiprocs = get-wmiobject win32_process -filter "Name='explorer.exe'" | where { $_.getowner().user -eq $owner } | select-object processid
$procs = $wmiprocs | % { get-process -id $_.processid | select-object starttime }
$obj = new-object object
$obj | add-member -type noteproperty -name Username $owner
$obj | add-member -type noteproperty -name Logon ($procs | sort -descending)[0].starttime
$op += $obj
}
$op | ft -auto
Note that your PowerShell instance will need to be running as Admin to query the processes of other users.

Related

PowerShell - server local admins reporting from a list of servers

Beginner question. We only grant access to servers by AD group. We need to report who has admin access to a list of Windows servers. My auditor likes my Server Admins script however she also wants to know the group members first, last name. I don't need to use the ADGroupMember script, if there is a better way.
If someone could point me in the right direction that will be great. It's important I understand so I can do it myself next time : )
Thanks in advance
$computers = Get-content "c:\scripts\servers.txt"
ForEach ($Line In $computers)
{
#write-host $Line
Invoke-command -ComputerName $line -ScriptBlock { net localgroup administrators} | Get-ADGroupMember -Identity "$_????what goes here????" |%{get-aduser $_.SamAccountName | select userPrincipalName } | out-file "c:\scripts\'$line'LocalAdmin.txt"
}
This script works great but does not list out group members first, lastname
$computers = Get-content "c:\scripts\servers.txt"
ForEach ($Line In $computers)
{
#write-host $Line
Invoke-command -ComputerName $line -ScriptBlock { net localgroup administrators} | out-file "c:\scripts\'$line'LocalAdmin.txt"
}
If you really need information about the users in the local Administrators group, you can use the cmdlets from the PSv5.1+ Microsoft.PowerShell.LocalAccounts module.
However, note that local accounts just have a single .FullName property, not separate first and last name ones. Also, this property may or may not be filled in:
Invoke-Command -ComputerName (Get-Content c:\scripts\servers.txt) -ScriptBlock {
Get-LocalGroupMember -Group Administrators |
Where-Object ObjectClass -eq User |
Select-Object Sid |
Get-LocalUser
} |
Sort-Object PSComputerName |
Select-Object PSComputerName, Name, FullName
If domain users are among the group's members and you do need separate first and last name information, pipe to Get-ADUser instead of to Get-LocalUser - you can distinguish users by their source (where they are defined) via the .PrincipalSource property, available on the output objects from Get-LocalGroupMember from Window 10 / Windows Server 2016.
An alternative to mklement0's helpful answer, somewhat old school, using [adsi]:
$servers = Get-Content c:\scripts\servers.txt
Invoke-Command -ComputerName $servers -ScriptBlock {
$adsi = [adsi]"WinNT://$env:COMPUTERNAME,computer"
$adsi.PSBase.Children.Find('Administrators').PSBase.Invoke('members') |
ForEach-Object {
$Name = $_.GetType().InvokeMember('Name','GetProperty',$null,$_,$null)
$class = $_.GetType().InvokeMember('Class','GetProperty',$null,$_,$null)
$adspath = $_.GetType().InvokeMember('ADSPath','GetProperty',$null,$_,$null)
$sid = [System.Security.Principal.SecurityIdentifier]::new(
$_.GetType().InvokeMember('objectsid','GetProperty',$null,$_,$null),0
).Value
[pscustomobject]#{
Name = $Name
Class = $Class
Path = $adspath -replace '^WinNT://'
SecurityIdentifier = $sid
}
} | Sort-Object Class -Descending
} | Where-Object Class -EQ User

Use powershell on remote machine for Edge Version

I have a script that returns Version numbers for 3rd Party software running on our Domain. Java, chrome, etc to ensure we are up to date.
This is all working OK.
However, I am trying to use:
Get-AppxPackage -Name Microsoft.MicrosoftEdge | select-object Version
within a remote Powershell session or 'invoke-command' but it is returning no results,
the command works fine if locally, and also when RDP'd onto the remote machine.
How can i use this cmdlet remotely to check Edge is version compliant?
Many Thanks.
EDIT:
import-module activedirectory
$workstations = Get-ADComputer -Filter "OperatingSystem -like 'Windows 10 *'" -Property * | select name -ExpandProperty Name
foreach ($workstation in $workstations)
{
$session = New-PSSession -Computername $workstation
$resultofsession = Invoke-Command -Session $Session -ScriptBlock{
$Path="HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
$path2 = "HKLM:\SOFTWARE\Microsoft\Internet Explorer\"
$java = Get-ItemProperty $Path | Select-Object DisplayName, DisplayVersion | where displayname -like "java*"
$chrome = Get-ItemProperty $path | Select-Object DisplayName, DisplayVersion | where displayname -ceq "Google Chrome"
$adobe = Get-ItemProperty $path | Select-Object DisplayName, DisplayVersion | where displayname -ceq "Adobe Acrobat Reader DC"
$edge = Get-AppxPackage -Name Microsoft.MicrosoftEdge | select-object Version
$ie = get-itemProperty $path2
$Object = New-Object PSObject -property #{
'chrome' = "CHROME: " + $chrome.displayversion + ","
'edge' = "EDGE: " + $edge + ","
'ie' = "IE: " + $ie.svcVersion + ","
'java' = "JAVA: " + $java.Displayversion + ","
'adobe' = "ADOBE: " + $adobe.displayversion + ","
'hostname' = hostname
}
Write-output $object
}
remove-pssession $session
write-output $resultofsession | format-table -HideTableHeaders -autosize -force | Out-File "C:\web\Version.txt" -append
}
Get-AppxPackage will return only information for the current users profile (in this case, the account running the script). You likely need to add the -AllUsers switch, but note this will return a result for each user logged in (and they may have different versions). You can use the -user parameter to specify a specific user.
AppX packages are only updated for the user profile when they log in, hence why different users can have different versions of an app on the same workstation. Assuming everything works as expected, the app should be updated when the user next logs in.
Try the following to return the version number for each user ID:
$edge = Get-AppxPackage -AllUsers -Name Microsoft.MicrosoftEdge | select-object #{N="User"; E={$_.packageUserInformation.UserSecurityId.Username}},Version
Example output:
User Version
---- -------
test 42.17127.1.0
S-1-5-18 44.17763.1.0
jacob 44.18252.1000.0

How to "read" a user account with Powershell

I am trying to create a backup powershell script for user documents. I have created a script where I am putting on a form box the username I want to backup and then the script proceeds. The last thing is that I want to have a rule that if I put a wrong name, the script will not proceed.
Does anyone knows how I can "read" the present useraccounts on a laptop, in order to create rule to cross-check the input with the useraccounts?
Best regards.
This will read all of the folders in C:\Users and attempt to find a corresponding account in AD:
Get-Childitem C:\users -Directory |
ForEach-Object {
$profilePath = $_.FullName
Get-AdUser -Filter {SamAccountName -eq $_.Name} |
ForEach-Object {
New-Object -TypeName PsCustomObject |
Add-Member -MemberType NoteProperty -Name Name -Value $_.Name -PassThru |
Add-Member -MemberType NoteProperty -Name ProfilePath -Value $profilePath -PassThru |
Add-Member -MemberType NoteProperty -Name SID -Value $_.SID -PassThru
}
} | Format-Table Name,SID, profilePath -AutoSize
Obviously you can modify to get different AD properties, if needed. Also, remove the Format-Table command in order to pipe the output objects for further manipulation.
Note:
This requires the AD PowerShell Module to be installed on the system you run the script on.
It only outputs an object if it finds a user account, so you won't see anything for folders it finds that have no account.
Get-WmiObject -Class Win32_UserAccount -Filter "LocalAccount='True'" | Select Name
will give you names of local accounts. Other available fields for select are listed here.

List process for current user

As an administrator I can get a users processes by running this
Get-Process -IncludeUserName | Where UserName -match test
But as a non-administrator I can't use -IncludeUserName becuase "The 'IncludeUserName' parameter requires elevated user rights".
So if I'm logged on as the user test, how do I list only his processes and not everything that's running?
This is faster, one line, doesn't require admin.
Get-Process | ? {$_.SI -eq (Get-Process -PID $PID).SessionId}
You can do that through WMI. Here is an excerpt of an article you can find here.
$View = #(
#{l='Handles';e={$_.HandleCount}},
#{l='NPM(K)';e={ (Get-Process -Id $_.ProcessId).NonpagedSystemMemorySize/1KB -as [int]}},
#{l='PM(K)';e={ $_.PrivatePageCount/1KB -as [int]}},
#{l='WS(K)';e={ $_.WorkingSetSize/1KB -as [int]}},
#{l='VM(M)';e={ $_.VirtualSize/1mB -as [int]}},
#{l='CPU(s)';e={ (Get-Process -Id $_.ProcessId).CPU -as [int]}},
#{l='Id';e={ $_.ProcessId}},
'UserName'
#{l='ProcessName';e={ $_.ProcessName}}
)
Get-WmiObject Win32_Process | % { $_ |
Add-Member -MemberType ScriptProperty -Name UserName -Value {
'{0}\{1}' -f $this.GetOwner().Domain,$this.GetOwner().User
} -Force -PassThru
} | ? UserName -match $env:USERNAME | ft $View -AutoSize
Get-Process alone will not give you this information, you'll need WMI:
$owners = #{}
gwmi win32_process |% {$owners[$_.handle] = $_.getowner().user}
$ps = get-process | select processname,Id,#{l="Owner";e={$owners[$_.id.tostring()]}}
foreach($p in $ps) {
if($p.Owner -eq $env:USERNAME) {
$p
}
}
Thanks for your code. Based on this, I created a modified version to allow users to kill (a subset of) their own processes:
#Script to allow users to kill (a subset of) their own processes
#Based on : https://stackoverflow.com/questions/35195221/list-process-for-current-user
#Previously we used Task Nanny created by Michel Stevelmans which is a lot faster, but it did not show a process that was causing issues for our users.
$UserProcesses = #()
$Owners = #{}
Get-WmiObject win32_process | Foreach{$owners[$_.handle] = $_.getowner().user}
$Processes = Get-Process | select processname,Description,Id,#{l="Owner";e={$owners[$_.id.tostring()]}}
Foreach($Process in $Processes)
{
IF($process.Owner -eq $env:USERNAME)
{
$UserProcesses += $Process
}
}
$UserProcessesToExclude = #(
'concentr', #Citrix Connection Center
'conhost', #Console Window Host
'dwm', #Desktop Windows Manager
'explorer', #Explorer
'Receiver', #Citrix Receiver Application
'rundll32', #Windows host process (Rundll32)
'ssonsvr', #Citrix Receiver
'taskhost' #Host Process for Windows Tasks
'wfcrun32' #Citrix Connection Manager
'wfshell' #Citrix wfshell shell
)
$UserProcesses | Where{$_.ProcessName -notin $UserProcessesToExclude} | Out-GridView -Title 'Task killer - Select the process(es) you want to kill. Hold CTRL to select multiple processes.' -PassThru | Foreach{Stop-Process -id $_.Id}

User input to perform action

I'm building a script that I want to allow for user input to take action.
Currently this tool can checked all remote servers for any automatic stopped services. I do not want the script to automatically start them though. What I would like user input to start these services.
Write-Host "Checking for Stopped Services..."
$stoppedServices = Invoke-Command $Servers {
Get-CimInstance Win32_Service -ComputerName $Servers -Filter "startmode = 'auto' AND state != 'running'" |
Sort state | select name, state
} -Credential $Credential
if ($stoppedServices -ne $null) {
Clear-host
$stoppedServices
} else {
Clear-Host
Write-Host ("No stopped services found!")
}
So users have the ability to see the stopped services. I want to do 2 things from this. Write out the stopped services with the option, do you want to start these stopped services?
With that option then either exit or start the service. I'd imagine I can achieve this with another foreach loop but can never get it work.
You may want to use Get-WmiObject instead of Get-CimInstance, collect the matching services in a hashtable, and pass the list into Out-GridView with the -PassThru option. The gridview will return just the objects selected by the user which you can use to call the StartService() method on the service object in the hashtable.
$svc = #{}
Get-WmiObject Win32_Service -Computer $Servers -Credential $Credential -Filter "startmode = 'auto' AND state != 'running'" |
ForEach-Object { $svc[$_.DisplayName] = $_ }
$svc.Values | Select-Object SystemName, DisplayName, State |
Sort-Object State, SystemName, DisplayName |
Out-GridView -PassThru |
ForEach-Object { $svc[$_.DisplayName].StartService() }