Anyone using c# to connect to Exchange online? I am running into a problem that I can't seem to make progress on.
I have the following snippet of code trying to connect to exchange online:
public Runspace getSpace() {
String schema = "http://schemas.microsoft.com/powershell/Microsoft.Exchange";
Uri server = new Uri("https://outlook.office365.com/PowerShell");
string certificateThumbprint = "thumbprint";
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(server, schema, certificateThumbprint);
Runspace rsp = RunspaceFactory.CreateRunspace(connectionInfo );
rsp.Open();
return rsp;
}
Which results in:
System.Management.Automation.Remoting.PSRemotingTransportException: Connecting to remote server outlook.office365.com failed with the following error message : For more information, see the about_Remote_Troubleshooting Help topic.
at System.Management.Automation.Runspaces.AsyncResult.EndInvoke()
I know the cert is working because when I do the following I am able to connect:
Connect-ExchangeOnline -AppId "application guid" -Organization "tenent.onmicrosoft.com" -CertificateThumbprint "thumbprint"
Any ideas on what I could try next? Thanks!
For connecting to O365 with certificate thumbprint I use the following code, which works:
using (Runspace remoteRunspace = RunspaceFactory.CreateRunspace()){
remoteRunspace.Open();
using (PowerShell powershell = PowerShell.Create())
{
powershell.Runspace = remoteRunspace;
powershell.AddCommand("Import-Module");
powershell.AddParameter("Name", "ExchangeOnlineManagement");
powershell.Invoke();
powershell.Commands.Clear();
powershell.AddCommand("Connect-ExchangeOnline");
powershell.AddParameter("AppId", "");
powershell.AddParameter("CertificateThumbprint", "");
powershell.AddParameter("Organization", "");
powershell.Invoke();
powershell.Commands.Clear();
powershell.AddCommand("Get-EXOMailbox");
powershell.AddParameter("Identity", "");
powershell.Invoke();
Collection<PSObject> results = powershell.Invoke();
powershell.Commands.Clear();
powershell.AddCommand("Disconnect-ExchangeOnline");
powershell.Invoke();
}
remoteRunspace.Close();}
Consider using EWS or Graph API instead.
Related
This program has been running successfully for over 4 years. Just recently (8/4/2022), the pscommand version of the program has failed. We are trying to figure out what changed.
We are getting the error "Connecting to remote server outlook.office365.com failed with the following error message : Access is denied. For more information, see the about_Remote_Troubleshooting Help topic." ONLY when dealing with pssession / PSCommands.
Code:
public Collection<PSObject> runPSCommand(PSCommand _command, string _commandName, PSCommand _secondCommand = null)
{
PSCredential credential = new PSCredential(this.emailLogin, this.emailPass);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri(this.WSManConnectionURI), this.MSSchema, credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic;
try
{
using (Runspace runspace = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(connectionInfo))
{
PowerShell powershell = PowerShell.Create();
PSCommand remoteSigned = new PSCommand();
runspace.Open();
powershell.Runspace = runspace;
wsmanconnectionURI: https://outlook.office365.com/PowerShell-LiveID
MSSchema: http://schemas.microsoft.com/powershell/Microsoft.Exchange
Fails at runspace.Open().
We run multiple different type of commands on this program (Connect-ExchangeOnline, Connect-AzureAD, Connect-MSOLService) that are ALL working, it is JUST running the PSCommands that fail.
Tried with powershell as well and it is also failing:
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Authentication Basic -AllowRedirection -Credential Get-Credential
With error: "New-PSSession : [outlook.office365.com] Connecting to remote server outlook.office365.com failed with the following error message : Access is denied.
For more information, see the about_Remote_Troubleshooting Help topic."
Again, this was working for multiple years and JUST started failing. We checked passwords, check logins, tried multiple users.
Thank you for any assistance.
So, Darin lead me to the correct answer, and it was probably deprecation per https://learn.microsoft.com/en-us/powershell/exchange/exchange-online-powershell-v2?view=exchange-ps. Apparently PSSession was deprecated, and we had to move the commands over to exchange online.
For those that it might help, our new code is:
public Collection<PSObject> runExchangeCommand(Command _command, string _commandName)
{
InitialSessionState initialSession = InitialSessionState.CreateDefault();
initialSession.ImportPSModule(new[] { "MSOnline" });
//initialSession.ImportPSModule(new[] { "ExchangeOnlineManagement" });
PSCredential credential = new PSCredential(this.emailLogin, this.emailPass);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri("https://outlook.office365.com/PowerShell-LiveID"), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic;
connectionInfo.MaximumConnectionRedirectionCount = 10;
Command connectCommand = new Command("Connect-ExchangeOnline");
connectCommand.Parameters.Add(new CommandParameter("Credential", credential));
try
{
using (Runspace runspace = RunspaceFactory.CreateRunspace(initialSession))
{
runspace.Open();
Pipeline pipe = runspace.CreatePipeline();
pipe.Commands.Add(connectCommand);
var results = pipe.Invoke();
var error = pipe.Error.ReadToEnd();
if (error.Count > 0)
{
foreach (PSObject err in error)
{
//more logging not sharing that code
}
}
pipe = runspace.CreatePipeline();
pipe.Commands.Add(_command);
var results2 = pipe.Invoke();
var error2 = pipe.Error.ReadToEnd();
if (error2.Count > 0)
{
foreach (PSObject er in error2)
{
//more logging, not sharing that code
}
}
return results2;
}
}
I left out logging / catch code because that included some identifying things.
As for creating the command, it boils down to what command you want to run. Here is one example of the runExchangeCommand:
public bool removeCalendarInvites(string email)
{
Command removeMeetings = new Command("Remove-CalendarEvents");
removeMeetings.Parameters.Add("Identity", email);
removeMeetings.Parameters.Add("CancelOrganizedMeetings");
removeMeetings.Parameters.Add("QueryWindowInDays", 365);
removeMeetings.Parameters.Add("Confirm", false);
Collection<PSObject> results = runExchangeCommand(removeMeetings, "removeCalendarEvents");
The command name is only used for logging / identifying what errors were caught in.
Thanks for looking
Can we check Dns Forwarders configured on each domain controller in C# .NET? Could not find any classes supporting this.
Please assist.
Using WSManConnectionInfo and Runspace, I could achieve fetching the desired details.
WSManConnectionInfo connInfo = new WSManConnectionInfo(new Uri("http://ServerName:5985/wsman"));
Collection<PSObject> output = null;
string command = "Get-DnsServerForwarder";
using (Runspace remoteRS = RunspaceFactory.CreateRunspace(connInfo))
{
remoteRS.Open();
using (var pShell = PowerShell.Create())
{
pShell.Commands.AddCommand(command);
output = pShell.Invoke();
}
}
I am creating an exchange user (new-mailbox) and then setting some AD parameters on them after the user is created in the same runspace with commands that will not run in the Exchange runspace unless import-module 'activedirecty' is ran. Is there a way to import the module after the runspace is created as I can do with the Powershell prompt?
inside the same runspace session I want to run:
new-mailbox
set-mailbox
set-user
set-aduser
The last one is what requires me to import the AD module I can successfully run the command inside of Powershell directly, but can't seem to figure out how to add the module mid runspace session? I'd tried
powershell.AddParameter("import-module -name 'activedirectory'; set-aduser xxxx")
and
powershell.AddParameter("import-module -name 'activedirectory'")
powershell.AddParameter("set-aduser xxxx")
and
powershell.AddScript("import-module -name 'activedirectory'; set-aduser xxxx")
This works below
public void SetPasswordNeverExpiresProperty(bool PasswordNeverExpires, string alias)
{
string dn = "CN=xxx,OU=xxx,OU=xxx=xxx=xxx=xxx,DC=xx,DC=xx,DC=xxx,DC=xxx"
DirectoryEntry objRootDSE = new DirectoryEntry("LDAP://" + dn);
ArrayList props = new ArrayList();
int NON_EXPIRE_FLAG = 0x10000;
int EXPIRE_FLAG = 0x0200;
int valBefore = (int) objRootDSE.Properties["userAccountControl"].Value;
objRootDSE.Properties["userAccountControl"].Value = EXPIRE_FLAG;
objRootDSE.CommitChanges();
string valAfter = objRootDSE.Properties["userAccountControl"].Value.ToString();`
And I'm out of guesses, any help would be appreciated.
PSCredential ExchangeCredential = new PSCredential(PSDomain + #"\" + PSUsername, PSpwd);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri("xxxxxx/powershell"), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", ExchangeCredential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Kerberos;
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
PowerShell powershell = PowerShell.Create();
if (runspace.RunspaceStateInfo.State == RunspaceState.Opened)
{
// do nothing
}
else
{
runspace.Open();
powershell.Runspace = runspace;
}
try
{
psobjs = powershell.Invoke();
}
catch (Exception ex)
{
result = "Failed: " + ex.Message;
}
powershell.Commands.Clear();
}
I'll sum up my comments in an answer, since it seems I was unexpectedly helpful :)
I also had found that you can't use Import-Module when using remote PowerShell like that. It's kind of annoying, but such is life.
Years ago, I implemented an automatic account creation service in our environment for AD and Exchange 2010. I found I had to do the AD account manipulation with DirectoryEntry and then only the Exchange stuff with PowerShell.
The problem is making sure that both things happen on the same domain controller so you don't run into replication problems.
So you have two options: Use New-Mailbox to create the mailbox and AD account in one shot. As you pointed out, the OriginatingServer property of the result has the domain controller. But there is also a DistinguishedName property there too! (I just found this when you mentioned the server property) Then you can create a DirectoryEntry object against the same domain controller like this:
new DirectoryEntry($"LDAP://{domainController}/{distinguishedName}")
Or, what I did (I think because I didn't realize at the time that I could get the DC from the result of New-Mailbox), is create the AD object first with DirectoryEntry, pull the domain controller it got created on from .Options.GetCurrentServerName(), then pass that in the DomainController parameter to Enable-Mailbox.
I'm trying to get the lower store from a 2010 Exchange server, and the function will run in a WCF container.
The problem I'm facing is that I'm unable to run multiple PowerShell commands in the pipeline.
I've tried the following (based on this, how to invoke the powershell command with "format-list" and "out-file" pipeline from c#?):
string strCommand = #"Get-MailboxDatabase -Status | select ServerName,Name,DatabaseSize | Sort-Object DatabaseSize";
string CommandLine = string.Format("&{{{0}}}", strCommand);
pipeLine.Commands.AddScript(CommandLine);
But I get:
Unhandled Exception: System.Management.Automation.RemoteException: Script block literals are not allowed in restricted language mode or a Data section.
Also I tried,
Command getMailbox = new Command("Get-MailboxDatabase");
getMailbox.Parameters.Add("Status", null);
Command sort = new Command("Sort-Object");
pipeLine.Commands.Add(getMailbox);
pipeLine.Commands.Add(sort);
Collection<PSObject> commandResults = pipeLine.Invoke();
But not luck:
Unhandled Exception: System.Management.Automation.RemoteException: The term 'Sort-Object' is not recognized as the name of a cmdlet
I wonder if I should use multiple pipelines (one pipeline per cmdlet), but I am not sure.
It sounds like the problem is the runspace. If that's an Exchange server, and you're running that in the remote management session provided by Exchange, the only thing you can do in that session is run the Exchange cmdlets. The Select-Object and Sort-Object cmdlets and other PowerShell language elements just aren't there to use.
Considering that Sort-Object is a command which is not recognized by the schema named 'http://schemas.microsoft.com/powershell/Microsoft.Exchange" then I proceed to develop a function using Snap-Ins and it's working fine.
Notice I'm taking the first database because the default sort mode is ascending. Also I'd like to comment that if you compile on Framework 4.0 you're going to get a "Value cannot be null error message" so you have to change to 3.5.
Keep in mind that it is being used by a WCF Service so no problem with Snap-Ins. If you like to use it on any other application, like a console-based application then you should install EMS 2010 on that computer.
This function basically execute the following PowerShell command, Get-MailboxDatabase -Status | Sort-Object DatabaseSize
private static string getLowServerStoreDN_SnapIn(string ExchangeSite)
{
string strResult = string.Empty;
RunspaceConfiguration rsConfig = RunspaceConfiguration.Create();
PSSnapInException snapInException = null;
PSSnapInInfo info = rsConfig.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.E2010", out snapInException);
Runspace runspace = RunspaceFactory.CreateRunspace(rsConfig);
try
{
runspace.Open();
Command getMailbox = new Command("Get-MailboxDatabase");
getMailbox.Parameters.Add(new CommandParameter("Status", null));
Command sort = new Command("Sort-Object");
sort.Parameters.Add("Property", "DatabaseSize");
Pipeline commandPipeLine = runspace.CreatePipeline();
commandPipeLine.Commands.Add(getMailbox);
commandPipeLine.Commands.Add(sort);
Collection<PSObject> getmailboxResults = commandPipeLine.Invoke();
if (getmailboxResults.Count > 0)
{
PSObject getMailboxResult = getmailboxResults[0];
strResult = getMailboxResult.Properties["Name"].Value.ToString();
//foreach (PSObject getMailboxResult in getmailboxResults)
//{
// strResult = getMailboxResult.Properties["Name"].Value.ToString();
//}
}
}
catch (ApplicationException e)
{
//Console.WriteLine(e.Message);
throw new FaultException("function getLowServerStoreDN_SnapIn(" + ExchangeSite + "): " + e.Message,
FaultCode.CreateReceiverFaultCode("BadExchangeServer", "http://example.com"));
}
return strResult;
}
I wrote a very small application, which access the Remote Power Shell of Exchanger Server 2010 SP1 and execute some scripts. Here is the sample code. Everything is in try and catch block.
string insecurePassword = "mypassword";
SecureString securePassword = new SecureString();
foreach (char passChar in insecurePassword.ToCharArray())
{
securePassword.AppendChar(passChar);
}
PSCredential credential = new PSCredential("mydomain\\administrator", securePassword);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri("http://exchange2010.domain.com/powershell?serializationLevel=Full"), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Kerberos;
Runspace runspace = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(connectionInfo);
PowerShell powershell = PowerShell.Create();
PSCommand command = new PSCommand();
ICollection<System.Management.Automation.PSObject> results;
//The command I want to execute is Set-MailContact with some parameters.
command.AddCommand("Set-MailContact");
command.AddParameter("Identity", "SomeIdentityOfContact");
command.AddParameter("EmailAddressPolicyEnabled", false);
command.AddParameter("PrimarySmtpAddress", "myEmailAddress#domain.com");
command.AddParameter("Confirm", false);
command.AddParameter("Force", true);
powershell.Commands = command;
// open the remote runspace
runspace.Open();
// associate the runspace with powershell
powershell.Runspace = runspace;
// invoke the powershell to obtain the results
results = powershell.Invoke();
I am trying to set PrimarySmtpAddress of a MailContact, but for some reasons I am getting the following exception:
System.Management.Automation.RemoteException: Cannot process argument transformation on parameter 'PrimarySmtpAddress'. Cannot convert value "SMTP:myEmailAddress#domain.com" to type "Microsoft.Exchange.Data.SmtpAddress"
I think its must be due to serialization/de-serialization. Does someone have any idea on how to correctly pass the email address's value?
Any hint help will be highly appreciated!
I think you are confusing smtp server address with email address, try to pass something like smtp.yourcomapnydomain.com instead of such email address and test again.
Try leaving off the SMTP: qualifier. That's already implicit in the -primarySMTPAddress parameter.