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"
Related
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
}}
";
I am creating a WPF UI and I am having trouble getting the output from a foreach loop within a function into a WPF textbox. The variables I need to output ar $id $newValue and $serviceName
#Iteration through services perform string replacement
ForEach ($Response in $Responses ) {
$id = $Response.id
$newValue = $Response.value.Replace("\Service", "")
$serviceName = $Response.serviceName
Write-Output $id
Write-Output $serviceName
Write-Output $newValue
#if statement here for testing this should be removed
if ($id -eq ) {
$Payload = #{
hostName = '';
hostOperatingSystem = 0;
optionalFilter1 = '';
optionalFilter2 = '';
description = '';
serviceName = $serviceName;
sectionName = "Services";
signature = 'null';``
value = $newValue
} | ConvertTo-Json -Depth 10
#Service Configuration API PUT method call to update fullpath value
$newURI = $URI + '/' + $id
$Response = Invoke-RestMethod -Uri $newURI -Method PUT -Headers $Headers -Body $Payload -ContentType 'application/json'
}
#here for testing this should be removed
else {
$output.text = "Nothing to do"
}
}
$output.text = "Done!"
}
[xml]$Form = Get-Content "MainWindow.xaml"
$NR = (New-Object System.Xml.XmlNodeReader $Form)
$Win = [Windows.Markup.XamlReader]::Load($NR)
$URL = $win.FindName("URL")
$cid = $win.FindName("Client_ID")
$csecret = $win.FindName("Client_Secret")
$update = $Win.FindName("Update")
$output = $Win.FindName("output")
$update.Add_Click( {
$UserAuthRoute = $URL.text
$client_id = $cid.text
$client_secret = $csecret.text
if ($UserAuthRoute -eq "" -or $client_secret -eq "" -or $client_secret -eq "") {
[System.Windows.MessageBox]::Show("Please enter in all values","Variables Required")
}else{
UpdateServices $UserAuthRoute $client_id $client_secret
$output.text += "Service ID:"+$id , "Service Name: "+$serviceName, "$newValue"
}
})
$Win.ShowDialog() ```
The solution was to append the value and separate with a newline
$output.text += "Service ID: " + $id + "`r`n" + "Service Name: " + $serviceName + "`r`n" + "Service Value: " + $newValue + "`r`n"
I'm trying to reproduce the following working chunk of Powershell in C#.
We are connecting on an Exchange2010 instance.
$ExURI = "http://ExchangeUrl/PowerShell/"
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ExURI -Authentication Kerberos
$userName = "patatem"
Import-PSSession $Session -AllowClobber -EA SilentlyContinue | Out-Null
Get-Recipient $userName
Disable-Mailbox -Identity $userName -Confirm:$False
#enable-mailbox -identity $userName -Alias $userName -database "AnExchangeDatabase"
remove-PSSession $Session
I've been following the steps referenced here : https://blogs.msdn.microsoft.com/wushuai/2016/09/18/access-exchange-online-by-powershell-in-c/
In the following block of code, I get positive results when I call Get-Mailbox, Get-Recipient.
When calling Disable-Mailbox, I get the following error
The term 'Disable-Mailbox' 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.
Why is it recognizing Get-Mailbox but not Disable-Mailbox?
(I've tried adding another chunk of code to do the Import session section of Powershell but that didn't change anything.)
public void EnableCommand(string identity, string database)
{
if (!string.IsNullOrWhiteSpace(identity))
{
using (var runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
var powershell = PowerShell.Create();
var command = new PSCommand();
command.AddCommand("New-PSSession");
command.AddParameter("ConfigurationName", "Microsoft.Exchange");
command.AddParameter("ConnectionUri", new Uri(Constants.DefaultOutLookUrl));
command.AddParameter("Authentication", "Kerberos");
powershell.Commands = command;
powershell.Runspace = runspace;
var result = powershell.Invoke();
if (powershell.Streams.Error.Count > 0 || result.Count != 1)
{
throw new Exception("Fail to establish the connection");
}
powershell = PowerShell.Create();
command = new PSCommand();
command.AddCommand("Invoke-Command");
command.AddParameter("ScriptBlock", System.Management.Automation.ScriptBlock.Create("Get-Mailbox"));
command.AddParameter("Session", result[0]);
powershell.Commands = command;
powershell.Runspace = runspace;
var mailBoxes = powershell.Invoke();
// This will give me a result
var returnValue = new StringBuilder();
foreach (var item in mailBoxes)
{
returnValue.AppendLine(item.ToString());
}
// check the other output streams (for example, the error stream)
if (powershell.Streams.Error.Count > 0)
{
returnValue.AppendLine($"{powershell.Streams.Error.Count} errors: ");
foreach (var err in powershell.Streams.Error)
{
returnValue.AppendLine($"{err.ToString()}");
}
}
// This will also work
powershell = PowerShell.Create();
command = new PSCommand();
command.AddCommand("Invoke-Command");
command.AddParameter("ScriptBlock", System.Management.Automation.ScriptBlock.Create("Get-Recipient SomeEmail"));
command.AddParameter("Session", result[0]);
powershell.Commands = command;
powershell.Runspace = runspace;
var mailBoxes2 = powershell.Invoke();
foreach (var item in mailBoxes2)
{
returnValue.AppendLine(item.ToString());
}
if (powershell.Streams.Error.Count > 0)
{
returnValue.AppendLine($"{powershell.Streams.Error.Count} errors: ");
foreach (var err in powershell.Streams.Error)
{
returnValue.AppendLine($"{err.ToString()}");
}
}
// this will give me The term 'Disable-Mailbox' 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.
powershell = PowerShell.Create();
command = new PSCommand();
command.AddCommand("Invoke-Command");
command.AddParameter("ScriptBlock", System.Management.Automation.ScriptBlock.Create("Disable-Mailbox -Identity patatem -Confirm:$False"));
command.AddParameter("Session", result[0]);
powershell.Commands = command;
powershell.Runspace = runspace;
var mailBoxes3 = powershell.Invoke();
foreach (var item in mailBoxes3)
{
returnValue.AppendLine(item.ToString());
}
// check the other output streams (for example, the error stream)
if (powershell.Streams.Error.Count > 0)
{
returnValue.AppendLine($"{powershell.Streams.Error.Count} errors: ");
foreach (var err in powershell.Streams.Error)
{
returnValue.AppendLine($"{err.ToString()}");
}
}
Console.WriteLine(returnValue);
}
}
The term 'Disable-Mailbox' 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.
If the user attempting to run the Disable-Mailbox-cmdlet has insufficient permissions to disable a mailbox (RBAC), this nonspecific error message is issued.
Run your code with sufficient permissions and it should work.
TechNet article: Understanding Role Based Access Control
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.
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