Unsure how to handle powershell response in .NET - c#

I have 2 powershell scripts that I execute from c# which I'm using to first list message using an IMAP cmdlet and the 2nd script performs a view on a specific message. Both execute successfully from powershell, the 2nd one I am trying to retrieve an attachment and I see a bunch of data output to the console like so...
Sent from my iPhone
------=_NextPart_000_0027_01CCDAA7.399EBE00
Content-Type: image/jpeg; name="photo.JPG"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="photo.JPG"
/9j/4QH6RXhpZgAATU0AKgAAAAgACgEPAAIAAAAGAAAAhgEQAAIAAAAHAAAAjAESAAMAAAABAAYA
AAEaAAUAAAABAAAAlAEbAAUAAAABAAAAnAEoAAMAAAABAAIAAAEyAAIAAAAUAAAApAITAAMAAAAB
AAEAAIdpAAQAAAABAAAAuIglAAQAAAABAAABZgAAAABBcHBsZQBpUGhvbmUAAAAAAEgAAAABAAAA
SAAAAAEyMDA5OjA5OjIwIDE1OjEwOjU1AAAKgp0ABQAAAAEAAAE2kAAABwAAAAQwMjIxkAMAAgAA
ABQAAAE+kAQAAgAAABQAAAFSkQEABwAAAAQBAgMAoAAABwAAAAQwMTAwoAEAAwAAAAEAAQAAoAIA
BAAAAAEAAAZAoAMABAAAAAEAAASwpAYAAwAAAAEAAAAAAAAAAAAAAA4AAAAFMjAwOTowOToyMCAx
so it appears everything is fine so far - except there is I believe a difference in the function result as possible a stream/pipe from powershell?
The first one returns a collection of PSObjects which is called like so
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.AddScript(script);
ps.Invoke();
foreach (PSObject result in ps.Invoke())
{
dynamic val = result.BaseObject;
}
The 2nd script executes without error using Invoke() however trying to retrieve data via for each or
dynamic xx = ps2.Invoke();
xx is empty;
I thought maybe I need to use BeginInvoke and call asynchronously so I tried
static dynamic GotMail(dynamic o)
{
return o;
}
delegate dynamic SomeDelegate(dynamic o);
and attempted to use like so..
SomeDelegate sd = GotMail;
IAsyncResult ar = ps2.BeginInvoke();
dynamic val2 = sd.EndInvoke(ar);
and I recieve "The async result object is null or of an unexpected type." I'm not even passing parameters into the powershell scripts..everything is hardcoded - the script I run from powershell successfully is exactly what I execute from .NET
Any suggestions for this much appreciated.
Thanks

You're invoking the script twice by calling Invoke() twice. Remove the first call to invoke e.g.:
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.AddScript(script);
//ps.Invoke();
foreach (PSObject result in ps.Invoke()) {
dynamic val = result.BaseObject;
}

Related

C# & Powershell: Not returning all Powershell results

I've been making a C# application that runs some Powershell stuff, the issue is that it doesn't seem to be be collecting the correct/all results of the Powershell commands.
The below code:
private void Button_Click(object sender, RoutedEventArgs e)
{
List<string> results = RunScript("Get-ADUser testuser2 -Properties *");
this.ShowMessageAsync("OK",results[0]);
}
private List<string> RunScript(string comand)
{
var powerShell = PowerShell.Create();
powerShell.Runspace = runspace;
powerShell.AddScript(comand);
Collection<PSObject> results = powerShell.Invoke();
List<string> resultsList = new List<string>();
if (powerShell.Streams.Error.Count > 0)
{
this.ShowMessageAsync("Something went wrong!", "Check error log for details!");
runspaceBox.AppendText(powerShell.Streams.Error[0] + "\r\n");
}
else
{
foreach (PSObject obj in results)
{
runspaceBox.AppendText(obj.ToString() + "\r\n");
resultsList.Add(obj.ToString());
}
}
return resultsList;
}
Results in:
{CN=testuser2,DC=testdomain,DC=com}
Putting the command into Powershell directly results in the below:
PS C:\Users\Ultimate-V1> Get-ADUser testuser2 -Properties *
[A bunch of extra stuff above]
Description :
DisplayName : Test User2
DistinguishedName : CN=Test
One,CN=Users,DC=testdomain,DC=com
Division :
DoesNotRequirePreAuth : False
[A lot of extra stuff below]
I've snipped a lot of the results out of the above command as it is very long.
What I can't work out is why it is only returning the Distinguished Name and not all of the results, this happens with some commands but not others. The command to return all AD groups on a user works fine for example.
If possible i'd love it to have EVERYTHING that the command returns, I will be formatting the resultsList later in the code.
Any help or pointers would be greatly appreciated!
Because you casted the result as a string, so you got a string representation of the complex object (whatever the result of the object's .ToString() method).
In PowerShell itself, it doesn't cast everything to a string to display it; it has various rules about how to display objects in general (usually a table or list format), and some types have specific formatters.
PetSerAl already answered in the comments, but the way to get a string representation of what PowerShell displays on the console is to send your output through Out-String:
powerShell.AddScript(comand).AddCommand("Out-String");

Where is Microsoft.Exchange.RecipientTasks located

I am trying to automate a process that gets and sets mailbox permissions using remote powershell via an automated exchange pipeline. The AccessRights property of the Get-MailboxPermission command output is of type Microsoft.Exchange.RecipientTasks.MailboxRights []. I cannot find this namespace anywhere on nuget and it does not appear to be part of the EWS api, as far as I can tell. Does anyone know where this namespace can be found so I can reference it in my project, or if there is a way to cast the output as a standard object type?
Thank you
In Remote Powershell you have deserialized types https://blogs.msdn.microsoft.com/powershell/2010/01/07/how-objects-are-sent-to-and-from-remote-sessions/ so the server side types aren't what you should be using at that point. All you need to do is something like
Command getmbPerms = new Command("Get-MailboxPermission");
getmbPerms.Parameters.Add("Identity", Mailbox);
Pipeline plPileLine = Runspace.CreatePipeline();
plPileLine.Commands.Add(getmbPerms);
Collection<PSObject> RsResultsresults = plPileLine.Invoke();
foreach (PSObject obj in RsResultsresults)
{
Console.WriteLine(obj.Properties["User"].Value.ToString());
PSObject AccessRights = (PSObject)obj.Properties["AccessRights"].Value;
System.Collections.ArrayList AccessRightsCol = (System.Collections.ArrayList)AccessRights.BaseObject;
foreach (String Permission in AccessRightsCol)
{
Console.WriteLine(Permission);
}
}
plPileLine.Stop();
and then just parse the Enums out of the String. When you Set rights for a user the AccessRights parameter is just a string that matches the server side enums.
Command AddPermissions = new Command("Add-MailboxPermission");
AddPermissions.Parameters.Add("Identity", Mailbox);
AddPermissions.Parameters.Add("User", UserName);
AddPermissions.Parameters.Add("AccessRights", "FullAccess");
AddPermissions.Parameters.Add("AutoMapping", false);

Calling PowerShell function from within the script in C# Application

I just started playing with PowerShell two days ago. I am running a PowerShell script form a C# console application. I pass in a list of objects (MyObj1) as a parameter to the script and do some data manipulation to the object. However, when I call a function within the script I get a function not recognized error while printing out the errors. Here is a small sample of what I am doing.
public class MyObj1
{
public string Property1 {get; set;}
public int Property2 {get; set;}
}
Here is part of code that I am using to run the script:
var rs = RunspaceFactory.CreateRunspace();
rs.Open();
rs.SessionStateProxy.SetVariable("list", myObjectList);
rs.SessionStateProxy.SetVariable("xml", xml);
var ps = PowerShell.Create();
ps.Runspace = rs;
var psScript = System.IO.File.ReadAllText(#"C:\temp\ps.ps1");
ps.AddScript(psScript);
ps.Invoke();
foreach (var item in myObjectList)
{
Console.WriteLine(item.Property1 + "; " + item.Property1);
}
Console.WriteLine("\n================================ ERRORS ============================ \n");
foreach (ErrorRecord err in ps.Streams.Error)
{
Console.WriteLine(err.ToString());
}
And here is the actual script:
$list | ForEach {
$_.Property1 = $_.GetType()
DoSomeThing ([ref] $_)
}
Function DoSomeThing ($MyObject)
{
$Asset.Property2 = $Asset.Property2 + $Asset.Property2
}
Two days ago while I was playing with same script using some dummy classes like above, I was able to modify the data but since this morning, I have modified the script to use real classes. And Ctrl+Z will only take me back to a certain point in my editor. In the ForEach loop everything works until I cal the function.
As for the project, the plan is to push this application with required data manipulation in C# and then have some optional changes done in the script. The point is to avoid push to the application to production server and handling all/most data manipulation in the script.
Move function declaration to the beginnig of the script file.
Function DoSomeThing ($MyObject)
{
$Asset.Property2 = $Asset.Property2 + $Asset.Property2
}
$list | ForEach {
$_.Property1 = $_.GetType()
DoSomeThing ([ref] $_)
}
PS doesn't compile script, it executes it command by command. So you have to create/declare function before you use it.

Creating new EventSource using Powershell differs to C#?

I am creating a new EventLog EventSource, and I use my own custom set of categories.
I currently do this in C# code with the following:
var eventSourceData = new EventSourceCreationData(sourceName, logName)
{
CategoryCount = AllCategories.Count(),
CategoryResourceFile = resourceFilePath,
ParameterResourceFile = resourceFilePath,
MessageResourceFile = resourceFilePath
};
EventLog.CreateEventSource(eventSourceData);
I have now converted this to a powershell script like this:
New-eventlog -logname $Log -ComputerName $ComputerName -Source $Source -CategoryResourceFile $categoryDllPath -MessageResourceFile $categoryDllPath -ErrorVariable Err -CategoryCount 20
Notr -CategoryCount 20 at the end, my script fails on this argument saying it is not a valid parameter. (Which it seems not to be)
So my question is, using Powershell how can I provide the CategoryCount so that the logging works correctly?
Thanks so much.
The error message is A parameter cannot be found that matches parameter name 'CategoryCount'.
As far as I can see, New-EventLog doesn't support CategoryCount.
But you can still use .NET classes directly in Powershell, so something like this should work:
$eventSourceData = new-object System.Diagnostics.EventSourceCreationData("$SourceName", "$logName")
$eventSourceData.CategoryCount = 20
$eventSourceData.CategoryResourceFile = $CategoryDllPath
$eventSourceData.MessageResourceFile = $CategoryDllPath
If (![System.Diagnostics.EventLog]::SourceExists($eventSourceData.Source))
{
[System.Diagnostics.EventLog]::CreateEventSource($eventSourceData)
}

Passing variables to powershell from C# : Output file location variables

I have recently began using C# to invoke powershell scripts, with some success :)
$spWeb = Get-SPWeb -Identity http://127.0.0.1
$listTemplate = [Microsoft.SharePoint.SPListTemplateType]::DocumentLibrary
$spWeb.Lists.Add($args, "", $listTemplate) > $myDirectory
$spWeb.Lists.Add returns a SharePoint GUID.
Question:
I simply wish to pass in the directory/filename in which the GUID will
be written. How could that be done?
I have posted on the MSDN forums here: http://goo.gl/5p0oz but have continued my search on stackoverflow due to a seemingly dead end thread. This post is a cumulative gathering of the information found through MSDN responses.
You can try using the Set-Content cmdlet instead, like this. You need to pass the $myFile as a string and Set-Content will do the rest for you -
Inside your script i.e. MyScript.ps1 here, you will have this piece of code -
param([string]$parameter, [string]$myFile)
try
{
$spWeb = Get-SPWeb -Identity http://127.0.0.1
$listTemplate = [Microsoft.SharePoint.SPListTemplateType]::DocumentLibrary
$guid = $spWeb.Lists.Add($parameter, "", $listTemplate)
$guid | Set-Content $myFile
}
catch
{
throw ("Failed. The error was: '{0}'." -f $_ )
}
How to Run:
Open Powershell Prompt and type this
.\MyScript.ps1 "someparam1" "D:\Output.txt"
C# Bit -
private PowerShell _ps;
_ps = PowerShell.Create();
var parameter = new List<string>
{
parameter,
myFile
};
var sr = new StreamReader(#"C:\MyScript.ps1");
_ps.AddScript(sr.ReadToEnd()).AddParameters(parameter);
_ps.Invoke();

Categories

Resources