Setting Exchange Mailbox ACL using Powershell - c#

I'm trying to figure out how to do this kind of thing, but using PowerShell calls (from C#). We are moving to Exchange 2010 and my old code doesn't want to work, hence the PowerShell.
IExchangeMailbox exMb = (IExchangeMailbox)userDe.NativeObject;
IADsSecurityDescriptor securityDescriptor = (IADsSecurityDescriptor)exMb.MailboxRights;
IADsAccessControlList acl = (IADsAccessControlList)securityDescriptor.DiscretionaryAcl;
AccessControlEntry ace = new AccessControlEntry();
...
...
I've got the mailbox OK using:
using (PowerShell powershell = PowerShell.Create())
{
powershell.AddCommand("Get-Mailbox");
powershell.AddParameter("Identity", username);
runspace.Open();
powershell.Runspace = runspace;
return powershell.Invoke()[0];
}
But if I then pass the result to a similar method to get the ACL so I can start modifying that, like this
using (PowerShell powershell = PowerShell.Create())
{
powershell.AddCommand("Get-Acl");
powershell.AddArgument(mailbox);
runspace.Open();
powershell.Runspace = runspace;
return powershell.Invoke()[0];
}
...I get
The term 'Get-Acl' is not recognized as the name of a cmdlet
...coming out in the logs. I also tried 'Get-ACL' in case it was case sensitive but I think the first version is correct.
I also tried Get-MailboxPermission but the docs for that say it doesn't even have a return type, so it wouldn't give me an object to manipulate afterwards.
Please help

Figured it out eventually:
powershell.AddCommand("Add-MailboxPermission");
powershell.AddParameter("Identity", mailboxIdentity);
powershell.AddParameter("User", groupName);
powershell.AddParameter("AccessRights", "FullAccess");
powershell.AddParameter("InheritanceType", "All");

Related

C# Powershell not returning any Get-ChildItem results

When I run powershell from my MVC application, i get no results from the Get-ChildItem –Path IIS:\AppPools command.
If i run it directly in powershell it works fine. I have set the site to run on administrator account, thinking that maybe it cant "see" the pools, but no luck.
This is just a test too, accessing the pools, as what i was origionally struggling with was turning off an application pool, as it "could not be found". with Get-WebAppPoolState -name $service
public static string test()
{
using (var powershell = PowerShell.Create())
{
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
using (RunspaceInvoke invoker = new RunspaceInvoke())
{
invoker.Invoke("Set-ExecutionPolicy Unrestricted");
invoker.Invoke("Import-Module WebAdministration");
runspace.Open();
powershell.Runspace = runspace;
powershell.AddScript(#"Import-Module WebAdministration
Get-ChildItem –Path IIS:\AppPools");
var results = powershell.Invoke();
var result = "";
result += ResultsToString(results); // results is a blank list
result += String.Join(",", powershell.Streams.Error.ToList().Select(x => x.Exception.Message));
return result;
}
}
}
}
Any ideas where the issue lies? code, permissions?

Runspace credentials Powershell

I have a Powershell script I want to run by C# as another user.
Here is the code to call the ps script from the current session :
using (PowerShell PowerShellInstance = PowerShell.Create())
{
PowerShellInstance.AddScript(RemoveUser);
PowerShellInstance.AddParameter("GID", GID);
try
{
PowerShellInstance.Invoke();
return true;
}
catch (Exception e)
{
Debug.WriteLine(e.StackTrace);
}
return false;
}
This code works perfectly. But now I want to execute it as another user. I saw a lot of code sample talking about a WSManConnectionInfo so I tried this piece of code found in another question :
var password = new SecureString();
Array.ForEach("myStup1dPa$$w0rd".ToCharArray(), password.AppendChar);
PSCredential credential = new PSCredential("anotherUser", password);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo() { Credential = credential };
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runspace.Open();
using (PowerShell PowerShellInstance = PowerShell.Create())
{
PowerShellInstance.Runspace = runspace;
PowerShellInstance.AddScript(RemoveUser);
PowerShellInstance.AddParameter("GID", GID);
try
{
PowerShellInstance.Invoke();
return true;
}
catch (Exception e)
{
Debug.WriteLine(e.StackTrace);
}
return false;
}
}
But at the moment I add a WSManConnectionInfo, I get a "PSRemotingTransportException" saying that Connecting to remote server localhost failed.
It seems normal because There isn't anything waiting for a connection to my localhost and I don't want to add one. The runspace works when I implement it like that :
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
using (PowerShell PowerShellInstance = PowerShell.Create())
{
PowerShellInstance.Runspace = runspace;
PowerShellInstance.AddScript(RemoveUser);
PowerShellInstance.AddParameter("GID", GID);
try
{
PowerShellInstance.Invoke();
return true;
}
catch (Exception e)
{
Debug.WriteLine(e.StackTrace);
}
return false;
}
}
It seems that there isn't any remote connection implemented, even if we are in localhost. So can I just add some credentials for another user to execute this code and avoid the remote connection ?
You can store a credential variable in an XML file using the following:
$credential = Get-Credential
$credential | Export-Clixml -Path C:\path\to\credential.xml
Execute this with the normal account. Only the normal account will be able to load the credential using the following command:
$credential = Import-Clixml -Path C:\path\to\credential.xml
Once loaded you can execute the Cmdlet like
Remove-ADUser -Identity GlenJohn -Credential $credential -Confirm:$false
If another user tries to import the file the following error is shown:
Import-Clixml : Key not valid for use in specified state.
I recommend you watch the video https://www.youtube.com/watch?v=Ta2hQHVKauo which will give you an in-depth insight on storing credentials.
KR
Guenther

Calling Get-MsolUser returns no results

I'm working on the some proof of concept code for a system that will manage a large number of Office365 accounts, however, I seem to be struggling at the first hurdle with a rather daft problem.
I'm using the RunspaceFactory to fire my Powershell commands at Office 365 and whilst the code appears to be running without any errors I never get a list of users back.
Firstly here's my code....
Runspace runSpace = RunspaceFactory.CreateRunspace();
runSpace.Open();
Pipeline pipeline = runSpace.CreatePipeline();
Command msolConnect = new Command("Connect-MsolService");
System.Security.SecureString securePassword = new System.Security.SecureString();
foreach (char pwdLetter in password)
{
securePassword.AppendChar(pwdLetter);
}
PSCredential credential = new PSCredential(username, securePassword);
msolConnect.Parameters.Add("Credential", credential);
pipeline.Commands.Add(msolConnect);
Command msolGetUser = new Command("Get-MsolUser");
msolGetUser.Parameters.Add("SearchString", "hayley");
pipeline.Commands.Add(msolGetUser);
Collection<PSObject> connectOutput = pipeline.Invoke();
foreach (PSObject psObject in connectOutput)
{
Console.WriteLine(psObject.Members["DisplayName"].Value.ToString());
}
The pipline.HadErrors is false and the connectOutput is always empty. It appears that code is succesfully running but without returning any results.
I have tried;
the same command in Windows Powershell and I get back a list of expected results.
mis-spelling SearchString (just to check that the command was running and the parameter was being passed) and an error is generated as I would expect I have also
using ImportPSModule(new[] { "MsOnline" }) to ensure the MSOnline module is available
other commands (e.g. Get-MsolGroup) and get back an empty list
I know find myself scratching my head on a Friday afternoon hoping that someone else may be able to help me...
Thanks in advance,
Darren
First you need to install SharePoint Online Management Shell from the following link: https://www.microsoft.com/en-us/download/details.aspx?id=35588. But you may also need to install Microsoft Online Services Sign-In Assistant version 7.0 or greater version from the following link: https://www.microsoft.com/en-my/download/details.aspx?id=39267 and then need to install Windows Azure Active Directory Module.
After that you can follow the following code:
InitialSessionState iss = InitialSessionState.CreateDefault();
iss.ImportPSModule(new[] { "MSOnline" });
using (Runspace myRunSpace = RunspaceFactory.CreateRunspace(iss))
{
Pipeline pipeline = myRunSpace.CreatePipeline();
myRunSpace.Open();
// Execute the Get-CsTrustedApplication cmdlet.
using (System.Management.Automation.PowerShell powershell = System.Management.Automation.PowerShell.Create())
{
powershell.Runspace = myRunSpace;
Command connect = new Command("Connect-MsolService");
System.Security.SecureString secureString = new System.Security.SecureString();
string myPassword = "Password";
foreach (char c in myPassword)
secureString.AppendChar(c);
connect.Parameters.Add("Credential", new PSCredential("admin#domain.microsoftonline.com", secureString));
powershell.Commands.AddCommand(connect);
Collection<PSObject> results = null;
Collection<ErrorRecord> errors = null;
results = powershell.Invoke();
errors = powershell.Streams.Error.ReadAll();
powershell.Commands.Clear();
Command getuser = new Command("Get-MsolUser");
getuser.Parameters.Add("MaxResults", 4);
powershell.Commands.AddCommand(getuser);
results = null;
errors = null;
results = powershell.Invoke();
foreach (PSObject psObject in results)
{
Console.WriteLine(psObject.Members["DisplayName"].Value.ToString());
}
}
}
Try appending the Get-MsolUser with -All

Why does PowerShell class not load a snapin

I have this code in which I load a snapin (from MS Dynamics NAV in this case):
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddScript("Add-PSSnapin Microsoft.Dynamics.Nav.Management")
.AddScript("Get-NAVServerInstance");
//This does not work. Says unknown cmdlet Get-NAVServerInstance
//ps.AddCommand("Add-PSSnapin").AddArgument("Microsoft.Dynamics.Nav.Management")
// .AddCommand("Get-NAVServerInstance");
var output = ps.Invoke();
}
}
This code works when I use the AddScript method as shown in the code.
But why does AddCommand method not work (see commented code)? Looks like the snapin is not loaded, because the error says that the Get-NAVServerInstance cmdlet is unknown.
How is this supposed to work?
I know I can create a runspace with an InitialSessionState on which I have imported the snapin. Then the ps.AddCommand("Get-NAVServerInstance") is working.
But when I want to create a remote runspace session (using WSManConnectionInfo) I can't find a way to supply an initialSessionState.
UPDATE:
So it seems that AddCommand only can be used for cmdlets available when the runspace is opened (or created?). Using an InitialSessionState or RunspaceConfiguration instance with RunspaceFactory.CreateRunspace(...) will do. So this code works:
var config = RunspaceConfiguration.Create();
PSSnapInException warning;
config.AddPSSnapIn("Microsoft.Dynamics.Nav.Management", out warning);
using (Runspace runspace = RunspaceFactory.CreateRunspace(config))
{
runspace.Open();
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddCommand("Get-NAVServerInstance");
var output = ps.Invoke();
}
}
But my problem is in that case, that I can't specify a WSManConnectionInfo instance.
So how can I create a runspace with a remote connection with a snapin (installed on the remote machine) loaded? How to supply a configuration for a remote connection?
I finally found a hint how to configure a remote session (see https://superuser.com/a/518567).
You need to register a session configuration on the remote computer with
Register-PSSessionConfiguration -Name MyShell -StartupScript 'MyInitScript.ps1'
Then you can set the shellUri parameter of WSManConnectionInfo to http://schemas.microsoft.com/powershell/MyShell
The runspace you create this way will have the commands available which are imported by the MyInitScript.ps1 startup script.
So now this code will work:
string shell = "http://schemas.microsoft.com/powershell/MyShell";
var target = new Uri("https://myserver:port/wsman");
var secured = new SecureString();
foreach (char letter in "password")
{
secured.AppendChar(letter);
}
secured.MakeReadOnly();
var credential = new PSCredential("username", secured);
var connectionInfo = new WSManConnectionInfo(target, shell, credential);
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runspace.Open();
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddCommand("Get-NAVServerInstance");
var output = ps.Invoke();
}
}
Try invoking AddScript like so:
.AddScript("...", false)
this will execute the command in the global scope instead of a new local scope.
I think the proper way to do this is to use the RunspaceConfiguration class. It has an AddPSSnapin method.

Syntax not supported by this runspace in Powershell and C#

I am trying to get information about my users' mailboxes using power shell integrated into C#, but i am getting below error:
This syntax is not supported by this run space. This might be because it is no-language mode.
Here is the code that I am using:
using System;
using System.Collections.ObjectModel;
using System.Collections;
using System.Linq;
using System.Text;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Threading;
using System.Globalization;
namespace Office365
{
class Program
{
static void Main(string[] args)
{
CultureInfo oldCI = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
System.Security.SecureString secureString = new System.Security.SecureString();
string myPassword = "mySecretPassword";
foreach (char c in myPassword)
secureString.AppendChar(c);
PSCredential credential = new PSCredential("my#maildomain.com", secureString);
Console.WriteLine("Forbinder....");
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri("https://ps.outlook.com/PowerShell-LiveID?PSVersion=2.0"), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic;
connectionInfo.SkipCACheck = true;
connectionInfo.SkipCNCheck = true;
string cmd2 = #"Get-Mailbox | Select-object Identity, displayname, ProhibitSendReceiveQuota, #{n='size';e={$MBXSTAT=Get-MailboxStatistics $_.Identity; $MBXSTAT.TotalItemSize}}";
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runspace.Open();
using (Pipeline pipeline = runspace.CreatePipeline(cmd2))
{
pipeline.Commands.AddScript(cmd2);
Collection<PSObject> results = pipeline.Invoke();
foreach (PSObject obj in results)
{
// Do something with each result object
}
}
}
}
}
Any Suggestions on this? How can I overcome to this issue?
Normally i like to leave the dead alone but in this case i feel the need to resurrect this old post because i encountered the exact problem, need a place to keep this code and hope this prevents anybody else from wasting too much time.
I have found connecting to O365 with WSManConnectionInfo problematic and after wasting far too much time trying to get it to work i won't be using it unless i have no other choice.
That said the following code works for me and behaves the same as if i had opened a PowerShell prompt entered the commands i want to run :) it makes testing a lot simpler.
using (var _power_shell = PowerShell.Create()) {
_power_shell.AddScript("Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process");
_power_shell.Invoke();
var credential = new PSCredential(username, password.ToSecureString());
_power_shell.Runspace.SessionStateProxy.SetVariable("Credential", credential);
_power_shell.AddScript("$Session = New-PSSession –ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell -Credential
$Credential -Authentication Basic -AllowRedirection");
_power_shell.AddScript("Import-PSSession $Session");
_power_shell.Invoke();
_power_shell.AddScript("Get-Mailbox -RecipientTypeDetails RoomMailbox | Select-Object DisplayName, PrimarySmtpAddress");
var results = _power_shell.Invoke();
foreach (var obj in results)
{
/* Do something with the results */
}
_power_shell.AddScript("Remove-PSSession $Session");
_power_shell.Invoke();
}
The above code assumes username & password as string variables already in scope. Also an Extention method ToSecureString exists and is in scope.
As far as i understand PowerShell.Creste() provides an execution environment set to Restricted so you first need to change that. In this case becasue we scoped the Set-ExecutionPolicy to the process Admin rights are not required.
From what little i found on the subject it appears you need to remove the session when you are done or you may get throttled and lock yourself out of O365 for some time.
This is restriction from Exchange online and you need to use Commands instead of scripts in this case.
For example, running cmdlet Get-Mailbox -Identity user#mydomain.onmicrosoft.com will look like:
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri("https://ps.outlook.com/PowerShell-LiveID?PSVersion=2.0"), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic;
Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo);
PowerShell shellInstance = PowerShell.Create();
shellInstance.Runspace = runspace;
PSObject mailbox = shellInstance.Runspace.AddCommand("Get-Mailbox").AddParameter("Identity","user#mydomain.onmicrosoft.com").Invoke().First();
I didn't use complex scripts and pipelines in my project so I'm not sure how it works if you need to run something rather tricky.

Categories

Resources