i understand that the subject line looks too mundane and there are posts addressing many such questions. I did not find what i was exactly looking for and hence this post.
I am invoking a powershell file from machine A to be executed on a remote machine(machine B).
//here is the code snippet:
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
string scripttext = "$secpasswd = ConvertTo-SecureString '222_bbbb' -AsPlainText –Force";
string scripttext1 = "$mycreds = New-object -typename System.Management.Automation.PSCredential('TS-TEST-09\\Administrator',$secpasswd)";
string scripttext2 = "$s = New-PSSession -ComputerName TS-TEST-09 -Credential $mycreds";
**string scripttext5 = "Invoke-Command -Session $s -FilePath 'helper.ps1' | out-null";**
pipeline.Commands.AddScript(scripttext);
pipeline.Commands.AddScript(scripttext1);
pipeline.Commands.AddScript(scripttext2);
pipeline.Commands.AddScript(scripttext5);
Collection<PSObject> results = pipeline.Invoke();
now in line
string scripttext5 = "Invoke-Command -Session $s -FilePath 'helper.ps1' | out-null";
i have to pass a few parameters(for example, username from c# environment : useralias) to this helper.ps1 file for processing. can any one guide me to a correct method of doing so?
TIA,
Manish
If the username and useralias come from the local machine then you can do so like this:
string scripttext5 = "Invoke-Command -Session $s -FilePath 'helper.ps1' -Args " +
username + "," + useralias + " | Out-Null";
This assumes that your helper.ps1 file takes at least two parameters. If so, then the values of the C# variables username and useralias will get assigned to the first two parameters.
okay so here is the solution that worked for me.
it takes its bits from Keith's solution. the trick is to expose variables from c# using
runspace.SessionStateProxy.SetVariable("PSuseralias", this.useralias);
now using $PSuseralias as
string scripttext5 = "Invoke-Command -Session $s -FilePath 'helper.ps1' -Args $PSuseralias;
this does the trick.
Related
I have a PowerShell script that closes opened shared files on remote server
$User = "user"
$PWord = ConvertTo-SecureString -String "pas" -AsPlainText -Force
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $PWord
$cim = New-CimSession -ComputerName epicorlive -Credential $Credential
Get-SmbOpenFile -CimSession $cim | Where-Object Path -like "*dll\Form.dll" | Close-SmbOpenFile -Force
Running this script from native PowerShell window works fine.
but when i try to run from C# code there is an error:
The term 'Get-SmbOpenFile' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again
string scriptText = //"\r\n" +
"$User = \"user\"\r\n" +
"$PWord = ConvertTo-SecureString -String \"pas\" -AsPlainText -Force\r\n" +
"$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $PWord\r\n" +
"$cim = New-CimSession -ComputerName epicorlive -Credential $Credential\r\n" +
"Get-SmbOpenFile -CimSession $cim | Where-Object Path -like \"*dll\\" + file + "\" | Close-SmbOpenFile -Force";
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(scriptText);
Collection<PSObject> results = pipeline.Invoke();
runspace.Close();
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
Why is this command not recognized running from C#?
How can i fix it?
If there is some other options to close opened shared files on remote computer from C# code, I will be glad to hear about them :)
I want to open powershell from C# code like this. However, i want to set a credential to powershell when it comes up.
Process myProcess = new Process();
try
{
myProcess.StartInfo.UseShellExecute = true;
myProcess.StartInfo.FileName = #"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe";
myProcess.StartInfo.CreateNoWindow = false;
myProcess.Start();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Essentially after powershell is running i want to do a Enter-PSSession to connect to remote computer but i don't want to prompt for username and password there.
I know this can be done.
PS C:\> $C = Get-Credential
PS C:\> Enter-PsSession -ComputerName "Server01" -Credential $C
I don't want to ask the user for credentials, instead i want to set it in the variable when invoking powershell. How can i achieve this ?
Current approach is two fold,
Dump the password via C#
string cmd = "\"" + mypwd+ "\"" + #" | ConvertTo-SecureString -AsPlainText -Force | convertfrom-securestring | out-file output";
PowerShellInstance.AddScript(cmd);
Collection<PSObject> PSOutput = PowerShellInstance.Invoke();
read it in powershell
$pwd = Get-Content .\output | ConvertTo-SecureString
$localcred = New-Object -typename System.Management.Automation.PSCredential -argumentlist "myusername",$pwd
Enter-PSSession -ComputerName 192.168.2.51 -Credential $localcred
Is there a neater way ?
you can use credential manager and avoid the hard coding or interactive get-credential : Accessing Windows Credential Manager from PowerShell or https://gallery.technet.microsoft.com/scriptcenter/how-to-add-credentials-to-c8e9bd5f
I am trying to run a remote PowerShell script to query and kill the tasks running in a specific session:
string hostname = "XADCG.mydomain.co.uk";
string script = "$pw = convertto-securestring -AsPlainText -Force -String password123 " + Environment.NewLine +
"$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist \"DOMAIN1\\zzkillcitrix\",$pw" + Environment.NewLine +
"$sessions = New-PSSession -ComputerName " + hostname + " -credential $cred" + Environment.NewLine +
"Invoke-Command -session $sessions -ScriptBlock {taskkill /s xa7-11.mydomain.co.uk /PID 26208 /F}" + Environment.NewLine +
"Remove-PSSession -Session $sessions" + Environment.NewLine +
"exit";
// Create and open runspace
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
// Create piepeline and feed it the script text
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(script);
Collection<PSObject> results = new Collection<PSObject>();
try
{
results = pipeline.Invoke();
var error = pipeline.Error.Read();
}
catch (Exception ex)
{
results.Add(new PSObject((object)ex.Message));
}
runspace.Close();
If I call tasklist, I get the normal, expected result in the results variable (the list of processes running on the server XADCG)
However, if I try running a query against another session (for example query session user.name /server:xa11.mydomain.co.uk), my results variable returns empty
After checking which errors are output, I have found the following:
taskkill /s xa11.mydomain.co.uk /pid 5876 /F
{ERROR: Logon failure: unknown user name or bad password.}
query session user.name /server:xa11.mydomain.co.uk
{Error 5 getting sessionnames}
tasklist /s xa7-11.mydomain.co.uk /fi \"SESSION EQ 4\"
{ERROR: Logon failure: unknown user name or bad password.}
Which obviously makes it seem that I am using the wrong username / password, but I am 100% sure that these are set correctly in the $cred variable
I can also confirm that the account zzkillcitrix does have permission to run these queries remotely (I am able to execute all of these commands by calling Process.Start with a runas for this user)
Is there somewhere else I need to set these credentials, or am I missing something else from my script?
Appreciate any help with this
I was able to resolve this with the following script:
string hostname = "SERVER.Domain.co.uk";
string script = "$pw = convertto-securestring -AsPlainText -Force -String \"PASSWORD\"" + Environment.NewLine +
"$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist \"Domain1\\zzkillcitrix\",$pw" + Environment.NewLine +
"$sessions = New-PSSession -ComputerName " + hostname + " -credential $cred" + Environment.NewLine +
"Invoke-Command -session $sessions -ScriptBlock {taskkill /s xa7-11.Domain1.co.uk /pid 23736 /U Domain1\\zzkillcitrix /P PASSWORD /F }" + Environment.NewLine +
"Remove-PSSession -Session $sessions" + Environment.NewLine +
"exit";
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(script);
Collection<PSObject> results = new Collection<PSObject>();
try
{
results = pipeline.Invoke();
var errors = pipeline.Error.Read(pipeline.Error.Count);
foreach (var error in errors)
{
var y = error;
}
}
catch (Exception ex)
{
results.Add(new PSObject((object)ex.Message));
}
runspace.Close();
So I needed to authenticate when creating New-PSSession with my $sessions variable, and then authenticate again when calling the actual command:
"Invoke-Command -session $sessions -ScriptBlock {taskkill /s xa7-11.Domain1.co.uk /pid 23736 /U Domain1\\zzkillcitrix /P PASSWORD /F }" + Environment.NewLine +
I still don't understand why I am able to run that command manually from PowerShell from that same server without providing credentials, but it makes sense in that I am remoting onto the server with $sessions and then remoting back out, into the session in question when including the authentication details in the ScriptBlock.
I am new to PowerShell. I am trying to execute PowerShell script from C#. PS Script that I have written transfer xml file from host computer (running PS script) to a remote computer. Script is as follows
$Username = "User"
$Password = "Pass"
$SecurePass = ConvertTo-SecureString -AsPlainText $Password -Force"
$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$SecurePass"
$contents = [IO.File]::ReadAllBytes("D:\MyFile.xml")
Invoke-Command -ComputerName ComputerName -Credential $Cred {[IO.File]::WriteAllBytes("D:\MyFile.xml",$using:contents)}
I store username and password of remote computer, to which I want to transfer my file, in variables. Convert password to secure string and create a credential object containing username and password. Use credential to copy the contents of file from my computer to remote computer.
This code works fine if I execute these commands one by one from my powershell or by running these lines from a .ps1 powershell script from my computer.
I used following code in C# to execute above script and transfer the file.
Runspace runSpace = RunspaceFactory.CreateRunspace();
Pipeline pipeline = runSpace.CreatePipeline();
runSpace.Open();
string script = "";
script = "$Username = \"User\"\n";
script = script + "$Password = \"Pass\"\n";
script = script + "$SecurePass = ConvertTo-SecureString -AsPlainText $Password -Force\n";
script = script + "$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$SecurePass\n";
script = script + "$contents = [IO.File]::ReadAllBytes(\"D:\\MyFile.xml\")\n";
script = script + "Invoke-Command -ComputerName ComputerName -Credential $Cred {[IO.File]::WriteAllBytes(\"D:\\MyFile.xml\",$using:contents)}\n";
// pipeline.Runspace.SessionStateProxy.SetVariable
pipeline.AddScript(script);
Collection<PSObject> results = pipeline.Invoke();
However, it did not transfer the file. So, I thought I need to Add each line in script one by one to the pipeline and then execute it. So I changed it to following.
string[] commands = script.Split('\n');
foreach (string command in commands)
{
pipeline.AddScript(command);
}
Collection<PSObject> results = pipeline.Invoke();
It did not work either. I executed single Invoke-Command from C# code on my computer and it worked fine. Also, if I try to execute other single PS commands they work. So I assessed that setting variable values like this $User = "user" is not working through C#.
I used SessionStateSetProcy.SetVariable to Store variable values in powershell session as follows.
pipeline.Runspace.SessionStateProxy.SetVariable("User", "User");
pipeline.Runspace.SessionStateProxy.SetVariable("Password", "Pass");
var value = pipeline.Runspace.SessionStateProxy.GetVariable("Password");
Console.WriteLine(value);
So, I was able to set value and retrieve as well. But, since I was using Other cmdlets like ConvertTo-SecureString, I realized I need to execute these cmdlets separately in my code.
I finally I came up with following piece of code.
pipeline.Runspace.SessionStateProxy.SetVariable("User", "User");
pipeline.Runspace.SessionStateProxy.SetVariable("Password", "Pass");
Command command = new Command("ConvertTo-SecureString");
string pass_value = pipeline.Runspace.SessionStateProxy.PSVariable.GetValue("Password").ToString();
command.Parameters.Add("AsPlainText",pass_value);
command.Parameters.Add("Force");
pipeline.Runspace.SessionStateProxy.SetVariable("SecurePass",command);
/*Build a command2*/
Command command1 = new Command("New-Object");
string user_name = pipeline.Runspace.SessionStateProxy.PSVariable.GetValue("User").ToString();
command1.Parameters.Add("TypeName", "System.Management.Automation.PSCredential");
string args = user_name + " , "+pass_value;
command1.Parameters.Add("-ArgumentList", args);
pipeline.Runspace.SessionStateProxy.SetVariable("Cred", command1);
byte[] wifiXML = System.IO.File.ReadAllBytes(#"D:\MyFile.xml");
pipeline.Runspace.SessionStateProxy.SetVariable("contents", wifiXML);
Object a = pipeline.Runspace.SessionStateProxy.PSVariable.GetValue("contents");
string script = "Invoke-Command -ComputerName ComputerName -Credential $Cred {[IO.File]::WriteAllBytes(\"D:\MyFile.xml\",$using:contents)}";
pipeline.Commands.AddScript(script);
Collection<PSObject> results = pipeline.Invoke();
It gave some weird error in System.Management.Automation. I know in SetVariable I am doing it wrong by passing command variable.
I have tried PowerShell ps = PowerShell.Create() instead of pipeline as well and tried ps.AddCommand and ps.AddParameters followed by ps.AddArgument as well. But I am not sure that once I run cmdlet ConvertTo-SecureString through ps.AddCommad, ps.AddArgument etc., how should I set it's output value to $Cred variable?
I am aware that solution to this won't be this complicated and someone must have executed multiple line powershell script through C#. All I want to accomplish is execute the .ps1 script lines mentioned in very start from C# code.I have tried bunch of methods including mentioned above to no use, so any help related to it would be much appreciated?
Try in this way
string script = #"
$Username = 'User'
$Password = 'Password'
$SecurePass = ConvertTo-SecureString -AsPlainText $Password -Force
$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$SecurePass
$contents = [IO.File]::ReadAllBytes('D:\MyFile.xml')
Invoke-Command -ComputerName ComputerName -Credential $Cred {[IO.File]::WriteAllBytes('D:\\MyFile.xml',$using:contents)}
";
pipeline.Commands.AddScript(script);
Collection<PSObject> results = pipeline.Invoke();
I'm currently trying to run a powershell script on my web service, which maps a drive to a shared folder on another system and copies a folder to it. The strange problem I currently have is that the script seems to be executed just fine, however the job in which the script is running doesn't seem to finish properly. Therefore I have to set a timeout for the task to be finished by force, otherwise it won't finish at all. However this is not really what I want, since it can have some nasty side effects if the script takes longer than expected etc. On the other hand I want to have the execution as fast as possible in the given scenario, so I'd like to let the script finished "naturally".
This is my current setup
The C# web service calls the powershell script like this:
public Collection<PSObject> executeCommand(String pCommand, String pName)
{
// Call the script
var runspaceConfiguration = RunspaceConfiguration.Create();
var runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
var pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(pCommand);
pipeline.Commands.AddScript("Wait-Job -Name " + pName + " -Timeout 60");
pipeline.Commands.AddScript("Receive-Job -Name " + pName);
return pipeline.Invoke();
}
String shareArguments = "some stuff here";
String shareCommandName = "copyFolder";
String shareCommand = "Start-Job -filepath " + currentDirectory + "\\scripts\\copyFolder.ps1 -ArgumentList " + shareArguments + " -Name " + shareCommandName + " -RunAs32";
Collection<PSObject> results1 = executeCommand(shareCommand, shareCommandName);
StreamWriter sharestream = new StreamWriter("D:\\shareoutput.txt");
foreach (PSObject obj in results1)
{
sharestream.WriteLine(obj.ToString());
}
sharestream.Close();
The script itself:
param($sharepath,$shareuser,$sharepassword,$hostname,$sourcefolder)
# create a credentials object
$secpasswd = ConvertTo-SecureString $sharepassword -AsPlainText -Force
Write-Output "0"
$cred = New-Object System.Management.Automation.PSCredential ($shareuser, $secpasswd)
Write-Output "1"
# Access the share
New-PSDrive -Name J -PSProvider FileSystem -Root $sharepath -Credential $cred
Write-Output "2"
# Copy the folder including the file
Copy-Item $sourcefolder "J:\" -Recurse -Force
Write-Output "3"
# Unmap drive
Remove-PSDrive -Name J
When I retrieve the debug output of the job, the output looks like this. So it seems that the New-PSDrive call seems to block here somehow:
0
1
Any idea what is the cause for this and how I can fix it?
Thanks in advance for any hint
You have problem with New-PSDrive. First Check your PSCredential object.
then update the script with
New-PSDrive -Name J -PSProvider FileSystem -Root $sharepath -Credential $cred -Persist
Perfect article:
https://technet.microsoft.com/en-us/library/hh849829.aspx