none
Microsoft.Exchange.Data.Directory.Management.DistributionGroup RRS feed

  • Question

  • I have written logic to create an instance of DistributionGroup from the Exchange server library.  How do I save it to the Global Address List?  All of the DistributionGroup references online point to Exchange Power Shell examples, and the Microsoft.Exchange.Data.Directory.Management API reference contains no examples whatsoever.
    Thursday, September 5, 2013 10:37 PM

Answers

  • After some trial and error, this is the code I used to run the Exchange PowerShell scripts required to query and update DistributionGroup objects:

    RunInExchange("New-DistributionGroup 'MyGroup'; Add-DistributionGroupMember -Identity 'MyGroup' -Member 'me@mydomain.com';", "admin@mydomain.com", "password", "exchange.mydomain.com");  

    public static string RunInExchange(string cmd, string adminUserEmail, string password, string exchangeServer)
    {
        string cmdResult = "";
        System.Uri uri = new Uri("http://" + exchangeServer 
            + "/powershell?serializationLevel=Full");
        System.Security.SecureString securePassword = String2SecureString(password);
        PSCredential creds = new PSCredential(adminUserEmail, securePassword);
        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", creds);
        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
        {
            // Associate the Runspace with PowerShell and invoke it
            runspace.Open();
            powershell.Runspace = runspace;
            Collection<PSSession> result = powershell.Invoke<PSSession>();
            foreach (ErrorRecord current in powershell.Streams.Error)
            {
                Console.WriteLine("Exception: " + current.Exception.ToString());
                Console.WriteLine("Inner Exception: " + current.Exception.InnerException);
            }
            if (result.Count != 1)
                throw new Exception("Unexpected number of Remote Runspace connections returned.");
            // Set the Exchange Runspace as a local variable on the PS 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();
            // Import the cmdlets in the current Runspace (using Import-PSSession)
            powershell = PowerShell.Create();
            command = new PSCommand();
            command.AddScript("Import-PSSession -Session $ra");
            powershell.Commands = command;
            powershell.Runspace = runspace;
            powershell.Invoke();
            // Now the Exchange Server Script
            powershell = PowerShell.Create();
            command = new PSCommand();
            command.AddScript(cmd);
            command.AddCommand("Out-String");
            powershell.Commands = command;
            powershell.Runspace = runspace;
            Collection<PSObject> results = new Collection<PSObject>();
            results = powershell.Invoke();
            foreach (PSObject PSresult in results)
            {
                cmdResult += PSresult.ToString() + "\r\n";
            }
        }
        finally
        {
            runspace.Dispose();
            runspace = null;
            powershell.Dispose();
            powershell = null;
        }
        return cmdResult;
    }
    private static SecureString String2SecureString(string password)
    {
        SecureString remotePassword = new SecureString();
        for (int i = 0; i < password.Length; i++)
            remotePassword.AppendChar(password[i]);
        return remotePassword;
    }

    Thanks!


    • Marked as answer by CZahrobsky Friday, October 18, 2013 12:14 AM
    • Edited by CZahrobsky Friday, October 18, 2013 12:31 AM Added String2SecureString
    Friday, October 18, 2013 12:13 AM

All replies

  • Microsoft.Exchange.Data.Directory.Management is just a type that is used within the Exchange Management Shell and there is no separate API where you can use this Type outside of this. If you want to create a Distribution list from C# then the only supported way is using Remote Powershell eg something like

                    String AdminUserName = unUserName;
                    String Password = pnPassword;
    
                    System.Security.SecureString secureString = new System.Security.SecureString();
                    foreach (char c in Password)
                        secureString.AppendChar(c);
                    PSCredential credential = new PSCredential(AdminUserName, secureString);
                    WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri("https://" + PSServerName), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", credential);
                    connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic;
                    connectionInfo.SkipCACheck = true;
                    connectionInfo.SkipCNCheck = true;
    
                    connectionInfo.MaximumConnectionRedirectionCount = 4;
                    runspace = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(connectionInfo);
                    runspace.Open();
                    Pipeline plPileLine = runspace.CreatePipeline();
                    Command newDG = new Command("New-DistributionGroup");
                    newDG.Parameters.Add(new CommandParameter("Name", "Test-DL"));
                    newDG.Parameters.Add(new CommandParameter("Members", new String[] { "glen@domain.com", "fred@domain.com" }));
                    plPileLine.Commands.Add(newDG);
                    Collection<PSObject> RsResultsresults = plPileLine.Invoke();
                    if (plPileLine.Error.Count == 1)
                    {
    
                        var error = plPileLine.Error.Read() as Collection<ErrorRecord>;
                        if (error != null)
                        {
                            foreach (ErrorRecord er in error)
                            {
                            }
                        }
    
                    }
                    plPileLine.Stop();
    Cheers
    Glen

    Friday, September 6, 2013 1:24 AM
  • Using your code, I cannot connect.  It fails at runspace.Open().  I provided user name and password for accounts through which I can connect via remote desktop and open an Exchange PowerShell prompt, but I keep getting the following error:

    ## Connecting to remote server failed with 
    ## the following error message : 
    ## The client cannot connect to the destination 
    ## specified in the request. Verify that the 
    ## service on the destination is running and is 
    ## accepting requests. Consult the logs and 
    ## documentation for the WS-Management service 
    ## running on the destination, most commonly 
    ## IIS or WinRM. If the destination is the WinRM 
    ## service, run the following command on the 
    ## destination to analyze and configure the 
    ## WinRM service: "winrm quickconfig".  
    ## For more information, see the 
    ## about_Remote_Troubleshooting Help topic.

    "Winrm quickconfig" shows WinRM is already setup to receive requests for remote management.  My WinRM config has these settings:

    <cfg:Config xml:lang="en-US" xmlns:cfg="http://schemas.microsoft.com/wbem/wsman/1/config">
        <cfg:MaxEnvelopeSizekb>150</cfg:MaxEnvelopeSizekb>
        <cfg:MaxTimeoutms>60000</cfg:MaxTimeoutms>
        <cfg:MaxBatchItems>32000</cfg:MaxBatchItems>
    <cfg:MaxProviderRequests>4294967295</cfg:MaxProviderRequests>
        <cfg:Client>
            <cfg:NetworkDelayms>5000</cfg:NetworkDelayms>
            <cfg:URLPrefix>wsman</cfg:URLPrefix>
            <cfg:AllowUnencrypted>false</cfg:AllowUnencrypted>
            <cfg:Auth>
                <cfg:Basic>true</cfg:Basic>
                <cfg:Digest>true</cfg:Digest>
                <cfg:Kerberos>true</cfg:Kerberos>
                <cfg:Negotiate>true</cfg:Negotiate>
                <cfg:Certificate>true</cfg:Certificate>
                <cfg:CredSSP>false</cfg:CredSSP>
            </cfg:Auth>
            <cfg:DefaultPorts>
                <cfg:HTTP>5985</cfg:HTTP>
                <cfg:HTTPS>5986</cfg:HTTPS>
            </cfg:DefaultPorts>
            <cfg:TrustedHosts></cfg:TrustedHosts>
        </cfg:Client>
        <cfg:Service>
            <cfg:RootSDDL>O:NSG:BAD:P(A;;GA;;;BA)S:P(AU;FA;GA;;;WD)(AU;SA;GWGX;;;WD)</cfg:RootSDDL>
            <cfg:MaxConcurrentOperations>4294967295</cfg:MaxConcurrentOperations>
            <cfg:MaxConcurrentOperationsPerUser>15</cfg:MaxConcurrentOperationsPerUser>
            <cfg:EnumerationTimeoutms>60000</cfg:EnumerationTimeoutms>
            <cfg:MaxConnections>25</cfg:MaxConnections>
            <cfg:MaxPacketRetrievalTimeSeconds>120</cfg:MaxPacketRetrievalTimeSeconds>
            <cfg:AllowUnencrypted>false</cfg:AllowUnencrypted>
            <cfg:Auth>
                <cfg:Basic>false</cfg:Basic>
                <cfg:Kerberos>true</cfg:Kerberos>
                <cfg:Negotiate>true</cfg:Negotiate>
                <cfg:Certificate>false</cfg:Certificate>
                <cfg:CredSSP>false</cfg:CredSSP>
                <cfg:CbtHardeningLevel>Relaxed</cfg:CbtHardeningLevel>
            </cfg:Auth>
            <cfg:DefaultPorts>
                <cfg:HTTP>5985</cfg:HTTP>
                <cfg:HTTPS>5986</cfg:HTTPS>
            </cfg:DefaultPorts>
            <cfg:IPv4Filter>*</cfg:IPv4Filter>
            <cfg:IPv6Filter>*</cfg:IPv6Filter>
            <cfg:EnableCompatibilityHttpListener>false</cfg:EnableCompatibilityHttpListener>
            <cfg:EnableCompatibilityHttpsListener>false</cfg:EnableCompatibilityHttpsListener>
            <cfg:CertificateThumbprint></cfg:CertificateThumbprint>
        </cfg:Service>
        <cfg:Winrs>
            <cfg:AllowRemoteShellAccess>true</cfg:AllowRemoteShellAccess>
            <cfg:IdleTimeout>180000</cfg:IdleTimeout>
            <cfg:MaxConcurrentUsers>5</cfg:MaxConcurrentUsers>
            <cfg:MaxShellRunTime>2147483647</cfg:MaxShellRunTime>
            <cfg:MaxProcessesPerShell>15</cfg:MaxProcessesPerShell>
            <cfg:MaxMemoryPerShellMB>150</cfg:MaxMemoryPerShellMB>
            <cfg:MaxShellsPerUser>5</cfg:MaxShellsPerUser>
        </cfg:Winrs>
    </cfg:Config>
    Please advise.

    • Edited by CZahrobsky Friday, September 6, 2013 3:01 PM Added error details
    Friday, September 6, 2013 2:55 PM
  • Can you create a normal Remote PowerShell Session just from PowerShell (outside of your code) http://technet.microsoft.com/en-us/library/dd297932(v=exchg.141).aspx

    If your running it from a Domain connected machine you can use Kerberos for authentication and change https to http in the URI

    Cheers
    Glen

    Monday, September 9, 2013 5:26 AM
  • I can connect only on the server itself.

    It looks like the following script should work.  Am I missing something?

    cls
    ## Code From http://poshcode.org/624
    ## Create a compilation environment
    $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider
    $Compiler=$Provider.CreateCompiler()
    $Params=New-Object System.CodeDom.Compiler.CompilerParameters
    $Params.GenerateExecutable=$False
    $Params.GenerateInMemory=$True
    $Params.IncludeDebugInformation=$False
    $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null
    $TASource=@'
      namespace Local.ToolkitExtensions.Net.CertificatePolicy{
        public class TrustAll : System.Net.ICertificatePolicy {
          public TrustAll() { 
          }
          public bool CheckValidationResult(System.Net.ServicePoint sp,
            System.Security.Cryptography.X509Certificates.X509Certificate cert, 
            System.Net.WebRequest req, int problem) {
            return true;
          }
          public bool RedirectionUrlValidationCallback(string redirectionUrl) {
            bool result = false;
            System.Uri redirectionUri = new System.Uri(redirectionUrl);
            if (redirectionUri.Scheme == "https")
            {
                result = true;
            }
            return result;
          }
          
        }
      }
    '@ 
    $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)
    $TAAssembly=$TAResults.CompiledAssembly
    ## We now create an instance of the TrustAll and attach it to the ServicePointManager
    $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")
    $TrustAll.CheckValidationResult($null, $null, $null, $null)
    [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$True}
    [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll
    ## $ExCred = Get-Credential ...
    $Session = New-PSSession -ConfigurationName Microsoft.Exchange -Credential $ExCred -ConnectionUri https://myserver/powershell/ -Authentication Negotiate
    Import-PSSession $Session
    Get-Help Get-DistributionGroup

    Here's the output:

    True
    ## [myserver] Connecting to remote server failed 
    ## with the following error message : The server certificate on the destination computer (
    ## myserver:443) has the following errors:    
    ## The SSL certificate is signed by an unknown certificate authority. 
    ## For more information, see the about_Remote_Troubleshooting Help topic.
    ##  + CategoryInfo          : OpenError: (System.Manageme....RemoteRunspace:RemoteRunspace) [], PSRemotingTransportException
    ##  + FullyQualifiedErrorId : PSSessionOpenFailed
    ## Import-PSSession : Cannot validate argument on parameter 'Session'. The argument is null. 
    ## Supply a non-null argument and try the command again.
    ## At line:48 char:17
    ## + Import-PSSession <<<<  $Session
    ##    + CategoryInfo          : InvalidData: (:) [Import-PSSession], ParameterBindingValidationException
    ##    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.ImportPSSessionCommand
    ##
    ## Get-Help : Cannot find Help for topic "Get-DistributionGroup".
    ## At line:50 char:9
    ## + Get-Help <<<<  Get-DistributionGroup
    ##    + CategoryInfo          : ResourceUnavailable: (:) [Get-Help], HelpNotFoundException
    ##    + FullyQualifiedErrorId : HelpNotFound,Microsoft.PowerShell.Commands.GetHelpCommand

    Tuesday, September 10, 2013 11:11 PM
  • That error points to you using a Self Signed certification. To Get around this you can use

    $options = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck
    $Session = New-PSSession -SessionOption $options -ConfigurationName Microsoft.Exchange -Credential $ExCred -ConnectionUri https://myserver/powershell/ -Authentication Negotiate

    Cheers
    Glen
    Wednesday, September 11, 2013 2:25 AM
  • After some trial and error, this is the code I used to run the Exchange PowerShell scripts required to query and update DistributionGroup objects:

    RunInExchange("New-DistributionGroup 'MyGroup'; Add-DistributionGroupMember -Identity 'MyGroup' -Member 'me@mydomain.com';", "admin@mydomain.com", "password", "exchange.mydomain.com");  

    public static string RunInExchange(string cmd, string adminUserEmail, string password, string exchangeServer)
    {
        string cmdResult = "";
        System.Uri uri = new Uri("http://" + exchangeServer 
            + "/powershell?serializationLevel=Full");
        System.Security.SecureString securePassword = String2SecureString(password);
        PSCredential creds = new PSCredential(adminUserEmail, securePassword);
        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", creds);
        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
        {
            // Associate the Runspace with PowerShell and invoke it
            runspace.Open();
            powershell.Runspace = runspace;
            Collection<PSSession> result = powershell.Invoke<PSSession>();
            foreach (ErrorRecord current in powershell.Streams.Error)
            {
                Console.WriteLine("Exception: " + current.Exception.ToString());
                Console.WriteLine("Inner Exception: " + current.Exception.InnerException);
            }
            if (result.Count != 1)
                throw new Exception("Unexpected number of Remote Runspace connections returned.");
            // Set the Exchange Runspace as a local variable on the PS 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();
            // Import the cmdlets in the current Runspace (using Import-PSSession)
            powershell = PowerShell.Create();
            command = new PSCommand();
            command.AddScript("Import-PSSession -Session $ra");
            powershell.Commands = command;
            powershell.Runspace = runspace;
            powershell.Invoke();
            // Now the Exchange Server Script
            powershell = PowerShell.Create();
            command = new PSCommand();
            command.AddScript(cmd);
            command.AddCommand("Out-String");
            powershell.Commands = command;
            powershell.Runspace = runspace;
            Collection<PSObject> results = new Collection<PSObject>();
            results = powershell.Invoke();
            foreach (PSObject PSresult in results)
            {
                cmdResult += PSresult.ToString() + "\r\n";
            }
        }
        finally
        {
            runspace.Dispose();
            runspace = null;
            powershell.Dispose();
            powershell = null;
        }
        return cmdResult;
    }
    private static SecureString String2SecureString(string password)
    {
        SecureString remotePassword = new SecureString();
        for (int i = 0; i < password.Length; i++)
            remotePassword.AppendChar(password[i]);
        return remotePassword;
    }

    Thanks!


    • Marked as answer by CZahrobsky Friday, October 18, 2013 12:14 AM
    • Edited by CZahrobsky Friday, October 18, 2013 12:31 AM Added String2SecureString
    Friday, October 18, 2013 12:13 AM