I have a problem with sequentially invoking multiple powershell commands on HA Exchange environment (on single development host everything work fine). As long as it was about only Exchange commands I used workaround by piping multiple commands (execution was on the same node) everything was fine. My code:
string casUri = "cas1.srv.net/Powershell?serializationLevel=Full";
string credentialUserName = "User";
string credentialUserPassword = "secret";
string newOU = "thenewou";
string baseOU = "OU=root,DC=srv,DC=net";
System.Security.SecureString passwd = new System.Security.SecureString();
foreach (char c in credentialUserPassword) {
passwd.AppendChar(c);
}
PSCredential credential = new PSCredential(credentialUserName, passwd);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(
new Uri(casUri),
"http://schemas.microsoft.com/powershell/Microsoft.Exchange",
credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Default;
runspacePool = RunspaceFactory.CreateRunspacePool(1, 5, connectionInfo);
runspacePool.Open();
powershell = PowerShell.Create();
Command command;
command = new PSCommand();
command.AddCommand("New-ADOrganizationalUnit");
command.AddParameter("Path", baseOU);
command.AddParameter("Name", newOu);
command.AddParameter("DisplayName", "My new OU");
powershell.Commands = command;
powershell.Invoke();
// ... another AD commands: Set-ADForest, New-AcceptedDomain (work fine)
// v--- trouble
command = new PSCommand();
command.AddCommand("New-GlobalAddressList");
command.AddParameter("Name", "GAL.Name");
command.AddParameter("RecipientContainer", newOu + "," baseOU);
powershell.Commands = command;
powershell.Invoke();
Problem doesn't exists in single host environment.
On HA like this, another commands run on different hosts:
[myApp]---[CAS load balancer]---[CAS-1..CAS-N]---[EX-1..EX-N]
This cause an exception:
Active Directory operation failed on DC2.srv.net. This error is not retriable.
Additional information: The name reference is invalid.
This may be caused by replication latency between Active Directory domain controllers.
Active directory response: 000020B5: AtrErr: DSID-03152804, #1:
0: 000020B5: DSID-03152804, problem 1005 (CONSTRAINT_ATT_TYPE), data 0, Att 9b7f0292 (msExchSearchBase)
I have no idea how to force persistent connection with single remote shell for few commands.
Have you got any solution ?
Have you try this?
command.AddParameter("DomainController", "Your Domain Contorller FQDN");
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
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.
The wpf app is working well on a computer where it was developed. The files from bin/Release are used to run the app.
There is System.Management.Automation.dll used to work with powershell remotely on the Exchange Server 2010 to create new mailboxes. It's setup with Local = True so the dll is added into the files in the bin/release.
However, if the same files from bin/release are copied on another computer then all functionalities on Active Directory are working well except the mail-box creation part as it gives Error: Common Language Runtime detected an invalid program.
I could find a few advises as to uncheck 'Optimize Code' and rebuild it in VS but it seems as it's not helping.
As I said before all is working well on developer's computer. So, I run the app on another computer where VS 2013 was also installed and got an error:"The term 'Enable-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."
Here is the code:
public static class ManageMailBox
{
public static bool CreateMailBox(string strUserID, string admin_user, string pword) //, string str_upn)
{
try
{
string strExchangeServer = Constants.ExchangeServer;
Uri uri = new Uri(#"http://" + strExchangeServer + #"/powershell?serializationLevel=Full"); // orig works
/// must pass secure string
char[] passwordChars = pword.ToCharArray();
SecureString password = new SecureString();
foreach (char c in passwordChars)
{
password.AppendChar(c);
}
PSCredential credential = new PSCredential("DOMAIN\\" + admin_user, password);
Runspace runspace = RunspaceFactory.CreateRunspace();
PowerShell powershell = PowerShell.Create();
PSCommand command = new PSCommand();
command.AddCommand("New-PSSession");
command.AddParameter("ConfigurationName", "Microsoft.Exchange");
command.AddParameter("ConnectionUri", uri);
command.AddParameter("Credential", credential);
command.AddParameter("Authentication", "Default");
PSSessionOption sessionOption = new PSSessionOption();
sessionOption.SkipCACheck = true;
sessionOption.SkipCNCheck = true;
sessionOption.SkipRevocationCheck = true;
command.AddParameter("SessionOption", sessionOption);
powershell.Commands = command;
try
{
// open the remote runspace
runspace.Open();
// associate the runspace with powershell
powershell.Runspace = runspace;
// invoke the powershell to obtain the results
Collection<PSSession> result = powershell.Invoke<PSSession>();
foreach (ErrorRecord current in powershell.Streams.Error)
{
throw new Exception("Exception: " + current.Exception.ToString());
throw new Exception("Inner Exception: " + current.Exception.InnerException);
}
if (result.Count != 1)
throw new Exception("Unexpected number of Remote Runspace connections returned.");
// Set the runspace as a local variable on the runspace
powershell = PowerShell.Create();
command = new PSCommand();
command.AddCommand("Set-Variable");
command.AddParameter("Name", "ra");
command.AddParameter("Value", result[0]);
powershell.Commands = command;
powershell.Runspace = runspace;
powershell.Invoke();
// First import the cmdlets in the current runspace (using Import-PSSession)
powershell = PowerShell.Create();
command = new PSCommand();
//command.AddScript("Set-ExecutionPolicy -Unrestricted");
command.AddScript("Import-PSSession -Session $ra");
powershell.Commands = command;
powershell.Runspace = runspace;
powershell.Invoke();
// Now run get-ExchangeServer
powershell = PowerShell.Create();
command = new PSCommand();
command.AddCommand("Enable-Mailbox");
command.AddParameter("Identity", strUserID);
command.AddParameter("Alias", strUserID);
command.AddParameter("Database", "IAP Mailbox Database 0948752629");
powershell.Commands = command;
powershell.Runspace = runspace;
powershell.Invoke(); // ERROR !!! The term 'Enable-Mailbox' is not recognized as the name of a cmdlet, function
return true;
}
catch(Exception ex) {
throw new Exception(ex.Message.ToString()); }
finally
{
// dispose the runspace and enable garbage collection
runspace.Dispose();
runspace = null;
// Finally dispose the powershell and set all variables to null to free
// up any resources.
powershell.Dispose();
powershell = null;
}
}
catch (Exception argex)
{
throw new ArgumentException(argex.Message.ToString());
}
}
}
It seems like this could be a few things, it is hard to know what is the cause in your scenario. Firstly i would try running as Admin, failing that i would try these things:
reinstall the .net framework (make sure the version is correct)
Common Language Runtime detected an invalid program in Visual Studio
For C# projects go to the project properties and under the Build tab un-check "Optimize Code".
Common Language Runtime detected an invalid program?
Try enabling 32-bit applications in your application pool advanced settings.
or
I finally managed to solve this issue. I unchecked code optimization
in C# Express and that solved the issues.
InvalidProgramException / Common Language Runtime detected an invalid program
It's solved. It was a matter of permissions for PowerShell script to use cmdlet commands remotely.
Edit: while it could be the case why mail-box could not be created this error was on another computer with a different configuration.
On the computer where we received the “Common language runtime error” the problem was the older versuion of PowerShell.
I experienced this in a Xamarin Forms app because I had a button click event on an event that didn't exist. Removing the click event from the button resolved the issue.
I had this issue, published with "Optimize Code" checked in Project > Properties > Build and worked fine then re-published and "Optimize Code" unchecked and work OK again.
I'm writing a program in C# that adds a picture to an Active Directory user object (in the ThumbnailPhoto attribute). The program runs on another PC than the Exchange Server 2010. Using PowerShell remoting I'm able to, for example, enable a mailbox for the AD user.
When I execute the following commands in PowerShell (on another PC than the Exchange Server), it works perfectly:
enter-pssession exchange_server_fqdn
$cred = get-credential ( domain\administrator , **** )
$sess = new-pssession -configurationName Microsoft.Exchange -ConnectionUri http://exchange_server_fqdn/PowerShell/ -Authentication Kerberos -Credential $cred
import-pssession $sess
Import-RecipientDataProperty -Identity ADusername -Picture -FileData ([Byte[]]$(Get-Content -Path 'C:\\person.jpg' -Encoding Byte -ReadCount 0))
remove-pssession $sess
But I need to execute the commands from C#. The following code runs (no exception is thrown), but the image isn't added to the AD user object:
string username = "xxx";
string password = "yyy";
string exchangeServer = "zzz"; // FQDN of Exchange Server 2010
string liveIdconnectionUri = "http://" + exchangeServer + "/Powershell?serializationLevel=Full";
// credentials
SecureString passwordSecureString = new SecureString();
foreach (char c in password) passwordSecureString.AppendChar(c);
PSCredential credential = new PSCredential(username, passwordSecureString);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(
new Uri(liveIdconnectionUri),
"http://schemas.microsoft.com/powershell/Microsoft.Exchange",
credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Kerberos;
PowerShell powershell = PowerShell.Create();
Runspace runspace = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(connectionInfo);
runspace.Open();
powershell.Runspace = runspace;
PSCommand command = new PSCommand();
// Add ThumbnailPhoto attribute to Active Directory user
// This command works when executed on the Exchange Server 2010 in the Exchange Management Shell,
// But it doens't work when executed in C#.
command.AddScript("Import-RecipientDataProperty -Identity ADusername -Picture -FileData ([Byte[]]$(Get-Content -Path 'C:\\person.jpg' -Encoding Byte -ReadCount 0))");
powershell.Commands = command;
try {
Collection<PSObject> commandResults = powershell.Invoke<PSObject>();
foreach (PSObject result in commandResults) Console.WriteLine(result.ToString());
}
catch (Exception ex) {
Console.WriteLine(ex.Message);
}
The only problem in the code is the line with command.AddScript. If I replace that line with, for example, this:
command.AddCommand("Get-Mailbox");
command.AddParameter("Identity", "ADusername");
... then it does work. Enabling a mailbox also works.
How can I execute the command to add an image to an Active Directory user object from C# (using PowerShell remoting)?
Working code based on accepted answer:
DirectoryEntry container = new DirectoryEntry(LDAP_URI + USERS_DIR);
DirectoryEntry user = container.Children.Add("cn=" + username, "user");
// Set other property's of the user object:
//// user.Properties ["xxx"].Value = "yyy";
//// ...
byte [] buffer;
FileStream fileStream = new FileStream(#"c:\photo.jpg", FileMode.Open, FileAccess.Read);
try {
int length = (int) fileStream.Length; // get file length
buffer = new byte [length]; // create buffer
int count; // actual number of bytes read
int sum = 0; // total number of bytes read
// read until Read method returns 0 (end of the stream has been reached)
while ((count = fileStream.Read(buffer, sum, length - sum)) > 0)
sum += count; // sum is a buffer offset for next reading
}
finally {
fileStream.Close();
}
user.Properties ["thumbnailPhoto"].Value = buffer;
user.CommitChanges();
I would take a different approach:
byte[] pictureBytes = //Use a FileStream to read the data
command.AddCommand("Import-RecipientPropertyData");
command.AddParameter("Identity", "fooUser");
command.AddParameter("Picture", "$true");
command.AddParameter("Encoding", "Byte");
command.AddParameter("ReadCount", 0);
command.AddParameter(FileData, pictureBytes);
Alternatively, you could just read the picture in to your pictureBytes and use ADSI (DirectoryEntry) to write directly to the thumbnailPhoto attribute. Just make sure that pictureBytes is <= 10KB as that's the max on the attribute in AD.
This sounds like an authentication problem. Even though you're running the command on the Exchange server, it's actually being executed against Active Directory, and there's no guarantee which domain controller it will execute against. This is the double-hop scenario, which can be solved by using CredSSP in your WS-Man remoting session.
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.