I want to invoke a command on a remote server, I do not want to have to put in the password to run the script. I've tried encrypting the password and storing it in a txt file.
$username = "Mydomain\service.account"
$password = cat C:\username-password-encrypted.txt | convertto-securestring
$cred = new-object -typename System.Management.Automation.PSCredential - argumentlist $username, $password
Invoke-command $cred -Computer myserver -scriptblock {param([string]$LocalUser); Add-PSSnapin Citrix* ; Get-BrokerSession -max 10000 | Where-Object brokeringusername -eq "mydomain\$($LocalUser)" | Stop-BrokerSession} -ArgumentList $user
Here is the error I get
Invoke-Command : A positional parameter cannot be found that accepts argument 'System.Management.Automation.PSCredential'.
At \\uncpath\citrix\Installation Media\Citrix\Ticketing_script\Ticketing_Script - Copy (3).ps1:70 char:1
+ Invoke-command $cred -Computer MyServer -scriptblock {param([s ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Invoke-Command], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.InvokeCommandCommand
There has to be an easier way to run this command on myserver without having to put in the password every time.
You just have to specify the -Credential parameter:
Invoke-command -Credential $cred -Computer myserver -scriptblock {param([string]$LocalUser); Add-PSSnapin Citrix* ; Get-BrokerSession -max 10000 | Where-Object brokeringusername -eq "mydomain\$($LocalUser)" | Stop-BrokerSession} -ArgumentList $user
4 years later but here goes:
Frode F. had the right idea in the comments. Your third line has - argumentlist but it must be -ArgumentList, otherwise it will throw New-Object : A positional parameter cannot be found that accepts argument 'ArgumentList'.
Also, when I copy and run your exact code it does not throw the same error which leads me to believe that the code you're running is not the same as the one you've written here. Here are the different errors you've received and the reasons for them:
Incorrect syntax
A positional parameter cannot be found that accepts argument 'System.Management.Automation.PSCredential'.
This means that you've not specified the flag correctly, Powershell thinks you're using a flag or parameter called System.Management.Automation.PSCredential. You can simulate this by adding a hyphen like New-Object -TypeName - System.Management.Automation.PSCredential and see for yourself, so your syntax is wrong somewhere.
Incorrect password format
Cannot find an overload for "PSCredential" and the argument count: "2".
This means that the $Password is not in the correct format. You're importing a file called password-encrypted.txt but after this you pass it to ConvertTo-SecureString. Are you sure that the information in the password-encrypted.txt file is passed to both ConvertTo-SecureString and then to ConvertFrom-SecureString before you saved it, like so?
"MyPassword" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Out-File "C:\username-password-encrypted.txt"
For more information on how to deal with passwords in Powershell, see this post that goes into detail on how to do credentials in Powershell:
https://www.pdq.com/blog/secure-password-with-powershell-encrypting-credentials-part-1/
In conclusion
If you run the code you've provided here using both -ArgumentList (instead of - argumentlist) and -Credentials $creds like others have suggested it should run fine. You're most likely not running the code that you've provided here because with these two adjustments the code runs.
Related
I was trying to create a simple script that would go to a server and get the acl details of a folder. I tested the command:
Invoke-command -Computername Servername -ScriptBlock {(Get-Acl "\\Server\Folder\user folders").access | ft- auto}
This worked ok. However when I was trying to put it into a script that would allow me to enter the path in via a variable I always get:
Cannot validate argument on parameter 'Path'. The argument is null or empty. Supply an argument that is not null or empty and then
try the command again.
+ CategoryInfo : InvalidData: (:) [Get-Acl], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.GetAclCommand
Here is my script:
#get folder permissions from remote computer
$serverName = read-host "Please enter the name of the target server"
$folderPath = "\\server_name\Folder\user folders"
#read-host "Please enter the full path to the target folder"
Invoke-command -ComputerName $serverName -ScriptBlock {(get-acl $folderPath).access | ft -wrap}
Its probably something very simple, but I'd appreciate the help.
The issue is because you are trying to use the $folderPath variable, but on the remote computer that variable does not exist.
You will need to pass it through as an argument. There are multiple ways to do so, two such ways are below:
# Add desired variable to ArgumentList and define it as a parameter
Invoke-command -ComputerName $serverName -ArgumentList $folderPath -ScriptBlock {
param($folderPath)
(get-acl $folderPath).access | ft -wrap
}
OR
# In PS ver >= 3.0 we can use 'using'
Invoke-command -ComputerName $serverName $folderPath -ScriptBlock {(get-acl $using:folderPath).access | ft -wrap}
I am trying to follow this article to expand a variable in a scriptblock
My code tries this:
$exe = "setup.exe"
invoke-command -ComputerName $j -Credential $credentials -ScriptBlock {cmd /c 'C:\share\[scriptblock]::Create($exe)'}
How to fix the error:
The filename, directory name, or volume label syntax is incorrect.
+ CategoryInfo : NotSpecified: (The filename, d...x is incorrect.:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
+ PSComputerName : remote_computer
You definitely don't need to create a new script block for this scenario, see Bruce's comment at the bottom of the linked article for some good reasons why you shouldn't.
Bruce mentions passing parameters to a script block and that works well in this scenario:
$exe = 'setup.exe'
invoke-command -ComputerName $j -Credential $credentials -ScriptBlock { param($exe) & "C:\share\$exe" } -ArgumentList $exe
In PowerShell V3, there is an even easier way to pass parameters via Invoke-Command:
$exe = 'setup.exe'
invoke-command -ComputerName $j -Credential $credentials -ScriptBlock { & "C:\share\$using:exe" }
Note that PowerShell runs exe files just fine, there's usually no reason to run cmd first.
To follow the article, you want to make sure to leverage PowerShell's ability to expand variables in a string and then use [ScriptBlock]::Create() which takes a string to create a new ScriptBlock. What you are currently attempting is to generate a ScriptBlock within a ScriptBlock, which isn't going to work. It should look a little more like this:
$exe = 'setup.exe'
# The below line should expand the variable as needed
[String]$cmd = "cmd /c 'C:\share\$exe'"
# The below line creates the script block to pass in Invoke-Command
[ScriptBlock]$sb = [ScriptBlock]::Create($cmd)
Invoke-Command -ComputerName $j -Credential $credentials -ScriptBlock $sb
I'm trying to get the Invoke-Command cmdlet working with a CredSsp session by passing a locally defined function.
I have two local functions:
MyCopy which just invokes Copy-Item cmdlet on passed params with additional switches
MyCopyReturn which is the same as above, but in addition has return statement at the end
Now when I pass those functions to Invoke-Command without -Session parameter, they both succeed.
However, when specifying -Session for Invoke-Command, MyCopy fails with following exception (2. case):
Cannot bind argument to parameter 'Path' because it is null.
+ CategoryInfo : InvalidData: (:) [Copy-Item], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.CopyItemCommand
+ PSComputerName : localhost
Could someone please explain why result of passing MyCopyReturn is different from passing MyCopy?
function MyCopy ($from, $to) {
Copy-Item $from $to -Force -Recurse -Verbose
}
function MyCopyReturn ($from, $to) {
Copy-Item $from $to -Force -Recurse -Verbose
return
}
$credential = Get-Credential
$computerName = 'localhost'
$session = New-PSSession -ComputerName $computerName -Credential $credential -Authentication Credssp
# src is a file
$src = "c:\sandbox\a\test"
# dest is a directory
$dest = "c:\sandbox\b"
# 1.
# MyCopy works fine without giving session
Invoke-Command -ScriptBlock ${function:MyCopy} -ArgumentList $src,$dest
# 2.
# MyCopy DOESN'T WORK when session is given
Invoke-Command -Session $session -ScriptBlock ${function:MyCopy} -ArgumentList $src,$dest
# 4.
# MyCopyReturn works fine without the session
Invoke-Command -ScriptBlock ${function:MyCopyReturn} -ArgumentList $src,$dest
# 3.
# MyCopyReturn works fine with session too
Invoke-Command -Session $session -ScriptBlock ${function:MyCopyReturn} -ArgumentList $src,$dest
Remove-PSSession $session
I make some digging with ILSpy and can say, that the problem in the bug in ScriptBlock.GetPowerShell method. It does not bind parameters properly, if them come not from param block:
function f($Arg){write "`$Arg=`"$Arg`";`$Args=`"$Args`""}
function g{param($Arg) write "`$Arg=`"$Arg`";`$Args=`"$Args`""}
$Function:f.GetPowerShell('Test1','Test2').Invoke()
# $Arg="";$Args="Test1 Test2"
$Function:g.GetPowerShell('Test1','Test2').Invoke()
# $Arg="Test1";$Args="Test2"
As you can see, $Function:f.GetPowerShell('Test1','Test2') bind both arguments to $Args automatic variable and nothing get bound to $Arg. So when you invoke this command
Invoke-Command -Session $session -ScriptBlock ${function:MyCopy} -ArgumentList $src,$dest
$from and $to does not get bound properly and cause error.
Why error does not happens when you not use -Session parameter?
Looks like Invoke-Command have to convert ScriptBlock to PowerShell to invoke command in remote session and that does not required when ScriptBlock invoked locally.
Why MyCopyReturn does not cause same error?
Not all ScripBlocks can be converted to PowerShell by GetPowerShell method (in particular, it is required that ScriptBlock must have a single statement, having another statement return violate this). If that is the case (GetPowerShell throws ScriptBlockToPowerShellNotSupportedException), than Invoke-Command use some backup scenario to convert ScriptBlock to PowerShell and that scenario does not suffer from bug of GetPowerShell method.
I have a strange problem with one of my servers :
I am trying to open a PSsession with it.
If I copy my script directly in powershell everything works fine, but if i run it via a .ps1 file I get a access denied error.
The same sript works on multiple machines except this one.
Additonal information:
Executing Server : Server 2012
Target Server2003SP2
Another Server2003SP2 is working fine without a Problem
the Client Server was configured using :
Enable-PSRemoting -Force
Set-Item wsman:\localhost\client\trustedhosts MY2012Server -concatenate -force
Restart-Service WinRM
And the Error Message:
New-PSSession : [Server2003SP2] Connecting to remote server Server2003SP2 failed with the following error message : Access is denied. For more information,
Help topic.
At C:\Users\Administrator\Desktop\Script.ps1:23 char:13
+ $Session = New-PSSession -ComputerName $Servername -credential $Cred #-sessionO ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (System.Manageme....RemoteRunspace:RemoteRunspace) [New-PSSession], PSRemotingTransportException
+ FullyQualifiedErrorId : AccessDenied,PSSessionOpenFailed
Edit : My full SCript as requested :
$Password = "Hereismypasswordwith#and€init"
$Username = "Servername\Administrator"
$Servername = "Servername"
$Language = {
$oscode = Get-WmiObject Win32_OperatingSystem -ErrorAction continue
$oscode = $oscode.oslanguage
$switch = switch ($oscode){
1031 {"Deutsch"};
1033 {"English"};
default {"English"};
}
write-host $switch
return $switch
}
$SecurePassWord = ConvertTo-SecureString -AsPlainText $Password -Force
$Cred = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $Username, $SecurePassWord
$pssessionoption = new-pssessionoption -operationtimeout 7200000 -IdleTimeout 7200000
$Session = New-PSSession -ComputerName $Servername -credential $Cred -sessionOption $pssessionoption
Invoke-Command -Session $Session -Scriptblock $Language
Remove-PSSession -Session $Session
UPDATE :
it seems to be something within the Char encoding.
the password in the ps1 file produces a difrent output for the € in it :
in the ps1. ¬
in the ps window : ?
if i pass the Password as a Paramter it also works.
$password.gethash() also prouces difrent outputs. codepage is the same though (chcp)
the script was created in notepad++
Changing / Converting to ansi from UTC without BOM fixed the issue.. jesus crist who thinks about stuff like that / why the hell was it set to this value..
I am trying to install a service on a remote machine using the powershell.
So far I have the following:
Invoke-Command -ComputerName $remoteComputerName -ScriptBlock {
param($password=$password,$username=$username)
$secpasswd = ConvertTo-SecureString $password -AsPlainText -Force
$credentials = New-Object System.Management.Automation.PSCredential ($username, $secpasswd)
New-Service -Name "XXX" -BinaryPathName "c:\XXX.exe" -DisplayName "XXX XXX XXX" -Description "XXXXXX." -Credential $credentials -ErrorVariable errortext
Write-Host("Error in: " + $errortext)
} -ArgumentList $password,$username -ErrorVariable errortext
Write-Host("Error out: " + $errortext)
When there is an error while executing New-Service the $errortext ErrorVariable get set properly inside the ScriptBlock, because the text: "Error in: shows me the error.
The ErrorVariable of the Invoke-Command does not get set (which I expected).
My question is:
Is it somehow possible to set the ErrorVariable of the Invoke-Command to the error I got inside the ScriptBlock?
I know I could also use InstalUtil, WMI and SC to install the service, but this is not relevant at the moment.
No, you can't get the Errorvariable from the Invoke-Command call to be set the same as in the scriptblock.
But if your goal is "detect and handle errors in the scriptblock, and also get errors returned back to the context of the Invoke-Command caller" then just do it manually:
$results = Invoke-Command -ComputerName server.contoso.com -ScriptBlock {
try
{
New-Service -ErrorAction 1
}
catch
{
<log to file, do cleanup, etc>
return $_
}
<do stuff that should only execute when there are no failures>
}
$results now contains the error information.
The Invoke-Command argument list is a one way deal. You can either output the error variable in the script e.g. on the last line of the scriptblock put:
$errortext
or better yet, just don't capture the error via the -ErrorVariable at all. The scriptblock output, including errors, will flow back to the caller even over a remote connection.
C:\> Invoke-Command -cn localhost { Get-Process xyzzy } -ErrorVariable errmsg 2>$null
C:\> $errmsg
Cannot find a process with the name "xyzzy". Verify the process name and call the cmdlet again.
+ CategoryInfo : ObjectNotFound: (xyzzy:String) [Get-Process], ProcessCommandException
+ FullyQualifiedErrorId : NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.GetProcessCommand
+ PSComputerName : localhost
In general, I think it is much better to keep errors on the error stream, separated from the normal output.
This is almost certainly not the "correct" answer, but this is what I use when I want Invoke-Command to throw an error in the script.
$error.Clear()
Invoke-Command -ComputerName localhost -ScriptBlock {Command-ThatFails}
if ($error.Count -gt 0) { throw $error[0] }
If you wanted to keep the error in a variable, you could do the following:
$error.Clear()
Invoke-Command -ComputerName localhost -ScriptBlock {Command-ThatFails}
if ($error.Count -gt 0) { $myErrorVariable = $error[0] }
In the strictest sense, I believe the answer is no, you cannot set Invoke-Command's ErrorVariable to the contents of the ErrorVariable inside the script block. The ErrorVariable is only for the command it's attached to.
However, you can pass the variable in the script block out to Invoke-Command's scope. In your code you run your New-Service command with -ErrorVariable errortext. Instead, create your variable in the 'script' scope by prefacing the variable name with "script:", like this: -ErrorVariable script:errortext. That makes the variable available outside of the script block as well as inside.
Now your final line Write-Host("Error out: " + $errortext) will output the error that was generated inside of the script block.
More information here and here.