How can I launch .cmd files on a remote machine? - windows

I need to be able to launch a .cmd file that is on a remote machine, from within the directory that the file resides on that machine.
I've tried: invoke-command -ComputerName test123 -ScriptBlock { cmd /c c:/myfile.cmd } in powershell, which launches the .cmd, but then fails because it can't find the corresponding .cmds that this one launches (which all reside in the same directory).
Is there a way to launch this .cmd file, and have it's execution persist? i.e., even after the powershell window is closed, the .cmd will continue to run on the remote machine.

You need to change the working directory in the scriptblock. Add a Set-Location before calling the batch script:
Invoke-Command -ComputerName test123 -ScriptBlock {
Set-Location 'C:\'
& cmd /c ".\myfile.cmd"
}
If you need to create a detached process, you can do that for instance via WMI:
$hostname = 'test123'
$command = 'C:\path\to\script.cmd'
$workdir = 'C:\working\directory'
$p = [wmiclass]"\\$hostname\root\cimv2:Win32_Process"
$p.Create($command, $workdir)
Note that you need admin privileges on the remote host for this.

Related

Double-Click Execute as Administrator

I have a powershell file saved to my desktop that I'd like to be able to double-click on and run as administrator. I've tried updating the shortcut used to launch powershell to always run as admin, but I still get the same permission issue. Is there a way to setup the shortcut so that the file always executes as admin? Or another way to set it up so that all powershell scripts run as admin when double-clicked?
Thanks for any help!
Create a "proxy script" to launch other scripts as admin with, and then launch PowerShell using Start-Process -Verb RunAs to elevate it before execution:
RunAsProxy.ps1
# First arg should be the script path
$script = $args[0]
# Rest of args should be any script parameters
$scriptArgs = $args[1..$args.Count] -join ' '
$startProcessArgs = #{
Wait = $true
Verb = 'RunAs'
FilePath = 'powershell'
ArgumentList = "-File `"$script`" $scriptArgs"
}
Start-Process #startProcessArgs
exit $LASTEXITCODE
Create your shortcut to point to RunAsProxy.ps1 instead of the target script. Pass in the path to the target script you want to run elevated and optionally any parameters to that script. If UAC is enabled, you should get prompted to elevate before execution.
This should be re-usable with other scripts you want to do the same with.

How do you run Cleanmgr.exe remotely?

I'm having some trouble getting Clean Manager on Windows 10 to run remotely. I've seen a few different things were you can edit the registry and modify the /sageset or /sagerun to be specific things then run it remotely, but it seems no matter what I do the CleanMgr runs locally on my machine rather than running remotely.
I believe this is the closest I've gotten to get it to run remotely... It seems to still just run locally on my machine though.
Any ideas?
( All variables are set before this portion of the script, this is just a small portion of what's going on that I'm stuck on )
## Starts cleanmgr.exe
Function Start-CleanMGR {
Write-Host "Please provide your A-Account details to continue with this cleanup."
$creds = Get-Credential
Enter-PSSession -ComputerName $computername -Credential $creds
try {
$cleanmgr = Start-Process -Credential $creds -FilePath "C:\Windows\System32\cleanmgr.exe" -ArgumentList '/verylowdisk' -Wait -Verbose
if ($cleanmgr) {
Write-Host "Clean Manager ran successfully! " -NoNewline -ForegroundColor Green
Write-Host "[DONE]" -ForegroundColor Green -BackgroundColor Black
}
}
catch [System.Exception] {
Write-host "Cleanmgr is not installed! To use this portion of the script you must install the following windows features:" -NoNewline -ForegroundColor DarkGray
Write-host "[ERROR]" -ForegroundColor Red -BackgroundColor Black
}
} Start-CleanMGR
PowerShell always runs in the user context of the user who started the session. This is by design.
You can not run a GUI based application remotely using PowerShell. It is a Windows security boundary.
To run GUI apps, someone must be logged on, and you cannot use PowerShell to run code as the logged-on user.
You are also prompting for info, so, someone must be logged on.
If you are expecting a user to prived info, then you need to:
Create the script
Deploy the script to the user's machine or a files share from wich it
can be ran
Tell the user how to do it or create a batch file they would double
click to run the PowerShell script
Or
Set the script to run as a scheduled task at log on or at some point during the day, as the user credentials.
Variables have the scope and you cannot use local variables in a remote context unless they are scoped for that.
About Remote Variables
Using local variables
You can use local variables in remote commands, but the variable must
be defined in the local session.
Beginning in PowerShell 3.0, you can use the Using scope modifier to
identify a local variable in a remote command.
The syntax of Using is as follows:
$Using:<VariableName>
Still, the remote variable is not something you will do in your use case since you cannot do what you are after natively with PowerShell. You'll need a 3rdP tool like MS SysInternals PSExec to run code remotely as the logged-on user.
Using PsExec
Usage: psexec [\computer[,computer2[,...] | #file]][-u user [-p
psswd][-n s][-r servicename][-h][-l][-s|-e][-x][-i [session]][-c
executable [-f|-v]][-w directory][-d][-][-a n,n,...] cmd
[arguments]
-i Run the program so that it interacts with the desktop of the specified session on the remote system. If no session is specified the
process runs in the console session.
-u Specifies optional user name for login to remote computer.
I suggest that you use Invoke-Command
Function Start-CleanMGR ($computername, $creds) {
Invoke-Command -ComputerName $computername -Credential $creds -ScriptBlock {
try {
$cleanmgr = Start-Process -FilePath "C:\Windows\System32\cleanmgr.exe" -ArgumentList '/verylowdisk' -Wait -Verbose
if ($cleanmgr) {
return "Clean Manager ran successfully!"
}
}
catch [System.Exception] {
return "Cleanmgr is not installed! To use this portion of the script you must install the following windows features:"
}
}
}
Start-CleanMGR -computername "remotehost" -creds (Get-Credential)
As long as you execute cleanmgr.exe under a user account that has local admin rights, everything will work. Running cleanmgr.exe under the SYSTEM account, E.G. running from the Run Script Tool in SCCM/MECM will not work unless the script first opens a separate shell (DOS/PS) under a user that has local admin rights...even the cleanmgr.exe /verylowdisk will not run under the SYSTEM account.

Create SymLink with GPO

I have a BATCH script that makes symlinks with mklink. When I run it as an administrator or as a system account (with psexec -s -e) it works as it should. But when I try to use it in a GPO as a startup script it gives me an error "you do not have sufficient privilege to perform this operation" on a target computer. Windows 7 Pro SP1 x64. UAC is disabled.
Batch example:
mklink C:\log\cmd.link.exe C:\Windows\System32\cmd.exe >> C:\log\symlink.log 2>&1
I also tried to wrap it into a powershell script:
Start-Process -FilePath "$env:windir\system32\cmd.exe" -ArgumentList "/c mklink C:\log\cmd.link.exe C:\Windows\System32\cmd.exe >> C:\log\symlink.txt 2>&1" -Verb RunAs
but got the same error. What am I doing wrong?
Maybe there's another way to create a SymLink with GPO or PowerShell?
It appeared that the Group Policy Client (gpsvc) service (since GPO scripts runs with its privilegies) does not contain the privilege to create symolic links (SeCreateSymbolicLinkPrivilege):
C:\>sc qprivs gpsvc
[SC] QueryServiceConfig2 SUCCESS
SERVICE_NAME: gpsvc
PRIVILEGES : SeTakeOwnershipPrivilege
: SeIncreaseQuotaPrivilege
: SeAssignPrimaryTokenPrivilege
: SeSecurityPrivilege
: SeChangeNotifyPrivilege
: SeCreatePermanentPrivilege
: SeShutdownPrivilege
: SeLoadDriverPrivilege
: SeRestorePrivilege
: SeBackupPrivilege
If I want to use this privilege I should at first grant this privelege to the service. It can be done with this command:
sc privs gpsvc SeTakeOwnershipPrivilege/SeIncreaseQuotaPrivilege/SeAssignPrimaryTokenPrivilege/SeSecurityPrivilege/SeChangeNotifyPrivilege/SeCreatePermanentPrivilege/SeShutdownPrivilege/SeLoadDriverPrivilege/SeRestorePrivilege/SeBackupPrivilege/SeCreateSymbolicLinkPrivilege
After that you will be able to use mklink inside GPO scripts.
There are several caveats:
You should list all permissions (current + new). Otherwise you risk to replace all permissions with one.
The command needs System account permission to set privileges so you'll need to use psexec or GPO script (not sure).
If you intend to use psexec it will throw you an error about argument being too long. So you should save this command as a .bat file and then run it with psexec.
Many thanks to #PetSerAl who helped me to find this out.
I still had an issue running the sc.exe commands with PowerShell as a startup script via Group Policy. It was being denied access per Start-Transcript log file. I used the below PowerShell logic for example and it did not work for me in my case.
I tried several variations of multiple things and syntaxes using PowerShell.exe, -verb RunAs, Start-Process and slews of things short of running it as a local script with Task Scheduler as SYSTEM which I was trying to avoid.
Note: This is just a general example of one of the variations that failed with the same result and transcript output as all other
variations tried.
$privs = (sc.exe qprivs gpsvc).Split(":")[5..99] | % { Process { If( $_.Trim().Length -gt 0 ){ $_.Trim() } } };
$privs = $privs + "SeCreateSymbolicLinkPrivilege";
$privs = $privs -Join "/";
Invoke-Expression "sc.exe privs gpsvc $privs"
A solution that works (in my case)
I used the below PowerShell logic as a startup script via Group Policy and now creating symbolic links works. To keep the example simple, I used Google Chrome for generalization.
Basically I had to manipulate the multistring registry value of the correlated permissions for the service rather than using sc.exe appending the needed "SeCreateSymbolicLinkPrivilege" value that way.
#Start-Transcript -Path C:\Log\Transcript.txt
$v = (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\gpsvc").RequiredPrivileges;
If ( $v -notcontains "SeCreateSymbolicLinkPrivilege" ) {
$v = $v + "SeCreateSymbolicLinkPrivilege";
Set-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\gpsvc" RequiredPrivileges $v -Type MultiString;
};
$Chrome86 = "C:\Program Files (x86)\Google\Chrome";
$Chrome = "C:\Program Files\Google\Chrome";
If(!(Test-Path $Chrome86)){
If(Test-Path $Chrome){New-Item -Path $Chrome86 -ItemType SymbolicLink -Value $Chrome -Force}
}
If(!(Test-Path $Chrome)){
If(Test-Path $Chrome86){New-Item -Path $Chrome -ItemType SymbolicLink -Value $Chrome86 -Force}
}

Handle command for remote pc

I want to run handle for a specific folder for a remote pc at my network so to know which processes are locking the folders.
tried:
handle /accepteula \\remotePcName\c:\myFolder
handle /accepteula \\remotePcName\User(name of the account)\c:\myFolder
and some other combinations with no luck getting
No matching handles found.
Is it possible to do this? Run handle for a remote pc?
Could you please try like this:
c:\powershell\Tools\psexec.exe \\remotePcName C:\handle.exe c:\myFolder
How about using invoke-command to execute 'handles' remotely?
$serverName = 'serverName'
$pathtoCheck = 'C:\temp' # folder you want to check on the remote server.
$pathtoHandle = 'c:\temp\handle.exe' #location of handle.exe on the remote server.
Invoke-command -ComputerName $serverName -Scriptblock {
param(
[string]$handles,
[string]$pathToCheck
)
"$handles /accepteula $pathToCheck" | Invoke-Expression
} -ArgumentList $pathtoHandle,$pathtoCheck

How to keep remote powershell command alive after session end?

I use the following command to run setup_server.exe on remote Windows box:
powershell -command "$encpass=convertto-securestring -asplaintext RPASSWORD -force;$cred = New-Object System.Management.Automation.PSCredential -ArgumentList RUSER,$encpass; invoke-command -computername RCOMPUTERNAME -scriptblock {setup_server.exe} -credential $cred;"
setup_server.exe's task is to create some configuration files and start my_server.exe (some daemon process), then it finishes. And I want my_server.exe to keep running after setup_server.exe is finished.
So when I do it via CMD on local box (i.e. just run setup_server.exe from CMD) it works, but when I do it via powershell on remote host it doesn't work. Namely the my_server.exe gets started, but right after setup_server.exe is closed the server also gets closed(killed).
So the question is following:
Which powershell flags/cmdlets should I use to make the described scenario to work as in local mode?
NOTE: I want synchronously get output of setup_server.exe, so running remote command with -AsJob flag, probably wouldn't work for me, though I even don't know if it will keep the server alive after setup_server.exe's end.
The way to keep the remote PowerShell session running after the command has finished is to use a PSSession e.g.:
$s = new-PSSession computername
Invoke-Command -session $s { ..script.. }
... do other stuff, remote powershell.exe continues to run
Remove-PSSession $s # when you're done with the remote session
Generally though exes should run independently from the app that launched them.
Why are you using Invoke-Command. If you want a persistent Session, use Enter-PSSession.
$s = New-PSSession -Computername "Computername";
Enter-PSSession -Session $s;
setup_server.exe
# Once you are finnished
Exit-PSSession
With 'Enter-PSSession' you are not just Invoking some Command on the Server, you are directly logged-in like you probably know from SSH.
If you want your powershell session to keep running because you are running an exe, try using the -InDisconnectedSession switch. From what I understand, it will run the executable on the remote machine in a session that isn't actually connected to your computer. In essence, your computer will not destroy the session, when it disconnects, allowing the exe to continue to run.
invoke-command -computername RCOMPUTERNAME -scriptblock {start-process setup_server.exe} -InDisconnectedSession
If you need to do this on multiple computers. Setup an array of all the computer names.
Note: I don't believe this works with sessions that are already created.
In order to keep a powershell code running on the session exit it should be a process. And the windows way to keep the process is running a .exe or a windows service.
To keep a Powershell shell open after executing a command, I use the -NoExit switch, e.g. this script starts a remote interactive PS session on servername with user administrator
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoExit
-Command "Enter-PSSession -ComputerName servername -Credential administrator"
http://powershell-guru.com/powershell-tip-13-prevent-powershell-from-exiting-once-script-finished/

Resources