Accessing cmd remotely using PowerShell - powershell

I need to run a couple of bcdedit commands on a remote computer's cmd using a PowerShell script that runs on my computer. I am able to create a PSSession but I'm not sure how I can run cmd on the remote computer. When I run the code in the 'Invoke-Command' line, I get an error Connection to remote server failed with the following error message: Access is denied. When I just run Invoke-Command, I am prompted to enter the ScriptBlock, but when I do, I get yet another error: "Cannot bind parameter 'ScriptBlock' Cannot convert the "cmd /c 'bcdedit /copy {current} /d "Description"'} value of type System.String to type System.Management.Automation.ScriptBlock
I have never worked with PowerShell before. I need to do this in a couple of hours, and I am absolutely clueless right now.
Enable-PSRemoting -Force
Set-Item WSMan:\localhost\Client\TrustedHosts $ip -Concatenate -Force
$session = New-PSSession -ComputerName $ip -Credential $cred -ConfigurationName $config -UseSSL -SessionOption $sessopt
#problematic code
Invoke-Command -ComputerName $ip -ScriptBlock {cmd /c 'bcdedit /copy {current} /d "Description"'}
#works fine
Restart-Computer -ComputerName $ip -Force
ping.exe -t $ipaddr | Foreach{"{0}-{1}" -f (Get-Date -f "yyyy/MM/dd HH:mm:ss"), $_}
Assume that $ip, $ipaddr, $config, $sessopt and $cred store valid parameters.

You can run bcedit.exe directly in PowerShell, but because in PowerShell { and } are metacharacters, you need to quote identifiers such as {current}:
bcdedit /copy '{current}' /d 'Description'
See this answer for a discussion and list of PowerShell's metacharacters.
If you get an error on connecting to a remote computer, the implication is that your user account either doesn't have sufficient privileges to connect remotely or the target computer isn't set up for PowerShell remoting.
Note that Enable-PSRemoting -Force must be run on the target (server) machine, not on the calling (client) machine.
See the conceptual about_Remote_Troubleshooting topic.
The Restart-Computer cmdlet's -ComputerName parameter does not use PowerShell remoting, so the fact that it succeeds does not imply that PowerShell remoting, such as via Invoke-Command, works.
When I just run Invoke -Command, I am prompted to enter the ScriptBlock
PowerShell's automatic prompting feature for mandatory parameter values that weren't specified on the command line has severe limitations, and not being able to prompt for a script-block parameter value is one of them - see GitHub issue #4068; however, this additional problem is incidental to your real problem.

Thanks for all the suggestions, I was able to fix the error by adding -Credential to the Invoke-Command and Restart-Computer commands:
#problematic code
Invoke-Command -ComputerName $ip -Credential $cred -ScriptBlock {cmd /c 'bcdedit /copy {current} /d "Description"'}
Restart-Computer -ComputerName $ip -Credential $cred -Force

Related

Power shell Invoke remote script is not working

I am trying to invoke a remote bat file from my local machine that should start and run the bat in a remote machine terminal. My script is able to connect and show me the output. However, it is invoking the remote bat file but waiting on my screen with bat file output. and my idea is the bat file should invoke and running in the remote machine rather than showing the output on my terminal local terminal. What should be the way here?
loop{
Invoke-Command -ComputerName $computer -credential $cred -ErrorAction Stop -ScriptBlock { Invoke-Expression -Command:"cmd.exe /c 'C:\apacheserver.bat'" }
}
From what I understand you want the .bat file to not show you the result. If so, you should do it with the Out-Null or by redirecting STDOUT and STDERR to NULL.
IE: Invoke-Expression -Command:"cmd.exe /c 'C:\apacheserver.bat'" | Out-Null
If I'm understanding correctly, you want to suppress the output from the .bat file inside your local console? If so, redirecting to some form of $null is the way to go. You also shouldn't need Invoke-Expression as Invoke-Command can run the file directly:
Invoke-Command -ComputerName $computer -Credential $cred -ErrorAction Stop -Scriptblock {
cmd.exe /c 'C:\apacheserver.bat' | Out-Null
}
You could also use > $null instead of Out-Null if you prefer.
If you want to redirect the output of the Invoke-Command call to the remote console instead, that kind of defeats the purpose of the cmdlet. You could try redirecting the console output to a file on the remote computer if you want a local record:
$remoteFile = '\\server\C$\Path\To\Output.txt'
Invoke-Command -ComputerName $computer -Credential $cred -ErrorAction Stop -Scriptblock {
cmd.exe /c 'C:\apacheserver.bat' > $remoteFile
}
If I understand you, you want to run the script in the background and not wait for it:
Invoke-Command $computer -Credential $cred { start-process C:\apacheserver.bat }

Executing CMD or EXE file using PSSession (remote powershell) caused Error 1603 access denied

I have the following script powershell command but it returns access denied. I assume the Error 1603 is caused by remote accessing the server. However, the $username has admin rights in the computer01 server.
To recheck if my hunch was right, I tried to test with the following and I got access denied:
Start-Process cmd -Credential $Cred
Update
The error was due to the $Cred . Removing the -Credential argument works fine.
End of Update
The commands have no problems executing directly in the computer01 machine using the cmd.exe.
I want to use cmd /c in this case as I need to get the real exit code from the SETUP.EXE installer.
See full script below:
$script = {
#Param(
# [String]$username,
# [String]$password
#)
# $Cred = New-Object System.Management.Automation.PSCredential ($username, $password)
$respfile = "$env:TEMP\test.resp"
echo 'key=value' > $respfile
$username = "$env:USERDOMAIN\$env:USERNAME"
Write-Host Hello $username
$Creds = (Get-Credential -Credential "$env:USERDOMAIN\$env:USERNAME" )
Start-Process cmd -Credential $Creds
#This command cannot be run due to the error: Access is denied.
# + CategoryInfo : InvalidOperation: (:) [Start-Process], #InvalidOperationException
# + FullyQualifiedErrorId : #InvalidOperationException,Microsoft.PowerShell.Commands.StartProcessCommand
# + PSComputerName : computer01
# cmd /c "$path\SETUP.EXE /INSTALL -s /RESPFILE:'$respfile'"
runas /user:$Username "SETUP.EXE" /INSTALL -s /RESPFILE:"$respfile"
echo $LASTEXITCODE
# Error 1603
}
#$username = 'domain/user'
#$password = 'password'
$server = 'computer01'
$Creds = New-Object System.Management.Automation.PSCredential
$session = New-PSSession -ComputerName $server
#Invoke-Command -Session $session -Scriptblock $script -Argumentlist $username, $password
Invoke-Command -Session $session -Scriptblock $script -Credential $Creds #updated based on #postanote advise
Remove-PSSession -ComputerName $server
I have found the following similar link install-remotely but do not want to use the ENTER-PSSession command. I do not want to exit the current PSSession and remotely join again in server just to install then exit.
Any suggestions how to use only PSSession and successfully executing installers in the remote server?
As one mentioned in the comments, you don't need cmd.exe. You can use the call/invocation operator - & - to specify that the next token on the line is a command:
& "$path\SETUP.EXE" /INSTALL -s /RESPFILE:$respfile
Of course, for this to work, the parameters to SETUP.EXE need to be correct (I don't know whether that's the case or not).
Never pass plain text passwords in scripts. It exposes you to uneeded risks.
Use proper secured credentials models.
• Working with Passwords, Secure Strings and Credentials in Windows PowerShell
• quickly-and-securely-storing-your-credentials-powershell
PowerShell remoting requires the use of an implicit (New-PSSession) or explicit (Enter-PSSession) session.
• About Remote Requirements
There are only a handful of cmdlets you can use as non-Admin ir run without PSRemoting enabled.
• Tip: Work Remotely with Windows PowerShell without using Remoting or WinRM
As noted in the Powershell Help file | MS Docs link above, with PSRemoting, you must be using an account that is an admin on the remote host.
In Windows OS proper, to install software, you must be an admin and running that in an admin session.
PowerShell runs in the context of the user who started it.
If you are trying to run in another user context, that is a Windows Security boundary, and you cannot do that without PowerShell natively, you'd need other tools like MS Sysinternals PSExec. See also:
Find-Module -Name '*Invoke*' | Format-Table -AutoSize
# Results
<#
Version Name Repository Description
------- ---- ---------- -----------
...
3.1.6 Invoke-CommandAs PSGallery Invoke Command as System/User on Local/Remote computer using ScheduleTask.
...
#>
Try this refactored option...
$script = {
$Creds = (Get-Credential -Credential "$env:USERDOMAIN\$env:USERNAME" )
$respfile = 'whatever this is'
& "SETUP.EXE /INSTALL -s /RESPFILE:'$respfile'"
Write-Output $LASTEXITCODE
}
$server = 'computer01'
$session = New-PSSession -ComputerName $server
Invoke-Command -Session $session -Scriptblock $script -Credential $Creds
Remove-PSSession -ComputerName $server
Details
# Get specifics for a module, cmdlet, or function
(Get-Command -Name Invoke-Command).Parameters
(Get-Command -Name Invoke-Command).Parameters.Keys
Get-help -Name Invoke-Command -Examples
# Results
<#
Invoke-Command -ComputerName server01 -Credential domain01\user01 -ScriptBlock {Get-Culture}
$s = New-PSSession -ComputerName Server02 -Credential Domain01\User01
$LiveCred = Get-Credential
Invoke-Command -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.exchangelabs.com/PowerShell -Credential $LiveCred -Authentication Basic
Invoke-Command -Session $s -ScriptBlock { Get-HotFix } -SessionOption $so -Credential server01\user01
Enable-WSManCredSSP -Delegate Server02
Set-Item WSMan:\Server02*\Service\Auth\CredSSP -Value $True
Invoke-Command -Session $s -ScriptBlock {Get-Item \\Net03\Scripts\LogFiles.ps1} -Authentication CredSSP -Credential Domain01\Admin01
#>
Get-help -Name Invoke-Command -Full
Get-help -Name Invoke-Command -Online
So, I was able to solve my problem.
1603 is the error thrown by the setup.exe.
Just to be sure, I manually executed first the following directly in the server using CMD and it was working!
$path\SETUP.EXE /INSTALL -s /RESPFILE:'$respfile'
I did a lot of testings. Researched and as mentioned from comments above, I did different ways to execute programs using powershell. I even used ACL to change ownership of installer directory/ files, switching to different user accounts (with different priviledges) but still getting access denied (including the Admin account).
It took days before I realized the difference in output file size of manual run in machine and the remote. The cause was the $respfile. It really is worth checking every possible reason/ scenario why there's access denied. Plus I cannot extract the setup.exe and its contents to troubleshoot.
The $respfile was created via powershell. I noticed the size created by powershell is doubled compared to a CMD size that was needed. With that, I assumed that the setup.exe reads file in UTF-8 format. I only know that it's working when triggered via CMD and not via powershell.
I suddenly bumped on this links differrent Powershell and CMD sizes and convert file content to CMD readable file - utf8. After converting the $respfile to UTF-8 format, I was able to run the exe successfully.
Hopefully, this can help others too!

Running a remote cmd command in PowerShell

I uploaded some files to a remote host with PowerShell, by FTP. On this host runs Windows 7 Embedded.
It turns out there is EWF (Enhanced Write Filter). So after a restart the uploaded files were gone. For saving the changes it needs commit them in cmd (at the remote host) by: ewfmgr d:-commit How can I include this command in my PowerShell code?
The code:
Enable-PSRemoting -Force
Set-Item wsman:\localhost\client\trustedhosts -Value * -Force
Restart-Service WinRm
Test-WSMan $line
Invoke-Command -ComputerName $line -scriptblock {cmd.exe /c "ewfmgr d: -commit"} -credential $FTPCredential
When I run Enable-PSRemoting -Force manually on the remote computer, it works, but it is uncomfortable and take lots of time. Is there another way to do this once for many hosts simultaneously?
Example-Code:
$session = New-PSSession -ComputerName yourRemoteComputer
Invoke-Command -Session $session -Scriptblock {ewfmgr d: -commit}
Remove-PSSession -Session $session
You have to enable Powershell Remoting on your host to invoke a command like this (https://technet.microsoft.com/en-us/library/ff700227.aspx)
If you need to transmit Credentials to your remote host, you can add the -Credential-Parameter to New-PSSession. This article describes how to add valid Credentials to your script (https://technet.microsoft.com/en-us/library/ff700227.aspx)
Greetings, Ronny

Executing batch file in the remote machines using powershell as a background job

All I am trying to do is to execute a batch file in remote machines as a job.I have batch file located in all machines inside C:\Users\temp folder.Here is my code
$batFile = "test.bat"
foreach($m in $machine)
{
Invoke-Command -ComputerName $m -ScriptBlock{param($batFile) & cmd.exe /c "C:\Users\temp\$batFile"} -Authentication negotiate -Credential $cred -ArgumentList $batFile -AsJob
}
But I keep getting
The expression after '&' in a pipeline element produced an object that was not valid. It must result in a command name, a script
block, or a CommandInfo object
I tried using $using:batFile inside ScriptBlock as well with no success. Can anyone suggest me what I might be doing wrong? I am using powershell version 4.
do a trace on that,
Invoke-Command -ComputerName $m -ScriptBlock{param($batFile) Trace-Command NativeCommandParameterBinder -Expression { & cmd.exe /c "C:\Users\temp\$batFile"}} -Authentication negotiate -Credential $cred -ArgumentList $batFile -AsJob
and Try using Invoke-Expression as a workaround instead of &
invoke-Expression "cmd.exe /c 'C:\Users\temp\$batFile'"
Regards,
Kvprasoon

PowerShell Invoke-Command on an advanced function

I have an advanced function Copy-FilesHC that is available in a module file. This function copies some files from the Source to the Destination folder and generates some output for in a log file.
The function works fine locally:
Copy-FilesHC -Source $Src -Destination $Des *>> $Log
It also works on a remote machine:
# For remote use we need to make it available first
Import-Module (Get-Command Copy-FilesHC).ModuleName
Invoke-Command -Credential $Cred -ComputerName $Host -ScriptBlock ${Function:Copy-FilesHC} -ArgumentList $LocalSrc, $LocalDes
However, I can't seem to figure out how I can have it pass the output to a log file like in the first command. When I try the following, it fails:
Invoke-Command -Credential $Cred -ComputerName $Host -ScriptBlock ${Function:Copy-FilesHC *>> $Log} -ArgumentList $LocalSrc, $LocalDes
Invoke-Command : Cannot validate argument on parameter 'ScriptBlock'. The argument is null. Provide a vali
d value for the argument, and then try running the command again.
As indicated here I thought the $ sign for the ScriptBlock was incorrect. But this way I don't need to put my advanced function in a ScriptBlock to copy it over as it now happens automatically while it's only available within the module. So I just need to find out how to capture the output in the log file.
Thank you for your help.
Found the solution just a few minutes ago:
# For remote use we need to make it available first
Import-Module (Get-Command Copy-FilesHC).ModuleName
Invoke-Command -Credential $Cred -ComputerName $Host -ScriptBlock ${Function:Copy-FilesHC} -ArgumentList $LocalSrc, $LocalDes *>> $Log