I have created a web service that should allow to pass commands to the Exchange Server Powershell. When I test it by running it on the machine with VS (localhost) everything works. However, when I try to use this service from a other machine. I get Access Denied Error.
This is the service:
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Remoting;
using System.Management.Automation.Runspaces;
using System.Configuration;
using Microsoft.Exchange.WebServices.Data;
namespace pshell
{
[WebService(Namespace = "http://some1.domain.int/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class PowerShellService : System.Web.Services.WebService
{
private bool Authenticate(string u, string p)
{
if ((u == "xxxxxx") && (p == "xxxxxxx"))
return true;
else
return false;
}
private int SecurityLevel(string u)
{
if (u == "xxxxx")
return 100;
else
return 0;
}
[WebMethod]
public string PSCmd(string authuser, string authpass, string cmd, string pars)
{
if (!Authenticate(authuser, authpass))
return "<collection><RESULT status=\"ERROR\" message=\"Authentication failed!\" /></collection>";
String Password = System.Configuration.ConfigurationManager.AppSettings["UUPASS"];
System.Security.SecureString secureString = new System.Security.SecureString();
foreach (char c in Password)
secureString.AppendChar(c);
PSCredential ExchangeCredential = new PSCredential(System.Configuration.ConfigurationManager.AppSettings["UUNAME"], secureString);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri(System.Configuration.ConfigurationManager.AppSettings["UURI"]), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", ExchangeCredential);
Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic;
PowerShell powershell = PowerShell.Create();
PSCommand command = new PSCommand();
if (cmd.Trim() != "")
{
//here we check for security level
if (SecurityLevel(authuser) >= 100)
{
//admin fully allowed
command.AddCommand(cmd);
}
else
{
//test for allowed commands
if ((SecurityLevel(authuser) < 100) &&
(SecurityLevel(authuser) >= 90))
{
if (cmd.ToLower() == "get-mailbox")
{
command.AddCommand(cmd);
}
}
}
}
else
return "<collection><RESULT status=\"ERROR\" message=\"Missing command!\" /></collection>";
if (pars.Trim() != "")
{
string[] parameters = pars.Split('|');
foreach (string item in parameters)
{
String p = item.Substring(0, item.IndexOf("="));
String v = item.Substring(item.IndexOf("=") + 1);
if (p.Trim().ToLower() == "password")
{
System.Security.SecureString passString = new System.Security.SecureString();
foreach (char c in v)
passString.AppendChar(c);
command.AddParameter(p, passString);
}
else if ((v.Trim().ToLower() == "false") ||
(v.Trim().ToLower() == "true"))
{
if (v.Trim().ToLower() == "false")
command.AddParameter(p, false);
else
command.AddParameter(p, true);
}
else
{
command.AddParameter(p, v);
}
}
}
powershell.Commands = command;
runspace.Open();
Pipeline pl = runspace.CreatePipeline();
powershell.Runspace = runspace;
Collection<PSObject> results = null;
string xml = "<collection>";
try
{
results = powershell.Invoke();
var error = pl.Error.Read() as Collection<ErrorRecord>;
if (error != null)
{
foreach (ErrorRecord er in error)
{
xml += "<RESULT status=\"ERROR\" type=\"pipe\" message=\"" + er.ErrorDetails.Message + "\" />";
}
pl.Stop();
}
xml += "<RESULT status=\"OK\" />";
}
catch(Exception err)
{
xml += "<RESULT status=\"ERROR\" type=\"exception\" codelevel=\"1\" message=\"" + err.Message + "\" />";
}
try
{
foreach (PSObject item in results)
{
for (int i = 0; i < item.Properties.Count(); i++)
{
if (item.Properties.ElementAt(i).MemberType == PSMemberTypes.Property)
{
xml += "<" + item.Properties.ElementAt(i).Name + ">" +
item.Properties.ElementAt(i).Value +
"</" + item.Properties.ElementAt(i).Name + ">";
}
}
}
}
catch(Exception err)
{
xml += "<RESULT status=\"ERROR\" type=\"exception\" codelevel=\"2\" message=\"" + err.Message + "\" />";
}
xml += "</collection>";
return xml;
}
}
}
and this is the PHP code that I want to use to send a command:
$ini = ini_set("soap.wsdl_cache_enabled","0");
$params = array('authuser' => 'xxxx',
'authpass' => 'xxxx',
'cmd' => 'get-mailbox',
'pars' => '');
$client = new SoapClient("http://web.domain.com/pshell/callpshell.asmx?WSDL", array('soap_version' => SOAP_1_2));
$response = $client->PSCmd($params)->PSCmdResult;
print $response;
and this is the error message I receive:
Connecting to remote server some1.domain.int failed with the following error message : Access is denied. For more information, see the about_Remote_Troubleshooting Help topic.
I have enabled Remote Access on the Exchange Server, and I did everything Remote Troubleshooting suggested.
Any suggestions?
The Access Denied Error occurs because the web service is launched as IIS_USER which has insufficient rights to call remote powershell.
OK after long poking around, I resolved the issue like this:
Create a new Applciation Pool
Set the Identity of the application pool to the user that has the rights to access Powershell remotely
Bind the WebService to the Application pool
and it works :)
Related
Have a utility I wrote that checks (among other things) the last time a set of servers was rebooted. This works great as long as the servers are all within my domain and the user launching the app have rights on the servers. Added a section where the user can specify alternative credentials, in our case specifically to deal with another domain. The credentials I am feeding into have domain admin rights on the destination domain, yet my code is getting an Access Denied (Unauthorized Access) error.
thanks!
private void btnLastReboot_Click(object sender, EventArgs e)
{
ConnectionOptions conOpts = new ConnectionOptions();
if (selectedList.Count > 0)
{
Cursor currentCursor = Cursor.Current;
Cursor.Current = Cursors.WaitCursor;
stripProgress.Visible = true;
stripProgress.Minimum = 0;
stripProgress.Maximum = selectedList.Count();
stripProgress.Step = 1;
stripProgress.Value = 0;
rtfOut.SelectionTabs = new int[] { 100, 200 };
rtfOut.Text = "";
var sq = new SelectQuery("Win32_OperatingSystem");
if (prefs.useCurrentUser == true)
{
// Setting all fields to NULL causes current user info to be used
conOpts.Username = null;
conOpts.Password = null;
conOpts.Authority = null;
}
else
{
conOpts.Username = prefs.userName;
conOpts.Password = prefs.password.ToString();
conOpts.Authority = "ntlmdomain:" + prefs.domain;
}
foreach (ServerList anEntry in selectedList)
{
stripProgress.Value++;
try
{
var mgmtScope = new ManagementScope("\\\\" + anEntry.ServerName + "\\root\\cimv2", conOpts);
mgmtScope.Connect();
var mgmtSearcher = new ManagementObjectSearcher(mgmtScope, sq);
foreach (var item in mgmtSearcher.Get())
{
var lastBoot = item.GetPropertyValue("LastBootUpTime").ToString();
DateTime lboot = ManagementDateTimeConverter.ToDateTime(lastBoot);
rtfOut.Text += anEntry.ServerName + "\t";
if(anEntry.ServerName.Length <= 9)
{
rtfOut.Text += "\t";
}
rtfOut.Text += lboot.ToLongDateString() + " (" + lboot.ToLongTimeString() + ")\r\n";
}
}
catch (Exception ex)
{
if (ex is UnauthorizedAccessException)
{
rtfOut.Text += anEntry.ServerName + "\t <Access Denied>\r\n";
}
else
{
rtfOut.Text += anEntry.ServerName + "\t <not responding>\r\n";
}
}
}
stripProgress.Visible = false;
Cursor.Current = currentCursor;
}
}
Had to sleep on it, but the answer finally hit me in the shower...
I store the password the user provides in a SecureString variable, but the ConnectionOptions' password field expects the value as a clear string. I was able to test this by temporarily hard coding a password in, which then worked. The final solution was to convert the SecureString to a clear string and then assign it to the ConnectionOption.
If anyone is curious, I used this bit to convert the password back:
string password = new System.Net.NetworkCredential(string.Empty, securePassword).Password;
I'm able to get a simple c# function to work, but when I introduce something more complicated such as what's below, I'm getting syntax errors and there isn't a lot of examples on how to do this.
I've made updates to the code based on advice received here, but this code still does not function properly
cls
$dagDistribution = $null;
$distribution =
#'
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Security.Principal;
namespace MultiThreading
{
public class dagDistribution
{
public List<string> get(string dag)
{
DateTime start = DateTime.Now;
var response = new ConcurrentBag<Collection<PSObject>>();
var exceptions = new ConcurrentQueue<Exception>();
string dagName = "hqdag1";
string[] serversUnsorted = getDagMembers(dagName);
var servers = from s in serversUnsorted orderby s select s;
try
{
Parallel.ForEach(servers, server =>
{
response.Add(runPowerShellScript(server));
});
}
catch (AggregateException ae)
{
foreach (var aex in ae.InnerExceptions)
{
exceptions.Enqueue(aex);
}
}
List<string> returnValues = new List<string>();
foreach (var item in response)
{
string returnValue = parseServerResults(item);
returnValues.Add(returnValue);
}
returnValues.Sort();
return returnValues;
}
private Collection<PSObject> runPowerShellScript(object server)
{
Collection<PSObject> psobjs = new Collection<PSObject>();
string result = "";
string serverName = server.ToString();
WSManConnectionInfo wmc = new WSManConnectionInfo(new Uri("http://xxx/powershell"));
wmc.AuthenticationMechanism = AuthenticationMechanism.Kerberos;
wmc.ShellUri = "http://schemas.microsoft.com/powershell/Microsoft.Exchange";
using (Runspace runspace = RunspaceFactory.CreateRunspace(wmc))
{
PowerShell powershell = PowerShell.Create();
if (runspace.RunspaceStateInfo.State == RunspaceState.Opened)
{
// do nothing
}
else
{
runspace.Open();
powershell.Runspace = runspace;
}
try
{
PSCommand command = new PSCommand();
command.AddScript("get-mailboxdatabase -Server " + server + " -Status");
powershell.Commands = command;
psobjs = powershell.Invoke();
if (powershell.HadErrors == true)
{
result = "Failed - " + powershell.Streams.Error[0].ToString();
result = result.Replace("\"", "*");
}
}
catch (Exception ex)
{
string fail = ex.Message;
}
}
object serverNameO = server;
PSObject serverNameObj = new PSObject(serverNameO);
psobjs.Insert(0, serverNameObj);
return psobjs;
}
private string[] getDagMembers(string dagName)
{
Collection<PSObject> psobjs = new Collection<PSObject>();
string result = "";
string[] servers = null;
WSManConnectionInfo wmc = new WSManConnectionInfo(new Uri("http://xxx/powershell"));
wmc.AuthenticationMechanism = AuthenticationMechanism.Kerberos;
wmc.ShellUri = "http://schemas.microsoft.com/powershell/Microsoft.Exchange";
using (Runspace runspace = RunspaceFactory.CreateRunspace(wmc))
{
PowerShell powershell = PowerShell.Create();
if (runspace.RunspaceStateInfo.State == RunspaceState.Opened)
{
// do nothing
}
else
{
runspace.Open();
powershell.Runspace = runspace;
}
try
{
PSCommand command = new PSCommand();
command.AddScript("Get-DatabaseAvailabilityGroup -Identity " + dagName);
powershell.Commands = command;
psobjs = powershell.Invoke();
if (powershell.HadErrors == true)
{
result = "Failed - " + powershell.Streams.Error[0].ToString();
result = result.Replace("\"", "*");
}
PSPropertyInfo serversTemp = null;
foreach (PSObject psobj in psobjs)
{
serversTemp = psobj.Properties["servers"];
}
string s_servers = serversTemp.Value.ToString();
servers = s_servers.Split(' ');
}
catch (Exception ex)
{
string fail = ex.Message;
}
}
return servers;
}
private string parseServerResults(Collection<PSObject> serverObjs) // needs servername, totaldbs, activedbs, passivedbs, preferencecount (11,11,11,11), mounteddbs, dismounteddbs, dagname
{
// called independently with each server, first object is always the server name
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
int index = 0;
string returnValue = "";
string serverName = "";
int totalDbs = 0;
int activeDbs = 0; // whichever has activation preference 1
int passiveDbs = 0; // whichever has activation preference 2, 3 or 4
string activeCopyServerName = "";
int activationPreferenceOne = 0;
int activationPreferenceTwo = 0;
int activationPreferenceThree = 0;
int activationPreferenceFour = 0;
int mountedCount = 0;
int dismountedCount = 0;
string dagName = "";
string dagServerAndDatabaseName = "";
foreach (PSObject obj in serverObjs)
{
if (index == 0)
{
serverName = obj.ToString();
}
totalDbs = (serverObjs.Count - 1);
PSMemberInfoCollection<PSPropertyInfo> props = obj.Properties;
string currentPrimaryActivationServer = "";
foreach (PSPropertyInfo prop in props)
{
if (prop.Name == "MountedOnServer")
{
currentPrimaryActivationServer = prop.Value.ToString();
break;
}
}
List<string> propertyNames = new List<string>();
foreach (PSPropertyInfo prop in props)
{
string result = prop.Name + " | " + prop.Value;
if (prop.Name == "Mounted")
{
if (prop.Value.ToString() == "True")
{
if (currentPrimaryActivationServer.ToLower().StartsWith(serverName.ToLower()))
{
mountedCount++;
}
}
else
{
dismountedCount++;
}
}
else if (prop.Name == "MountedOnServer")
{
activeCopyServerName = prop.Value.ToString();
}
else if (prop.Name == "ActivationPreference")
{
string arr = prop.Value.ToString();
string[] vals = arr.Split(']');
foreach (string val in vals)
{
if (val != "")
{
string valTemp = val;
if (val.Contains("["))
{
valTemp = val.Replace("[", "");
}
string[] preference = valTemp.Split(',');
string preferenceZero = preference[0].ToString().Trim();
string preferenceOne = preference[1].ToString().Trim();
if (preferenceZero.ToLower() == serverName.ToLower())
{
if (preferenceOne == "1")
{
if (currentPrimaryActivationServer.ToLower().StartsWith(serverName.ToLower()))
{
activeDbs++;
}
else
{
passiveDbs++;
}
}
else
{
if (!(currentPrimaryActivationServer.ToLower().StartsWith(serverName.ToLower())))
{
passiveDbs++;
}
else
{
activeDbs++;
}
}
switch (preferenceOne)
{
case "1":
activationPreferenceOne++;
break;
case "2":
activationPreferenceTwo++;
break;
case "3":
activationPreferenceThree++;
break;
case "4":
activationPreferenceFour++;
break;
default:
break;
}
}
}
}
}
else if (prop.Name == "Server")
{
string activeCopyServerName2 = prop.Value.ToString();
}
else if (prop.Name == "MasterServerOrAvailabilityGroup")
{
dagName = prop.Value.ToString();
}
else if (prop.Name == "MailboxProvisioningAttributes")
{
dagServerAndDatabaseName = prop.Value.ToString();
}
propertyNames.Add(prop.Name.ToString()); // cumulative count of the property names
}
index++;
}
stopwatch.Stop();
Console.WriteLine(serverName + " - " + stopwatch.Elapsed.ToString());
return returnValue = serverName + "|" + totalDbs + "|" + activeDbs + "|" + passiveDbs + "|" + activationPreferenceOne + "," + activationPreferenceTwo + "," +
activationPreferenceThree + "," + activationPreferenceFour + "|" + mountedCount + "|" + dismountedCount + "|" + dagName;
}
}
}
'#
write-host "after here-string";
Add-Type -TypeDefinition $distribution -ReferencedAssemblies System.Collections, System.ComponentModel, System.Data, System.Drawing, System.Linq, System.Management.Automation, System.Security, System.Threading.Tasks, System.Windows.Forms, System.Threading, System.Collections.Concurrent, System.Security.Principal
$dagDistribution = New-Object MultiThreading.dagDistribution;
$val = $dagDistribution.get("dag2");
You have two problems. Probably really just one. By default Add-Type uses the C# version 5 compiler, which is the latest one to be included in Windows. The string interpolation with $ is a newer feature. See this answer Powershell Add-Type C# 6.0.
Second, you have powershell escape characters in your C# code that shouldn't be there. Instead use a literal here-string to include arbitrary C# source. EG:
$distribution = #'
namespace MultiThreading
{
....
}
'#
C# has no "special" way to reference .NET Framework types, so you have to provide the compiler with a list of assemblies your code depends on.
Add-Type will use will use the current .NET Framework assemblies if you specify the "short name" of the assembly in the -ReferencedAssemblies argument. So:
Add-Type -TypeDefinition $distribution -ReferencedAssemblies System.Data, System.Xml
If you need an assembly that can't be resolved this way, you have to list the Assembly FullName, and Add-Type will try to load it.
You definitely want to avoid putting a full AssemblyName for a .NET Framework assembly in your powershell code, as that might cause your script to break when running on a machine with a different .NET Framework version, or with .NET Core.
I want to create Mailbox in exchange server 2013 using c# .
I tried lots of codes but each one gets an error that there is no obvious solution to solve it.
my code is
public static Boolean CreateUser(string FirstName, string LastName, string Alias,string PassWord, string DomainName, string OrganizationalUnit)
{
string Name = FirstName + " " + LastName;
string PrincipalName = FirstName + "." + LastName + "#" + DomainName;
Boolean success = false;
string consolePath = #"C:\Program Files\Microsoft\Exchange Server\V15\bin\exshell.psc1";
PSConsoleLoadException pSConsoleLoadException = null;
RunspaceConfiguration rsConfig = RunspaceConfiguration.Create(consolePath, out pSConsoleLoadException);
SecureString spassword = new SecureString();
spassword.Clear();
foreach (char c in PassWord)
{
spassword.AppendChar(c);
}
PSSnapInException snapInException = null;
Runspace myRunSpace = RunspaceFactory.CreateRunspace(rsConfig);
myRunSpace.Open();
Pipeline pipeLine = myRunSpace.CreatePipeline();
Command myCommand = new Command("New-MailBox");
myCommand.Parameters.Add("Name", Name);
myCommand.Parameters.Add("Alias", Alias);
myCommand.Parameters.Add("UserPrincipalName", PrincipalName);
myCommand.Parameters.Add("Confirm", true);
myCommand.Parameters.Add("SamAccountName", Alias);
myCommand.Parameters.Add("FirstName", FirstName);
myCommand.Parameters.Add("LastName", LastName);
myCommand.Parameters.Add("Password", spassword);
myCommand.Parameters.Add("ResetPasswordOnNextLogon", false);
myCommand.Parameters.Add("OrganizationalUnit", OrganizationalUnit);
pipeLine.Commands.Add(myCommand);
pipeLine.Invoke(); // got an error here
myRunSpace.Dispose();
}
and call it :
Boolean Success = CreateUser("firstname", "lastName", "aliasName", "AAaa12345", "mydomain.com", "mydomain.com/Users");
which I get this error :
Additional information: The term 'New-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.
and another code that I test is:
string userName = "administrator";
string password = "mypass";
System.Security.SecureString securePassword = new System.Security.SecureString();
foreach (char c in password)
{
securePassword.AppendChar(c);
}
PSCredential credential = new PSCredential(userName, securePassword);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri("https://{my server IP}/POWERSHELL/Microsoft.Exchange"),
"http://schemas.microsoft.com/powershell/Microsoft.Exchange",
credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic;
connectionInfo.SkipCACheck = true;
connectionInfo.SkipCNCheck = true;
connectionInfo.MaximumConnectionRedirectionCount = 2;
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runspace.Open();
using (PowerShell powershell = PowerShell.Create())
{
powershell.Runspace = runspace;
//Create the command and add a parameter
powershell.AddCommand("Get-Mailbox");
powershell.AddParameter("RecipientTypeDetails", "UserMailbox");
//Invoke the command and store the results in a PSObject collection
Collection<PSObject> results = powershell.Invoke();
//Iterate through the results and write the DisplayName and PrimarySMTP
//address for each mailbox
foreach (PSObject result in results)
{
Console.WriteLine(
string.Format("Name: { 0}, PrimarySmtpAddress: { 1}",
result.Properties["DisplayName"].Value.ToString(),
result.Properties["PrimarySmtpAddress"].Value.ToString()
));
}
}
}
and I get this error
Additional information: Connecting to remote server {Server IP Address} failed with the following error message : [ClientAccessServer=WIN-FRP2TC5SKRG,BackEndServer=,RequestId=460bc5fe-f809-4454-8472-ada97eacb9fb,TimeStamp=4/6/2016 6:23:28 AM] Access is denied. For more information, see the about_Remote_Troubleshooting Help topic.
I think I gave every permission which is needed to my administrator user and firewall is off but it doesn't work yet.
Any help or hint !!
thanks
Try this:
//Secure String
string pwd = "Password";
char[] cpwd = pwd.ToCharArray();
SecureString ss = new SecureString();
foreach (char c in cpwd)
ss.AppendChar(c);
//URI
Uri connectTo = new Uri("http://exchserver.domain.local/PowerShell");
string schemaURI = "http://schemas.microsoft.com/powershell/Microsoft.Exchange";
//PS Credentials
PSCredential credential = new PSCredential("Domain\\administrator", ss);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(connectTo, schemaURI, credential);
connectionInfo.MaximumConnectionRedirectionCount = 5;
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Kerberos;
Runspace remoteRunspace = RunspaceFactory.CreateRunspace(connectionInfo);
remoteRunspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = remoteRunspace;
ps.Commands.AddCommand("Get-Mailbox");
ps.Commands.AddParameter("Identity","user#domain.local");
foreach (PSObject result in ps.Invoke())
{
Console.WriteLine("{0,-25}{1}", result.Members["DisplayName"].Value,
result.Members["PrimarySMTPAddress"].Value);
}
I unchecked "prefer 32-bit" and changed platform target to x64, the problem was solved.
with the following code :
public class ExchangeShellExecuter
{
public Collection<PSObject> ExecuteCommand(Command command)
{
RunspaceConfiguration runspaceConf = RunspaceConfiguration.Create();
PSSnapInException PSException = null;
PSSnapInInfo info = runspaceConf.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.E2010", out PSException);
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConf);
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.Add(command);
Collection<PSObject> result = pipeline.Invoke();
return result ;
}
}
public class ExchangeShellCommand
{
public Command NewMailBox(string userLogonName,string firstName,string lastName,string password
,string displayName,string organizationUnit = "mydomain.com/Users",
string database = "Mailbox Database 1338667540", bool resetPasswordOnNextLogon = false)
{
try
{
SecureString securePwd = ExchangeShellHelper.StringToSecureString(password);
Command command = new Command("New-Mailbox");
var name = firstName + " " + lastName;
command.Parameters.Add("FirstName", firstName);
command.Parameters.Add("LastName", lastName);
command.Parameters.Add("Name", name);
command.Parameters.Add("Alias", userLogonName);
command.Parameters.Add("database", database);
command.Parameters.Add("Password", securePwd);
command.Parameters.Add("DisplayName", displayName);
command.Parameters.Add("UserPrincipalName", userLogonName+ "#mydomain.com");
command.Parameters.Add("OrganizationalUnit", organizationUnit);
//command.Parameters.Add("ResetPasswordOnNextLogon", resetPasswordOnNextLogon);
return command;
}
catch (Exception)
{
throw;
}
}
public Command AddEmail(string email, string newEmail)
{
try
{
Command command = new Command("Set-mailbox");
command.Parameters.Add("Identity", email);
command.Parameters.Add("EmailAddresses", newEmail);
command.Parameters.Add("EmailAddressPolicyEnabled", false);
return command;
}
catch (Exception)
{
throw;
}
//
}
public Command SetDefaultEmail(string userEmail, string emailToSetAsDefault)
{
try
{
Command command = new Command("Set-mailbox");
command.Parameters.Add("Identity", userEmail);
command.Parameters.Add("PrimarySmtpAddress", emailToSetAsDefault);
return command;
}
catch (Exception)
{
throw;
}
//PrimarySmtpAddress
}
}
and run with :
var addEmailCommand = new ExchangeShellCommand().AddEmail("unos4#mydomain.com","unos.bm65#yahoo.com");
var res2 = new ExchangeShellExecuter().ExecuteCommand(addEmailCommand);
var emailDefaultCommand = new ExchangeShellCommand().AddSetDefaultEmail("unos4#mydomain.com", "unos.bm65#yahoo.com");
var res3 = new ExchangeShellExecuter().ExecuteCommand(emailDefaultCommand);
I'm basically trying to create a console application which executes a given script on a remote machines within a given IP range and stores the results.
This is my code so far but something is going wrong when I try to create a runspace with the WSManConnectionInfo object as arg.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;
using System.Text.RegularExpressions;
using System.ComponentModel;
using System.Security;
namespace GetSysInfo
{
class Program
{
private static string outPath;
static void Main(string[] args)
{
//get script path
Console.WriteLine("Enter full path to script.");
string path = Convert.ToString(Console.ReadLine());
//get IP range
Console.WriteLine("Input start IPv4.");
IPAddress sIP = IPAddress.Parse(Console.ReadLine().ToString());
Console.WriteLine("Input end IPv4.");
IPAddress eIP = IPAddress.Parse(Console.ReadLine().ToString());
//get list of IPs in range
RangeFinder rf = new RangeFinder();
List<string> IPrange = rf.GetIPRangeList(sIP, eIP);
//run script
foreach (var IP in IPrange)
{
try
{
RunScriptRemote(LoadScript(path), IP);
}
catch (Exception e)
{
Console.WriteLine("An error occured" + Environment.NewLine + e.Message);
}
}
}
//script executer
private static void RunScriptRemote(string script, string address)
{
Console.WriteLine("Enter username.");
String username = Console.ReadLine();
Console.WriteLine("Enter password.");
ConsoleKeyInfo key;
SecureString pass = new SecureString();
do
{
key = Console.ReadKey(true);
if (key.Key != ConsoleKey.Backspace && key.Key != ConsoleKey.Enter)
{
pass.AppendChar(key.KeyChar);
Console.Write("*");
}
else
{
if (key.Key == ConsoleKey.Backspace && pass.Length > 0)
{
pass.RemoveAt(pass.Length);
Console.Write("\b \b");
}
else if (key.Key == ConsoleKey.Enter && pass.Length > 0)
{
Console.Write(Environment.NewLine);
pass.MakeReadOnly();
}
}
}
while (key.Key != ConsoleKey.Enter);
PSCredential credential = new PSCredential(username, pass);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri("http://" + address + ":5985/wsman"), "http://schemas.microsoft.com/powershell/Microsoft.PowerShell", credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Negotiate;
connectionInfo.EnableNetworkAccess = true;
Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo);//point of crash
runspace.Open();
//set path to save results
Console.WriteLine("Enter full path to save results. Must be a directory.\nThis can be a local path or a network path.");
Console.WriteLine("In case of a network path the results will be merged automatically");
outPath = Convert.ToString(Console.ReadLine());
runspace.SessionStateProxy.SetVariable("filepath", outPath);
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddScript(script);
ps.Invoke();
}
//Pipeline pipeline = runspace.CreatePipeline();
//pipeline.Commands.AddScript(script);
//pipeline.Invoke();
runspace.Close();
}
//script loader
private static string LoadScript(string filename)
{
try
{
using (StreamReader sr = new StreamReader(filename))
{
StringBuilder fileContents = new StringBuilder();
string curLine;
while ((curLine = sr.ReadLine()) != null)
{
fileContents.Append(curLine + Environment.NewLine);
}
return fileContents.ToString();
}
}
catch (Exception e)
{
return e.Message;
}
}
}
public class RangeFinder
{
public IEnumerable<string> GetIPRange(IPAddress startIP,
IPAddress endIP)
{
uint sIP = ipToUint(startIP.GetAddressBytes());
uint eIP = ipToUint(endIP.GetAddressBytes());
while (sIP <= eIP)
{
yield return new IPAddress(reverseBytesArray(sIP)).ToString();
sIP++;
}
}
public List<string> GetIPRangeList(IPAddress startIP,
IPAddress endIP)
{
uint sIP = ipToUint(startIP.GetAddressBytes());
uint eIP = ipToUint(endIP.GetAddressBytes());
List<string> IPlist = new List<string>();
while (sIP <= eIP)
{
IPlist.Add(new IPAddress(reverseBytesArray(sIP)).ToString());
sIP++;
}
return IPlist;
}
//reverse byte order in array
protected uint reverseBytesArray(uint ip)
{
byte[] bytes = BitConverter.GetBytes(ip);
bytes = bytes.Reverse().ToArray();
return (uint)BitConverter.ToInt32(bytes, 0);
}
//Convert bytes array to 32 bit long value
protected uint ipToUint(byte[] ipBytes)
{
ByteConverter bConvert = new ByteConverter();
uint ipUint = 0;
int shift = 24;
foreach (byte b in ipBytes)
{
if (ipUint == 0)
{
ipUint = (uint)bConvert.ConvertTo(b, typeof(uint)) << shift;
shift -= 8;
continue;
}
if (shift >= 8)
ipUint += (uint)bConvert.ConvertTo(b, typeof(uint)) << shift;
else
ipUint += (uint)bConvert.ConvertTo(b, typeof(uint));
shift -= 8;
}
return ipUint;
}
}
}
I get the following error when trying to run: Common Language Runtime detected an invalid program.
I tried creating a new solution as Google suggested but it's obviously going wrong when creating the runspace.
I'm out of idea's and sources for help, so I'm reaching out to the Stackoverflow community.
Thanks in advance,
X3ntr
EDIT:
I tried cleaning my solution, manually deleting any pbd's, changed the target CPU, turned off code optimalization, allowed unsafe code, rebuilding, creating a new solution,... as suggested in this post.
I followed the advice from this answer: I added a reference to C:\windows\assembly\GAC_MSIL\System.Management.Automation and then ran this command in an elevated powershell: Copy ([PSObject].Assembly.Location) C:\
I am trying to query the users of two different Office 365 accounts simultaneously using C#. It works fine when trying from two Powershell Windows or when connecting and getting users one account after other by code. But not working when doing simultaneously using code.
On checking I found that only one log file is generated when trying from C#. But two different log files are generated when trying from PowerShell window.
Log folder location: %userprofile%\appdata\Local\Microsoft\Office365\Powershell
Which implies that when running from code, it works like running with single PowerShell window even with two runspaces.
Main method code:
Thread t1 = new Thread(() => connectandExec("admin#domain1.onmicrosoft.com", "Pwdd#123"));
Thread t2 = new Thread(() => connectandExec("admin#domain2.onmicrosoft.com", "Pwdd#123"));
t1.Start();
t2.Start();
Method that connects and gets the user:
public static void connectandExec(String userName, String password) {
InitialSessionState iss = InitialSessionState.CreateDefault();
iss.ImportPSModule(new String[] { "MSOnline" });
Runspace runspace = RunspaceFactory.CreateRunspace(iss);
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
Command cmd = new Command("Connect-MsolService");
System.Security.SecureString pwd = new System.Security.SecureString();
foreach (Char c in password.ToCharArray()) {
pwd.AppendChar(c);
}
log("Connecting to : " + userName);
PSCredential pscred = new PSCredential(userName, pwd);
cmd.Parameters.Add("Credential", pscred);
ps.Commands.AddCommand(cmd);
ps.Invoke();
if (ps.Streams.Error.Count > 0) {
log("Error when connecting: " + userName);
foreach (ErrorRecord errRecord in ps.Streams.Error) {
log(userName + errRecord.ToString());
}
} else {
log("Connected to : " + userName);
}
ps.Commands.Clear();
try {
ps.Commands.AddScript("Get-MsolUser -All");
ICollection<PSObject> results = ps.Invoke();
if (ps.Streams.Error.Count > 0) {
log("Error when getting users: " + userName);
foreach (ErrorRecord errRecord in ps.Streams.Error) {
log(userName + errRecord.ToString());
}
} else {
foreach (PSObject obj in results) {
if (obj != null && obj.ToString() != "") {
Object val = obj.Members["UserPrincipalName"].Value;
if (val != null) {
log(userName + ":" + val.ToString());
}
}
}
}
} catch (Exception ex) {
log(userName + ":Exception during getUsers: " + ex.ToString());
}
}
Your code is trying to use threads to do something which should be done in different application domains: "An application domain forms an isolation boundary for security".
The Office 365 library will no doubt use the app domain of the current thread - and you're just using two threads which belong to the same app domain, hence the confusion / failure.