Running powershell through c# recently getting error Access is denied - c#

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

Related

Connecting to Exchange Online with a Certificate and C#

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.

'Unauthorized operation' when executing Powershell.Invoke()

I am running the below code, as referenced from:
https://stackoverflow.com/questions/17067260/invoke-powershell-command-from-c-sharp-with-different-credential
I believe all necessary code is shown below but basically when GetPSResults is called, a PSCredential object is passed in. The issue arises when I try to execute ps.Invoke().
I am running this on localhost, running a Powershell script as a 'generic' user that has been created for use in this program (those credentials are set in the PSCredential object). This user has also been given 'admin' rights on my localhost. That user is trying to retrieve logs from a remote server. Given the error, I am assuming this is some type of permissions issue but I am not extremely familiar with that side of things.
protected override void OnStart(string[] args)
{
GetLogs newGetLogs = new GetLogs();
PSCredential credential = new PSCredential(#"MYDOMAIN\MYUSERID", newGetLogs.GetSecurePassword("TESTPASSWORD"));
Collection<PSObject> returnValues = GetPSResults(credential);
}
public static Collection<PSObject> GetPSResults(PSCredential credential, bool throwErrors = true)
{
Collection<PSObject> toReturn = new Collection<PSObject>();
WSManConnectionInfo connectionInfo = new WSManConnectionInfo();
connectionInfo.Credential = credential;
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runspace.Open();
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddCommand("get-eventlog");
ps.AddParameter("ComputerName", "MYSERVERNAME");
ps.AddParameter("LogName", "MYLOGNAME");
ps.AddParameter("EntryType", "Error");
ps.AddParameter("Newest", 20);
toReturn = ps.Invoke();
if (throwErrors)
{
if (ps.HadErrors)
{
throw ps.Streams.Error.ElementAt(0).Exception;
}
}
}
runspace.Close();
}
return toReturn;
}
public SecureString GetSecurePassword(string password)
{
var securePassword = new SecureString();
foreach (var c in password)
{
securePassword.AppendChar(c);
}
return securePassword;
}
I am getting this error:
System.Management.Automation.RemoteException: 'Attempted to perform an unauthorized operation.'
Within Visual Studio, I can drill down into the error to see the below image. I see that there are multiple mentions of Registry in these messages... is that a sign of some sort of permissions issue?
I have seen some mention of WinRM but I am not completely clear on the type of updates I would need to make... either in the code or on my user account(s).

Is it possible to import the AD module into an existing Exchange runspace using c#/powershell?

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.

Execute PowerShell Script from C# MVC Web Application

I need to execute a powershell script from my asp.net MVC Web application. My requirement is to create site collections dynamically. I have the script for it and it works perfectly.There are no arguments which are to be passed to the script. The code which I have been using has been displayed below:
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
Pipeline pipeline = runspace.CreatePipeline();
//Here's how you add a new script with arguments
Command myCommand = new Command(scriptfiellocation);
pipeline.Commands.Add(myCommand);
pipeline.Commands.Add("Out-String");
// Execute PowerShell script
var result = pipeline.Invoke();
On executing the code, when I check the count of variable result it gives the count as 1. However on checking my site, there is no site collection that has been created. I am not able to identify where I am going wrong as there is no run time error and the Invoke command also seems to be running properly.
Could anyone tell me where I might be going haywire ? Considering that the PowerShell script works perfectly when running through Management shell.
I had to forego the pipeline approach as I was not able to figure out what the issue was. Also another problem with that approach is that it threw the error: "Get-SPWbTemplate is not recognized as an cmdlet". The following code worked perfectly fine for me and created the required site collections:
PowerShell powershell = PowerShell.Create();
//RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
//Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration)
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
//RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
//scriptInvoker.Invoke("Set-ExecutionPolicy Unrestricted");
powershell.Runspace = runspace;
//powershell.Commands.AddScript("Add-PsSnapin Microsoft.SharePoint.PowerShell");
System.IO.StreamReader sr = new System.IO.StreamReader(scriptfilepath);
powershell.AddScript(sr.ReadToEnd());
//powershell.AddCommand("Out-String");
var results = powershell.Invoke();
if (powershell.Streams.Error.Count > 0)
{
// error records were written to the error stream.
// do something with the items found.
}
}
Also there was no requirement to set the execution policy.
well don't know if its help but i never use pipeline to run Command shell not sure how that work.
But here a quick example
Runspace RS = RunspaceFactory.CreateRunspace(myConnection);
PowerShell PS = PowerShell.Create();
PSCommand PScmd = new PSCommand();
string cmdStr = "Enable-Mailbox -Identity " + username + " -Database DB01 -Alias " + aliasexample;
PScmd.AddScript(cmdStr);
try
{
RS.Open();
PS.Runspace = RS;
PS.Commands = PScmd;
PS.Invoke();
}
catch (Exception ex)
{
ex.ToString();
}
finally
{
RS.Dispose();
RS = null;
PS.Dispose();
PS = null;
}
with the try catch you can catch the error with debugging if something goes wrong.
If i remember correctly i had to put ACL.exe for permission to file system so i can execute the commandshell you can do a quick search on google for it.
Hope this help.

Error executing remote Power Shell Script (Exchange 2010)

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.

Categories