Using System.DirectoryServices.Protocols from Powershell

This simple Powershell module demonstrates how to use robust and powerfull objects from System.DirectoryServices.Protocols (S.DS.P) from Powershell.

S.DS.P.zip
 
 
 
 
 
4.4 Star
(5)
9,290 times
Add to favorites
Active Directory
5/4/2019
E-mail Twitter del.icio.us Digg Facebook
Sign in to ask a question


  • How to use multiple LDAP (DCs) for redundancy
    2 Posts | Last post May 02, 2019
    • Hi Jiri,
      
      First I would like to thank you for this great module. I use it in a domain login script to fetch some user attributes.
      
      In order to get some redundancy I would like to add multiple LDAP servers in the script. At the moment I use something like this to initialize the LDAP connection:
      
      $MyConnection=Get-LdapConnection -LdapServer:dc1.domain.local -EncryptionType Kerberos
      
      Is there an easy way to add a list of LDAP servers and connect to the next available in case the primary dc1 is not available ?
      
      Thank you for your kind help.
      
      Best regards
      Andreas
    • Hello Andreas,
      apologies for late answer - recently I've been monitoring GitHub where you can ask questions, too.
      Easy way to achieve HA just by using domain DNS name (such as domain.local) as LdapServer name and let runtime to reach domain controller - works very well
      
      Hope this helps,
      Jiri
  • Adding and removing members from groups
    3 Posts | Last post November 04, 2018
    • Hi Jiri, I've successfully tested the module with non-AD LDAP server to replace a single member of a group but the capability to add additional members does not appear to be available 'out of the box'. I've read through 'Introduction to System.DirectoryServices.Protocols (S.DS.P)' at https://msdn.microsoft.com/en-us/library/bb332056.aspx and I see the advice to use ModifyRequest object with PermissiveModifyControl but I lack the skills to translate this into PowerShell so I can edit the Edit-LdapObject function. Are you able to provide some advice please?
    • Sorry! Just a few minutes after posting this question, I figured it out without any modification to the module/function. In case anyone else needs to know, when you build the object for the Edit-LdapObject function, you just need to define the member attribute in the hash table and then use an array of values for that attribute like this:
      
      $Props = @{"distinguishedName"=$null;instData=$null;uniqueMember=$null}
      $obj = new-object PSObject -Property $Props
      $obj.DistinguishedName = "cn=testgroup,ou=Groups,dc=sys,dc=com"
      $obj.instData = "this is a description"
      $obj.uniqueMember = "uid=testuser1,ou=People,dc=sys,dc=com", "uid=testuser2,ou=People,dc=sys,dc=com", "uid=testuser3,ou=People,dc=sys,dc=com"
      
      Edit-LdapObject -LdapConnection:$conn -Object $obj
    • Hi Dan,
      yes, you made it working the way how it's supposed to work.
      you may have noticed that Edit-LdapObject now has 'Replace' operation hardcoded. This is not limitation for single-valued properties, but for multi-valued properties, with pre-existing values (such as group members), it actually requires to load all values first (i.e. via Find-LdapObject), then add/remove values in-memory as needed, and then use Edit-LdapObject to place the result back.
      For large set of values, this may be ineffective; in vNext I may come with better option that allows more effective operations over multival props
      
      Hope this helps,
      Jiri
  • Can you control client side timeout
    2 Posts | Last post November 04, 2018
    • Hello Jiri
      First of all , let me say that your script was a huge help in getting a more powershell documented (technical) version of S.DS.P.
      Secondly , is there a way of controlling the client side timeout limit by a S.DS.P object ? 
    • Hello,
      client side timeout is controlled by Timeout parameter of Get-LdapObject cmdlet, and applies to all requests sent over that connection.
      then there's server side timeout controlled by Timeout property of Find-LdapObject cmdlet. It tells the server how to long it's allowed to process the search request.
      
      Hope this helps, please let me know your experience with timeouts in the use case you're working on.
      
      Thanks,
      Jiri
  • I am unable to get this script to work.
    10 Posts | Last post September 24, 2018
    • When I try to run it, I get an error "Find-LdapObject : Unable to find type [System.DirectoryServices.Protocols.LdapConnection]."  I'm running Windows 10 with .NET 4.6.1.   
      
      How do I get PowerShell to 'find' this library?  
    • Hello,
      this happens automatically when you import the module S.DS.P - dependency on assembly is specified in module manifest file (.psd1) and Powershell automatically loads dependencies.
      I just tested on my Win10 machine and works as expected
      
      Hope this helps,
      Jiri
    • Thanks, Jim, I did kinda/sorta figure that out after I posted the question.  I wasn't importing the script/using .psd1 as I needed to slightly modify S.DS.P.psm1 to work in my environment.  So, I copied S.DS.P.psm1 to a regular .sp1 file and I'm using that copy.  I had to add "Add-Type -AssemblyName System.DirectoryService.Protocols" to get the script to run.
      
      However, I still cannot connect to our LDAP server.  I should mention that our LDAP server is NOT Active Directory.  It is a NetIQ implementation on a Linux box (I think--I'm no LDAP or NetIQ guru but we use a NetIQ web interface to manage it, so I'm guessing it is a NetIQ LDAP implementation).
      
      The tweaks to the script I made are minor.  Around lines 168-172, I removed $domain from the $cred NetworkCredential variable because our LDAP server is not part of a domain.  I also had to add a $auth="Basic" variable and add it to the end of $LdapConnection because I kept getting an error that the LDAP server did not use the default authentication type (I get an error, “the authentication type is not supported”).
      
      With these changes, the script does run, but never logs onto the LDAP server.  I keep getting an error "The supplied credential is invalid."  I know the credential is valid because it is used in many Python scripts on a Linux server to automate some tasks.
      
      Not sure if this is important, but those Python scripts connect to the server by "ldaps://server1.univ.edu:637" (I've changed the actual server name for security, of course).  This PS script doesn't use the ldaps:// prefix, so I couldn't for the life of me connect on port 637.  I tried with  -port option and also using –UseSLL but I always got an error that the server was unavailable.  When I switched to the default port 389, I would then get past the server unavailable error but I’m now stuck at the invalid credential error mentioned before.
      
      (continued in next post...)
    • (continuation)
      
      My best guess at this point is that the .NET classes involved only work with Microsoft’s implementation of LDAD (that is, AD) and not any other.  For example, the authentication options available with System.DirectoryServices.Protocols (Anonymous, Basic, Digest, Dpa, Kerberos, Msn, Negotiate, Ntlm and Sicily) seem to be completely different than the standard LDAP options I found from a web search (Anonymous, Simple, and SASL).  Maybe the two Anonymous are the same, but our server doesn’t support that.  I’m guessing Basic is similar to Simple but perhaps not good enough to actually logon?  And there is no SASL option with DirectoryServices.Protocols so I can’t even try that.
    • Hello,
      yes, when you want to load assembly yourself, the way to go is via Add-Type, or [System.Reflection.Assembly]::LoadWithPartialName()
      
      Regarding what you're trying to achieve:
      - if the SSL endpoint is open on the server side, then LdapConnection will connect, provided that it can verify server certificate. If cert cannot be verified (such as server name provided to the connection does not match to what's in SSL certificate), then you'll get ServerUnavailable error. Below works easily for me (connecting with implicit creds):
         $di=new-object System.DirectoryServices.Protocols.LdapDirectoryIdentifier("dc.dom.com",636)
          $conn=new-object System.DirectoryServices.Protocols.LdapConnection($di)
          $conn.SessionOptions.SecureSocketLayer=$true
          $conn.Bind()
      
      - SimpleBind authentication shall work as well. Below sample code:
          $di=new-object System.DirectoryServices.Protocols.LdapDirectoryIdentifier("dc.dom.com",636)
      $conn=new-object System.DirectoryServices.Protocols.LdapConnection($di)
      $conn.SessionOptions.SecureSocketLayer=$true
      $conn.AuthType="Basic"
      $cred=new-object System.Net.NetworkCredential("CN=User,CN=Users,DC=dom,DC=com","password")
      $conn.Credential=$cred
      $conn.Bind()
      
      - SASL auth over non-ssl port shall work as well (as far as I remember, server must support LDAPv3 for this to work), see sample code below:
      
      $di=new-object System.DirectoryServices.Protocols.LdapDirectoryIdentifier("dc.dom.com",389)
      $conn=new-object System.DirectoryServices.Protocols.LdapConnection($di)
      
      $conn.AuthType="Basic"
      $cred=new-object System.Net.NetworkCredential("CN=User,CN=Users,DC=dom,DC=com","password")
      $conn.Credential=$cred
      $conn.SessionOptions.ProtocolVersion=3
      $conn.SessionOptions.StartTransportLayerSecurity($null)
      $conn.Bind()
      
      Overall, I guess that sample with SimpleBind auth shall work for you, as LDAP servers are generally expected to support this method. if you're able to publish LDAP enpoint, I will test
      
      Regards,
      Jiri
    • Jim, I tried all three of your snippets and still no go.  The first one ,–SSL, still gives me a “LDAP server is unavailable error.”  Ditto for the –SimpleBind one.  The –SASL one give me an error, “Exception calling "StartTransportLayerSecurity" with "1" argument(s): "A local error occurred."
      
      I also tested them from a different host, instead of my workstation, on a Windows 2012 server.  I get the same results there, too, except I don’t get the ‘local error’ for the –SASL snippet but it does give the “LDAP server is unavailable error.”  
      
      But now for an interesting twist…I decided to try this out on a completely different LDAP server (again, NOT an Active Directory server).    I tried your snippets on server2.system.univ.edu with different admin credintials, cn=myadmin,o=univ.  I couldn’t connect using SSL on this sever either, but if I removed the SSL info in the snippets, and did a default connection to port 389, they worked!  I could connect and bind to this server.
      
      Which leads me to wonder…was the “supplied credential is invalid” error I was getting on the first server actually accurate—that the ID and/or password I was using with it was bad?  But how could it be bad when it is running changes from a Python script on a Linux machine?  I’ll have to check with that server’s administrator to figure it out.
      
      In the meantime, I went back to test the S.DS.P script, but this time connecting to the second server mentioned above.  The script ran further than before, but still errored out.  I get the error “The server does not support the control. The control is critical” around line 250 ($rsp=$LdapConnection.SendRequest($rq, (new-object System.Timespan(0,0,$TimeoutSeconds))) –as [System.DirectoryServices.Protocols.SearchResponse];)
      
      I have no idea what that means.
      
    • Hello,
      the error you report at the end simply says that your LDAP server does not support timeout on the requests. Just replace the line
      $rsp = $LdapConnection.SendRequest($rq, (new-object System.Timespan(0,0,$TimeoutSeconds))) -as [System.DirectoryServices.Protocols.SearchResponse];
      with line
      $rsp = $LdapConnection.SendRequest($rq) -as [System.DirectoryServices.Protocols.SearchResponse];
      any you should get over this
      
      Hope this helps,
      Jiri    
    • Excellent! Thanks for the help.
    • Just a few followups as I've also used this script to connect to a NetIQ LDAP catalog.
      I used a separate function to create the connection and then passed that to this function. That allowed me to better tweak the connection parameters to suit non microsoft LDAP directories. I connected via StartTLS (as ldaps:// is deprecated in favor of Start TLS).
      
      Also I had to disable the ranged property retrieval for attribute logic entirely as it simply didn't work for me against NetIQ's eDirectory becuase they don't support this draft/proposed LDAP extension. The code should check for object identifier 1.2.840.113556.1.4.802 in the supportedControls operational attribute on the rootDSE. Clients must not use the range option unless this object identifier is present. 
    • @Alex: We now support non-ranged attribute retrieval, via parameter RangeSize. I decided to leave this on called to decide whether or not to use ranged attribute retrieval ratheer than automatically check RootDSE.
      
      Thanks for input!
      Jiri
  • PropertiesToLoad is not returning any properties
    4 Posts | Last post August 31, 2018
    • Hi,
      
      When I use Find-LdapObject against a NetIQ LDAP (eDirectory) server, properties specified using the PropertiesToLoad option are not returned.  There is no error, just empty columns for the requested properties.  Find-LdapObject does return the distinguishedName, so the search itself is working.  When I run it against AD it does return the properties; it seems this may only be an issue for non AD LDAP servers.
      
      Has anyone else experienced this, and any ideas on what the issue may be?
      
      Just to add, I'm currently using v1.7.5 of S.DS.P module.
      
      Thanks,
      
      Matt
    • I had the same issue as you but using IBM Directory Services instead of NetIQ. After some troubleshooting I was able to get expected results by modifying line 290 of S.DS.P.psm1:
      
            From this: $rng = "$($attrName.ToLower());range=$start`-$($start+$rangeSize-1)"
            To this: $rng = $attrName.ToLower()
      
      Seems to screw with results when not using ActiveDirectory as the target LDAP system. I know you have probably moved on by now, but figured someone else might come across the same issue.
    • Hi Matt, Steve,
      might be that servers you use may not support ranged attribute retrieval. I will look at this and may add support to load of attribute values without ranged retrieval.
      
      PS I do not monitor this Q/A very often, so apologies for late answer. If possible, please use https://github.com/jformacek/S.DS.P/issues to ask questions/log problems
      
      Thank you for testing with non-MS LDAP server,
      Jiri
    • Just update module to support loading of attributed without ranged retrieval. Give it a try please
      
      Regards,
      Jiri
  • The server does not support the control. The control is critical
    2 Posts | Last post August 31, 2018
    • Hi,
      
      I have an open ldap server, I have test using anonymous and basice authentication but always I got the error The server does not support the control. The control is critical, when I search.
      
      The connection is:
      $di=new-object System.DirectoryServices.Protocols.LdapDirectoryIdentifier("server.domain.com",389)
      $conn=new-object System.DirectoryServices.Protocols.LdapConnection($di)
      $conn.SessionOptions.SecureSocketLayer=$false
      $conn.AuthType="Anonymous"
      $conn.Bind()
      
      The error using find-ldapsearch is:
      Exception calling "SendRequest" with "1" argument(s): "The server does not
      critical."
      At C:\apl\S.DS.P.psm1:211 char:17
      + ...             $rsp = $LdapConnection.SendRequest($rq, $timeout) -as [System.Direc
    • Hi Felix,
      might be that your LDAP server does not support paging. Did you try with parameter -PageSize:0 ? Setting PageSize to 0 causes not to send PageResultRequestControl to server.
      
      PS I do not monitor this Q/A very often, so apologies for late answer. If possible, please use https://github.com/jformacek/S.DS.P/issues to ask questions/log problems
      
      Thank you,
      Jiri
  • How to successfully pass the parameters to the cmdlet?
    1 Posts | Last post June 15, 2018
    • Hi Jiri,
      
      The LDAP script is really helpful and time-saver. While passing values for SerachBase, I am getting below exception. Can you have a look into it that what am I missing once you get a moment?
      
      --
      Exception calling "SendRequest" with "2" argument(s): "The distinguished name contains
      invalid syntax."
      At C:\Program Files\WindowsPowerShell\Modules\S.DS.P\S.DS.P.psm1:248 char:17
      + ...             $rsp = $LdapConnection.SendRequest($rq, $Timeout) -as [Sy ...
      +                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
          + FullyQualifiedErrorId : DirectoryOperationException
      --
      
      Thanks,
      DSal
  • Proposed improvements
    4 Posts | Last post February 08, 2017
    • From a design perspective, I think it is far better to not intermingle the "bind/connection" with the "search" functionality. Why not split this out to a separate function within the same module?
      
      That would also allow future extension to create/update/delete objectsion using same connection object via additional functions.
    • Hi Oz,
      this is consolidated reply for your recent posts to different threads below.
      
      originally, the sample was meant to showcase S.DS.S to support people to switch from ADSI/DirectoryEntry objects. I noticed it gained some popularity - which is great as it shows the original purpose was reached.
      It's tested mostly against AD/AD LDS - while I'm in consulting business, I rarely meet other directories to the extent that would allow to test the script against.
      
      I also have code samples written that allow adding new objects, and editing and deleting existing - however, did not have enought time to test them to meet expected quality bar.
      
      Maybe you would be interested in helping with development? I have a workspace on Github, so let's move the source code there, for better cooperation on adding new features by people who have better access to various brands of directory servers.
      This site can still host recent, tested versions for wide public
      
      Thoughts?
      
      Jiri
    • Jiri,
      
      I've only written very basic code for adding new objects as that was required by my current project.
      
      Happy to contribute my changes back to a central workspace, and can readily test against some other LDAP implementations (primarily NetIQ eDirectory).
      
      Alex
    • Hi Alex,
      I will create a github repo with sources this week and let you know.
      Also, I have some code ready that allows addin/modifying LDAP objects - based on passing dictionary with props. Will create skeleton implementation for you to work on
      Would be great to have someone to test on other LDAP server platforms
      
      Thanks,
      Jiri
  • Retrieve all Properties
    3 Posts | Last post February 04, 2017
    • Hi Jiri,
      
      Working with the -propertiestoLoad:@() parameter how would you retrieve all the properties assigned to an object?
      
      Regards,
      Lachlan
    • Hi Lachlan,
      well, current implementation expects that you ask for specific properties and returns list of objects with those properties, forming a table. when retrieving all available props, output would not be a table as objects may have different props defined.
      
      However, let me look at this request; I may implement it in next versio of cmdlet
      
      Regards,
      Jiri
    • I have played a bit with tweaking the code to retrieve all properties assigned to an object.
      There are some significant downsides with this approach. Jiri covered some of these,
      
      However one additional issue is regarding constructed attributes. Constructed attributes are only returned if you request them. If you simply query for all attributes on an object, these attributes will never be returned. One must explicitly request them if they wish to see them.
  • find-ldapobject returning empty results
    3 Posts | Last post January 12, 2017
    • I have a rather complex ldap setup that includes certificates and authentication.  I have been able to get the connection to successfully bind and I can send requests.  So I used the find-ldapobject commandlet to return the results
      
      However, when I return the results using the line: 
      $results = find-ldapobject -ldapconnection $LDAPconnection -searchfilter "(ou=arch,*)" -searchbase "ou=people,dc=umich,dc=edu" -pagesize 0 -PropertiesToLoad @("cn","mobile") | select-object *
      
      I get a bunch of blank lines.  It seems to be working because when I limit the query down to 1 result, it only returns 1 blank line.  When it is set to the ou=arch,*  it returns about the number of blank lines as people in the ou.
      
      I had to set the pagesizse to 0 because when I ran it with the pagesize of 100, it said 0
      Exception calling "SendRequest" with "2" argument(s): "The server does not support the control. The control is critical."  So 0 (non-paged) seemed to be the only option.
      
      I know cn and mobile are valid properties because I can see them when I connect to the ldap directory with Windows' LDAPAdmin.exe
    • Hi Jeff,
      hard to tell what's wrong in your case - generally it is expected to work against any LDAP server (I tested against AD and AD LDS, and I have successful reports from non-MS LDAP servers usage as well)
      
      If you give me remote access to your lab, I will give it some time to debug to see what's wrong there.
      
      Regarding not supported controls: yes, different LDAP servers support different controls, so this error is expected for servers that do not support this control
      
      Regards,
      Jiri
    • As a feature request, the code should interrogate the supported controls for the specified LDAP server and gracefully degrade if some options are not supported. The current implementation seems mostly tested against AD which supports several controls (and authentication mechanisms) that aren't widely used elsewhere.
1 - 10 of 18 Items