I don't know what is wrong with below code.
Initially it works fine for few times. Then gradually the target server against which it is running, becomes unresponsive (I can't even login to that server unless I reboot it). When I try debugging the code the execution pointer doesn't return from below line, it disappears after entering below line:
Result = ps.Invoke();
Here is the complete code:
/// <summary>
/// Run PowerShell Script code provided as input
/// </summary>
/// <param name="ScriptCode">
/// Multiline script code provided as text input
/// </param>
/// <returns>
/// Collection of PSObject
/// </returns>
public Collection<PSObject> ExecutePowerShellScript(string ScriptCode)
{
Collection<PSObject> Result;
try
{
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
using (Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration))
{
runspace.Open();
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.Commands.AddScript(ScriptCode);
Result = ps.Invoke();
runspace.Close();
}
}
}
catch (Exception ex)
{
string Error = ex.Message.ToLower().Contains("inner exception") ? ex.InnerException?.Message : ex.Message;
PSObject errorObject = new PSObject();
errorObject.Properties.Add(new PSNoteProperty("Error", Error));
Result = new Collection<PSObject> { errorObject };
}
return Result;
}
I have to restart the target server after every few days since it becomes unresponsive. I suspect something is wrong with above code. Can someone please suggest?
EDIT: Script code which is passed to this function
ScriptCode = $#"
$Results, $ScheduledTask, $ScheduledTaskInfo = $null
$TaskName = ""{TaskName}""
Try
{{
$Credentials = [pscredential]::new('domain\{SCORCHUName}', (ConvertTo-SecureString -String '{ConfigurationManager.AppSettings.Get("SCORCHPwd")}' -AsPlainText -Force -ErrorAction Stop))
$CIMSession = New-CimSession -Name TaskStatusCheck -ComputerName {ChangeMonitoringService_ServerName} -Credential $Credentials -ErrorAction Stop
$ScheduledTask = Get-ScheduledTask -CimSession $CIMSession -TaskName $TaskName -ErrorAction Stop
$ScheduledTaskInfo = Get-ScheduledTask -CimSession $CIMSession -TaskName $TaskName -ErrorAction Stop | Get-ScheduledTaskInfo
if($ScheduledTask -ne $null -and $ScheduledTaskInfo -ne $null)
{{
$Results = [PSCustomObject][Ordered]#{{
TriggerEnabled = $ScheduledTask.Triggers[0].Enabled
JobName = $ScheduledTask.TaskName
JobState = $ScheduledTask.State
LastJobResult = $ScheduledTaskInfo.LastTaskResult
LastRunTime = $ScheduledTaskInfo.LastRunTime
NextRunTime = $ScheduledTaskInfo.NextRunTime
}}
}}
}}
Catch
{{
$Results = [PSCustomObject][Ordered]#{{
TriggerEnabled = $false
JobName = ""Error: $($Error[0].Exception.Message)""
JobState = ""Error""
LastJobResult = 0
LastRunTime = [Datetime]::MinValue
NextRunTime = [Datetime]::MinValue
}}
}}
Finally
{{
Try
{{
(Get-CimSession -Name TaskStatusCheck -ErrorAction Stop).Close()
(Get-CimSession -Name TaskStatusCheck -ErrorAction Stop).Dispose()
}}
Catch {{}}
$Results
}}
";
Related
I have a need to run PowerShell scripts from C# application. I like using method AddScript for this. And it works quite nice. However, it does not seem to work as expected when I added parameters to script with method AddParameters.
Here is test payload (PowerShell):
param ([string]$Arg1, [string]$Arg2, [switch]$ArgParamless)
$filename = "payload_with_params.txt"
$filepath = $env:temp
$fullpath = Join-Path -Path $filepath -ChildPath $filename
$dt = Get-Date -Format "yyyy.MM.dd HH:mm:ss"
$val = $dt+' '+$Arg1+' '+$Arg2+' '+$ArgParamless
Add-Content -Path $fullpath -Value "$val"
It works just fine if I push it from PS like this:
.\payload_with_params.ps1 -Arg1 "Bla 1" -Arg2 "Bla 2" -ArgParamless
Result:
2023.01.19 16:58:10 Bla 1 Bla 2 True
The C# code (oversimplified):
string command = File.ReadAllText(pathToPS1);
List<CommandParameter> paramList = new List<CommandParameter>();
paramList.Add(new CommandParameter("Arg1", "Bla 1"));
paramList.Add(new CommandParameter("Arg2", "Bla 2"));
paramList.Add(new CommandParameter("ArgParamless"));
using (PowerShell ps = PowerShell.Create())
{
//Adding script file content to object
ps.AddScript(command);
if (paramList != null)
{
if (paramList.Count > 0)
{
//Adding Params to object;
ps.AddParameters(paramList);
}
}
//Launching
ps.Invoke();
}
And the result:
2023.01.19 16:54:00 System.Management.Automation.Runspaces.CommandParameter System.Management.Automation.Runspaces.CommandParameter False
So.. it's not working as I expected. How should I supply parameters to script?
For [switch] parameters, you'll want to bind a bool - PowerShell will interpret true as "Present" and false as "Absent":
paramList.Add(new CommandParameter("ArgParamless", true));
Well, it turns out AddParameter(CommandParameter) approach doesn't work.
This approach is working:
string command = File.ReadAllText(pathToPS1);
using (PowerShell ps = PowerShell.Create())
{
//Adding script file content to object
ps.AddScript(command);
if (paramList != null)
{
if (paramList.Count > 0)
{
//Adding Params to object;
ps.AddArgument("-Arg1 XXXXXX");
}
}
//Launching
ps.Invoke();
}
On PowerShell end this arguments can be extracted for usage from $args array:
$arglist = $args -join " "
Even better approach using hashtable:
string command = File.ReadAllText(pathToPS1);
using (PowerShell ps = PowerShell.Create())
{
//Adding script file content to object
ps.AddScript(command);
if (paramList != null)
{
if (paramList.Count > 0)
{
//Adding Params to object;
var hashtable = new Hashtable {
{ "Arg1", "XXXXXX" },
{ "Arg2", "YYYYYY" },
{ "ArgParamless", true }
};
ps.AddArgument(hashtable);
}
}
//Launching
ps.Invoke();
}
And that's what you do on PS side to use it:
function Wrapper-Test ([string]$Arg1, [string]$Arg2, [switch]$ArgParamless) {
$Result = $Args1+' '+$Args2+' '+$ArgParamless
return $Result
}
$MyArgs = "";
$MyArg = $args[0]
$MyArgs = Wrapper-Test #MyArg;
$filename = "payload_with_params.txt"
$filepath = $env:temp
$fullpath = Join-Path -Path $filepath -ChildPath $filename
$dt = Get-Date -Format "yyyy.MM.dd HH:mm:ss"
$arglist = $args -join " "
$val = $dt + " " + $MyArgs
Add-Content -Path $fullpath -Value "$val"
I am trying to connect with office365 PowerShell. I had also imported the module "ExchangeOnlineManagement". I'm getting the below exception:
"The term Connect-ExchangeOnline is not recognized as cmdlet, function , script or operable problem".
string script = #"
Set-ExecutionPolicy Unrestricted
$user = '<your username>'
$pwd = '<your password>'
$SecurePass = ConvertTo-SecureString -AsPlainText $pwd -Force
$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $user,$SecurePass
Import-Module -Name ExchangeOnlineManagement
Connect-ExchangeOnline -Credential $Cred
";
try
{
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
Pipeline pipe = runspace.CreatePipeline();
pipe.Commands.AddScript(script);
try
{
var results = pipe.Invoke();
}
catch (Exception e)
{
}
var error = pipe.Error.ReadToEnd();
if (error.Count > 0)
{
foreach (PSObject err in error)
{
//more logging not sharing that code
}
}
}
}
catch (Exception ex)
{ }
I've been searching the solution of this problem for weeks now and still blocked...
I need to use the program "quser" in powershell but in my C# program.
If I run "quser" just in the powershell console I have a result.
But when I execute a powershell script with "quser", it returns nothing... That's weird.
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
String script = "$re = '(\\S+)\\s+?(\\S*)\\s+?(\\d+)\\s+(\\S+)\\s+(\\S+)\\s+(\\d+\\/\\d+\\/\\d+\\s+\\d+:\\d+)'\n"
+ "query user /server:"+machine+" | Where-Object { $_ -match $re } | ForEach-Object {\n"
+ "New-Object -Type PSCustomObject -Property #{"
+ "'Username' = $matches[1]\n"
+ "'LogonTime' = $matches[6]\n"
+ "}\n"
+ "} | Select-Object Username, LogonTime | ForEach-Object { Write-Output (\"{0} is connected from {1}\" -f $_.Username, $_.LogonTime) }";
ps.AddScript(script);
Collection<PSObject> result = ps.Invoke();
ps.Dispose();
runspace.Close();
runspace.Dispose();
Console.WriteLine(result.Count);
The result of the ps.Invoke() is empty.
But when I run this code from a powershell console it works.
Also another weird thing, it works in remote powershell :
String script = "$re = '(\\S+)\\s+?(\\S*)\\s+?(\\d+)\\s+(\\S+)\\s+(\\S+)\\s+(\\d+\\/\\d+\\/\\d+\\s+\\d+:\\d+)'\n"
+ "query user | Where-Object { $_ -match $re } | ForEach-Object {\n"
+ "New-Object -Type PSCustomObject -Property #{"
+ "'Username' = $matches[1]\n"
+ "'LogonTime' = $matches[6]\n"
+ "}\n"
+ "} | Select-Object Username, LogonTime | ForEach-Object { Write-Output (\"{0} is connected from {1}\" -f $_.Username, $_.LogonTime) }";
string shellUri = "http://schemas.microsoft.com/powershell/Microsoft.PowerShell";
System.Security.SecureString sString = new System.Security.SecureString();
foreach (char passwordChar in password.ToCharArray())
{
sString.AppendChar(passwordChar);
}
PSCredential credential = new PSCredential(username, sString);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(false, machine, 5985, "/wsman", shellUri, credential);
List<String> scriptOutput = new List<String>();
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runspace.Open();
Pipeline pipe = runspace.CreatePipeline();
pipe.Commands.AddScript(script);
Collection<PSObject> result = pipe.Invoke();
foreach (PSObject line in result)
{
scriptOutput.Add(line.ToString());
}
pipe.Dispose();
runspace.Close();
}
foreach(String line in scriptOutput)
{
Console.WriteLine(line);
}
It returns the connected user.
The fact is that I don't want to logged into the machine in order to see the conncted users. (because it works in the powershell console)
If someone could help me please.
Thanks.
Please refer the sample below:
RunspaceSample(string.Empty); fails with The term 'Add-VMToCluster' is not recognized as the name of a cmdlet.
Whereas the RunspaceSample("localhost") or RunspaceSample("somecomputerName") succeed. Any pointer why is it ? Does including RunspaceConnectionInfo changes the powershell version used or RunspaceConfiguration used for execution ?
Also what would be the performance impact if I create Runspace with RunSpaceConnectionInfo with ComputerName = "localhost" even for executing powershell script on a local computer ?.
Thanks
public static void RunspaceSample(string computerName)
{
Runspace rs;
if (string.IsNullOrEmpty(computerName))
{
rs = RunspaceFactory.CreateRunspace();
}
else
{
var connectionInfo = new WSManConnectionInfo
{
OperationTimeout = 10000,
OpenTimeout = 10000,
ComputerName = computerName
};
rs = RunspaceFactory.CreateRunspace(connectionInfo);
}
rs.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = rs;
string script = #"$ComputerName = 'somevm'; $null = Add-VMToCluster -Name $ComputerName -VMName $ComputerName -ErrorAction SilentlyContinue -verbose";
ps.AddScript(script);
Console.WriteLine("Script: {0}", script);
Console.WriteLine("------------------------");
foreach (PSObject result in ps.Invoke())
{
Console.WriteLine(result.ToString());
}
if (ps.HadErrors)
{
var sb = new StringBuilder();
foreach (var errorRecord in ps.Streams.Error)
{
sb.AppendFormat("\nError: {0} CategoryInfo: {1}", errorRecord.Exception.Message, (errorRecord.CategoryInfo != null) ? errorRecord.CategoryInfo.ToString() : string.Empty);
}
var errorMessage = sb.ToString();
Console.WriteLine(errorMessage);
}
}
The FailoverClusters module is only available in 64-bit PowerShell sessions. Making this assembly as 64 bit assembly resolved the module not found issue.
I am trying to run a powershell script in c#
here is the first test script
param(
[string] $computer,
[string] $password
)
out-file -filepath C:\File\parameter.txt -inputobject $computer -encoding ASCII -width 50 -Append
out-file -filepath C:\File\params.txt -inputobject $password -encoding ASCII -width 50 -Append
out-file -filepath C:\File\Sys32.txt -inputobject $password -encoding ASCII -width 50 -Append
and here is my C# supposed to run the powershellscript
private void testRunningPowershell()
{
string mypass = gettingPass();
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
runspace.SessionStateProxy.Path.SetLocation("C:\\Users\\robert\\Desktop\\Titanium\\");
RunspaceInvoke runSpaceInvoker = new RunspaceInvoke(runspace);
//runSpaceInvoker.Invoke("Set-ExecutionPolicy Unrestricted");
// create a pipeline and feed it the script text (AddScript method) or use the filePath (Add method)
using (Pipeline pipeline = runspace.CreatePipeline())
{
Command command = new Command(#"C:\Users\robert\Desktop\Titanium\Titanium2.ps1");
dictionaryParams.Remove("FriendlyName");
foreach (var item in dictionaryParams)
{
if (item.Key == (string)comboBoxScreen.SelectedItem)
{
CommandParameter testParam = new CommandParameter(null, item.Value);
CommandParameter testParam2 = new CommandParameter(null, mypass);
command.Parameters.Add(null, testParam);
command.Parameters.Add(null, testParam2);
}
}
pipeline.Commands.Add(command);
var results = pipeline.Invoke();
ReturnInfo ri = new ReturnInfo();
foreach (PSObject p in results)
{
Hashtable ht = p.ImmediateBaseObject as Hashtable;
ri.ReturnText = (string)ht["ComputerName"];
ri.ReturnText = (string)ht["password"];
}
runspace.Close();
}
}
}
my purpose is to test if the cmdlet is run with the right parameters
I've got a windows form app that allow the user to choose the parameter
I would like to get my parameter value written in Text File (.txt)
instead I am getting this
System.Management.Automation.Runspaces.CommandParameter
instead of the parameter's value choosen