$a = 1
while($a = 1){
$explorer = Get-Process explorer.exe -ErrorAction SilentlyContinue -IncludeUserName
if ($explorer.Administrator) {
}
else{
start explorer
}
timeout 10
}
I need to get the task from my user... Any ideas.
We are in a project on one computer. My friend is connected with Remotedesktop. We troll each other with closing the explorer etc. I want to restart the explorer after he closed it. His is still running. Thats the problem.
Try Get-Process with -IncludeUserName
$explorer = Get-Process explorer -ErrorAction SilentlyContinue -IncludeUserName
$explorer.UserName
But the IncludeUserName parameter requires elevated user rights. Run script as Administrator.
UPD. This will help you with process start if there is no explorer running with your username. Replace put_your_username_here to your username.
while($true){
$flagToStart = $true
$explorersArray = Get-Process "explorer" -ErrorAction SilentlyContinue -IncludeUserName
foreach($explorerProc in $explorersArray) {
if ($explorerProc.UserName -like "*put_your_username_here") {
Write-Output "No need to restart"
$flagToStart = $false
}
}
if($flagToStart -eq $true) {
Start-Process -FilePath "explorer"
}
Start-Sleep -Seconds 5
}
Related
I am working on developing PowerShell script to automate a task on a remote server by using Invoke-Command with WinRM.
The script will take the server IP, test WinRM and "Get-Credential" cmdlet to establish session and use Invoke-Command to run another script on remote server. I have made significant progress of what I want to achieve, however, I am having trouble on how to setup the code so that when I press the "Cancel" or "X" button on Get-Credential prompt it should abort the script and return to the regular PowerShell command line prompt.
Below is what I have so far, I have erased the comments and description of the code to keep the number of words less in here.
function SS
{
Add-Type -AssemblyName System.Windows.Forms
$BInput = [System.Windows.Forms.MessageBox]::Show('Do you want to proceed?', 'Confirmation',[System.Windows.Forms.MessageBoxButtons]::YesNo)
switch ($BInput)
{
"Yes" {
while ($true)
{
$server=Read-Host "Enter Server IP Address"
set-item -Path WSMan:\localhost\Client\TrustedHosts -Value "$server" -Force
if(Test-WSMan -ComputerName $server -ErrorAction SilentlyContinue)
{
Write-Host "$server is accessible, enter credentials to connect"
while ($true)
{
$creden=Get-Credential -Message "Please enter the server credentials that you want to connect"
$serversession = New-Pssession -ComputerName $server -Credential $creden -ErrorAction SilentlyContinue
if(-not($serversession))
{
write-warning "Credentials are not valild, please try again"
}
else
{
write-host "$server is connected, starting the workflow ......"
Invoke-Command -Session $serversession -FilePath "C:\Temp\XXX.ps1"
}
}
Break
}
else
{
write-host "Windows Remote Management (WinRM) protocol is not running, please check service and confirm."
}
}
Get-Pssession | Remove-PSSession
}
"No" {
Break
}
}
}
I understand I have to apply the changes / logic after this line
$creden=Get-Credential -Message "Please enter the server credentials that you want to connect"
But can't seem to find it yet. I looked online and have taken different approaches but no success so far. I would like to have opinions or recommendations on how to tackle this, appreciate your help.
Thanks
What i'm seeing is that you may be thinking too much into it. A simple if statement should do the trick, try:
$creden=Get-Credential -Message "Please enter the server credentials that you want to connect"
if(!$creden){break}
Continuing from my comment.
Try this refactor of your use case.
Point of note: Note fully tested since I do not have an environment at this time to test.
Function Start-WorkFlow
{
<#
.Synopsis
Execute a workflow
.DESCRIPTION
Sets up a WinRM session to a remote host to execute the defined workflow
.EXAMPLE
Start-WorkFlow
.EXAMPLE
swf
.INPUTS
Remote host IPAddress
Remove host credentials
.OUTPUTS
Resutls of teh workflow
.NOTES
V 0.0.1 - Prototype script. Clean-Up before production use
.COMPONENT
Stand-alone script
.ROLE
Administrative actions
.FUNCTIONALITY
Implemetned error logic for each code block
Restrict the user input to only be a proper IPAddress
Validate TCPIP state
Validate WSman state
Establish a new session
Process workflow
Exit session
#>
[cmdletbinding(SupportsShouldProcess)]
[Alias('swf')]
Param
(
)
If ((Read-Host -Prompt 'Do you want to proceed: [Yes/No]') -eq 'No' )
{Break}
Else
{
Do {$RemoteServerIPAddress = (Read-Host -Prompt 'Enter Server IP Address')}
Until ($RemoteServerIPAddress -match "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$")
Get-ChildItem -Path 'WSMan:\localhost\Client\TrustedHosts'
Try
{
(Test-Connection -ComputerName $RemoteServerIPAddress -Count 1 -ErrorAction Stop).IPV4Address
# Set-Item -Path 'WSMan:\localhost\Client\TrustedHosts' -Value $RemoteServerIPAddress -Force
Get-ChildItem -Path 'WSMan:\localhost\Client\TrustedHosts'
Try
{
Test-WSMan -ComputerName $RemoteServerIPAddress -ErrorAction Stop
"$RemoteServerIPAddress is accessible, enter credentials to connect"
Do
{
$Creds = $null
$CredMesssage = 'Please enter the remote server credentials that you want to connect.'
$CredMesssage = "$CredMesssage If credentials are not valid, you will be prompted to re-enter them."
$Creds = Get-Credential -Message $CredMesssage
if(-Not $creds)
{
Write-Warning -Message 'Credential request cancelled.'
Start-Sleep -Seconds 3
Exit
}
$NewPSSessionSplat = #{
ComputerName = $RemoteServerIPAddress
Credential = $Creds
Name = 'RemoteSessionName'
ErrorAction = 'Stop'
}
New-PSSession $NewPSSessionSplat
}
Until (Get-PSSession -Name 'RemoteSessionName')
"$RemoteServerIPAddress is connected, starting the workflow ......"
Invoke-Command -Session $RemoteServerSession -FilePath 'C:\Temp\XXX.ps1'
}
Catch
{
Write-Warning -Message 'Session connection results:'
$PSitem.Exception.Message
}
Finally
{
Get-PSSession |
Remove-PSSession -ErrorAction SilentlyContinue
}
}
Catch
{
Write-Warning -Message "
The remote server $RemoteServerIPAddress is not available
Exiting the session."
Start-Sleep -Seconds 3
Exit
}
}
}
Start-WorkFlow
I have a script that stops a service then renames a folder on the local computer, i would like to be able to make a run remotely by asking the command executer to enter the machine name of where it would like to run the script then execute.
try {
Stop-service wuauserv -force
} catch {
Write-error "Unable to stop the service WUAUSERV"
start-sleep -seconds 5
exit
}
try {
Rename-Item "C:\Windows\SoftwareDistribution" "C:\Windows\SoftwareDistribution.old" -force
} catch {
Write-error "Unable to rename the folder :("
start-service wuauserv
start-sleep -seconds 5
exit
}
start-service wuauserv
I would probably have the script take a parameter by adding this at the top
Param(
[Parameter(Mandatory)][string]$ComputerName
)
The Mandatory value will prompt the user to enter a value if it was not supplied.
Alternatively if you really want to manually ask the user you can use this:
$ComputerName = Read-Host "ComputerName"
This will also prompt the user to enter a variable.
Then use Invoke-Command to run your logic on the remote computer
Invoke-Command -ComputerName $ComputerName -ScriptBlock {
# Your code here..
}
Edit:
After your update, I notice you terminate the script by calling exit. I would advise you to use return instead. If someone runs this script from the commandline the script will kill the terminal, which is quite annoying.
Edit2: Here is a complete working example, with my recommendations:
Param(
[Parameter(Mandatory)][string]$ComputerName
)
Invoke-Command -ComputerName $ComputerName -ScriptBlock {
try {
Stop-Service wuauserv -Force
} catch {
Write-error "Unable to stop the service WUAUSERV"
Start-Sleep -Seconds 5
return
}
try {
Rename-Item "C:\Windows\SoftwareDistribution" "C:\Windows\SoftwareDistribution.old" -Force
} catch {
Write-error "Unable to rename the folder :("
Start-Service wuauserv
Start-Sleep -Seconds 5
return
}
Start-Service wuauserv
}
Thought I would share this quick function I made for myself, feel free to adapt it and improve it according to your needs.
Sometimes you want to run commands as the logged on user of a remote computer.
As you know, some commands show output for the user who runs it and if you run the same command with Invoke-Command, it won't return the user's information, but yours). Get-Printer is an example amongst many others.
There is no easy, quick way of running commands as the logged on user natively without any third-party apps like PsExec or others so I made this quick function that uses VBS, PS1 and Scheduled Task to make it happen.
It runs completly silently for the user (thanks to the VBS) and the output is shown in your console. Please note it assumes the remote computer has a C:\TEMP.
Created in a Windows 10, powershell v 5.1.17763.503 environement.
I don't pretend it's final and perfect, it's the simplest way I found to do what is needed and I just wanted to share it with you guys as it can be very useful!
Check the comments for explanation of the code and feel free to use it as you wish. Please share your version as I'm curious to see people improve it. A good idea would be to make it support multiple computers, but as I said it's a quick function I did I don't have too much time to put into refining it.
That being said, I had no problems using it multiple times as is :)
*Output returned is in form of a string, if you want to have a proper object, add '| ConvertFrom-String' and play with it :)
PLEASE NOTE: The surefire way of grabbing the username of who is currently logged on is via QWINSTA (since Win32_ComputerSystem - Username is only reliable if a user is logged on LOCALLY, it won't be right if a user is using RDP/RemoteDesktop). So this is what I used to grab the username, however, please note that in our french environement the name of the username property in QWINSTA is "UTILISATEUR",so you have to change that to your needs (english or other language) for it to work. If I remember correctly, it's "USERNAME" in english.
On this line:
$LoggedOnUser = (qwinsta /SERVER:$ComputerName) -replace '\s{2,22}', ',' | ConvertFrom-Csv | Where-Object {$_ -like "*Acti*"} | Select-Object -ExpandProperty UTILISATEUR
See code in the answer below.
function RunAsUser {
Param ($ComputerName,$Scriptblock)
#Check that computer is reachable
Write-host "Checking that $ComputerName is online..."
if (!(Test-Connection $ComputerName -Count 1 -Quiet)) {
Write-Host "$ComputerName is offline" -ForegroundColor Red
break
}
#Check that PsRemoting works (test Invoke-Command and if it doesn't work, do 'Enable-PsRemoting' via WMI method).
#*You might have the adjust this one to suit your environement.
#Where I work, WMI is always working, so when PsRemoting isn't, I enable it via WMI first.
Write-host "Checking that PsRemoting is enabled on $ComputerName"
if (!(invoke-command $ComputerName { "test" } -ErrorAction SilentlyContinue)) {
Invoke-WmiMethod -ComputerName $ComputerName -Path win32_process -Name create -ArgumentList "powershell.exe -command Enable-PSRemoting -SkipNetworkProfileCheck -Force" | Out-Null
do {
Start-Sleep -Milliseconds 200
} until (invoke-command $ComputerName { "test" } -ErrorAction SilentlyContinue)
}
#Check that a user is logged on the computer
Write-host "Checking that a user is logged on to $ComputerName..."
$LoggedOnUser = (qwinsta /SERVER:$ComputerName) -replace '\s{2,22}', ',' | ConvertFrom-Csv | Where-Object {$_ -like "*Acti*"} | Select-Object -ExpandProperty UTILISATEUR
if (!($LoggedOnUser) ) {
Write-Host "No user is logged on to $ComputerName" -ForegroundColor Red
break
}
#Creates a VBS file that will run the scriptblock completly silently (prevents the user from seeing a flashing powershell window)
#"
Dim wshell, PowerShellResult
set wshell = CreateObject("WScript.Shell")
Const WindowStyle = 0
Const WaitOnReturn = True
For Each strArg In WScript.Arguments
arg = arg & " " & strArg
Next 'strArg
PowerShellResult = wshell.run ("PowerShell " & arg & "; exit $LASTEXITCODE", WindowStyle, WaitOnReturn)
WScript.Quit(PowerShellResult)
"# | out-file "\\$ComputerName\C$\TEMP\RAU.vbs" -Encoding ascii -force
#Creates a script file from the specified '-Scriptblock' parameter which will be ran as the logged on user by the scheduled task created below.
#Adds 'Start-Transcript and Stop-Transcript' for logging the output.
$Scriptblock = "Start-Transcript C:\TEMP\RAU.log -force" + $Scriptblock + "Stop-Transcript"
$Scriptblock | out-file "\\$ComputerName\C$\TEMP\RAU.ps1" -Encoding utf8 -force
#On the remote computer, create a scheduled task that runs the .ps1 script silently in the user's context (with the help of the vbs)
Write-host "Running task on $ComputerName..."
Invoke-Command -ComputerName $ComputerName -ArgumentList $LoggedOnUser -ScriptBlock {
param($loggedOnUser)
$SchTaskParameters = #{
TaskName = "RAU"
Description = "-"
Action = (New-ScheduledTaskAction -Execute "wscript.exe" -Argument "C:\temp\RAU.vbs C:\temp\RAU.ps1")
Settings = (New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -DontStopOnIdleEnd)
RunLevel = "Highest"
User = $LoggedOnUser
Force = $true
}
#Register and Start the task
Register-ScheduledTask #SchTaskParameters | Out-Null
Start-ScheduledTask -TaskName "RAU"
#Wait until the task finishes before continuing
do {
Write-host "Waiting for task to finish..."
$ScheduledTaskState = Get-ScheduledTask -TaskName "RAU" | Select-Object -ExpandProperty state
start-sleep 1
} until ( $ScheduledTaskState -eq "Ready" )
#Delete the task
Unregister-ScheduledTask -TaskName "RAU" -Confirm:$false
}
Write-host "Task completed on $ComputerName"
#Grab the output of the script from the transcript and remove the header (first 19) and footer (last 5)
$RawOutput = Get-Content "\\$ComputerName\C$\temp\RAU.log" | Select-Object -Skip 19
$FinalOutput = $RawOutput[0..($RawOutput.length-5)]
#Shows output
return $FinalOutput
#Delete the output file and script files
Remove-Item "\\$ComputerName\C$\temp\RAU.log" -force
Remove-Item "\\$ComputerName\C$\temp\RAU.vbs" -force
Remove-Item "\\$ComputerName\C$\temp\RAU.ps1" -force
}
#____________________________________________________
#Example command
#Note: Sometimes Start-Transcript doesn't show the output for a certain command, so if you run into empty output, add: ' | out-host' or '| out-default' at the end of the command not showing output.
$Results = RunAsUser -ComputerName COMP123 -Scriptblock {
get-printer | Select-Object name,drivername,portname | Out-host
}
$Results
#If needed, you can turn the output (which is a string for the moment) to a proper powershell object with ' | ConvertFrom-String'
There are multiple users logged into the machine. for each user I wanted to print the status (logged in or locked) continuously for each user logged in to the machine. how can get the status of the user. this script will run on each user login using the Scheduler.
$user = $env:UserName
do
{
# Get system Status (Locked or Logged in )
print(status)
Start-Sleep -s 30
} while(1)
How to get the status. Please help me on this.
in case there are multiple users, you have to look for LogonUI.exe process and the session it is running in. You can test it with code below. Works fine on my terminal server. Please make sure that PowerShell is running with elevated permissions.
for ($i = 0; $i -lt 10; $i++) {
$process = #(Get-WmiObject -Class Win32_Process -Filter "Name = 'LogonUI.exe'" -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -eq $([System.Diagnostics.Process]::GetCurrentProcess().SessionId)})
if ($process.Count -gt 0) {
Write-Host "Computer is locked"
}
else {
Write-Host "Computer is unlocked"
}
Start-Sleep -Seconds 5
}
Hope it helps,
Stanislav
I'm trying to setup Powershell ISE to manage few VMs hosted under Hyper-V and using Powershell Direct.
What I'm trying to do is:
collect in one script all relevant "settings" under variables whose name starts with an appropriate prefix, for example "vms";
at the end of this script open a PowerShellTab for each VM to manage (if not already opened);
copy in the runspace of this new Tab all the relevant settings variables (I think this is necessary for the next step);
start a remote PSSession with the VM (if not already started) and copy or update the settings variables also in this remote PSSession;
enter interactively this PSSession to be able to launch my commands (F8) after selecting portions of sample/template scripts.
My script is this one.
$vms = 'VMName1','VMName2'
$vmsCredentials = #{
VMName1 = [pscredential]::new('User1', (ConvertTo-SecureString 'pw1' -AsPlainText -force))
VMName2 = [pscredential]::new('User2', (ConvertTo-SecureString 'pw2' -AsPlainText -force))
}
$vmsInfo1 = 'blah,blah,blah'
$vmsInfo2 = #{}
$vmsInfo3 = #(1,2,3)
$tabPrevious = $psISE.CurrentPowerShellTab
$vms |% {
Write-Information $_
$tab = $psISE.PowerShellTabs |? DisplayName -eq $_
if ($tab -eq $null)
{
# opens new PS ISE Tab
$tab = $psISE.PowerShellTabs.Add()
$psISE.PowerShellTabs.SetSelectedPowerShellTab($tabPrevious)
$tab.DisplayName = $_
$tab.ExpandedScript = $true
$tab.Files.Clear()
# open all files in new tab
$tabPrevious.Files |% FullPath |? { Test-Path $_ } |% {
[void]$tab.Files.Add($_)
}
}
else
{
# exit remote interactive session, if any
if ($tab.Prompt -match "^\[$_\]: ") {
while (!$tab.CanInvoke) { sleep -Milliseconds 100 }
$tab.Invoke('exit')
}
}
# export/update variables to tab
while (!$tab.CanInvoke) { sleep -Milliseconds 100 }
$rs = ($tab.InvokeSynchronous({ $ExecutionContext.Host.Runspace }, $false))[0]
if ($null -ne $rs) {
Get-Variable "vms*" |% { $rs.SessionStateProxy.SetVariable($_.Name, $_.Value) }
}
# start a new remote PSSession for the tab, if not already done
while (!$tab.CanInvoke) { sleep -Milliseconds 100 }
$tab.Invoke("if (`$null -eq `$pchPSSession) { `$pchPSSession = New-PSSession -VMName '$_' -Credential `$vmsCredentials['$_'] }")
# export/update variables to the remote PSSession and enter interactively
while (!$tab.CanInvoke) { sleep -Milliseconds 100 }
$tab.Invoke("Invoke-Command `$pchPSSession { `$args |% { Set-Variable `$_.Name `$_.Value } } -ArgumentList (Get-Variable 'vms*'); Enter-PSSession -Session `$pchPSSession")
# uncomment this line and no error occurs
#[void]$psISE.PowerShellTabs.Remove($tab)
}
Unfortunately this script works well:
the very first time I run the script in a fresh ISE,
or when only one new Tab need to be opened,
or if I close immediately the just added Tab (uncomment last line),
or under Debugger
otherwise the two last PowerShellTab.Invoke fails (Null Reference).
Any idea to solve this error?
Any way to do it better?