KnowledgeBase Archive

An Archive of Early Microsoft KnowledgeBase Articles

View on GitHub

Q263520: HOWTO: Programmatically Create a Distribution List Using ADSI

Article: Q263520
Product(s): Microsoft Exchange
Version(s): 2.5,5.5
Operating System(s): 
Keyword(s): kbADSI kbMsg kbVBp kbGrpDSMsg kbDSupport
Last Modified: 19-JAN-2002

-------------------------------------------------------------------------------
The information in this article applies to:

- Microsoft Exchange Server, version 5.5 
- Microsoft Active Directory Service Interfaces, version 2.5 
-------------------------------------------------------------------------------

SUMMARY
=======

ADSI can be used to create or modify Distribution Lists on Microsoft Exchange
Servers. This article provides sample code to create a Distribution List with
multiple members.

NOTE: In this article, the processes of creating a Distribution List and then
adding members are separated so that they can be used individually for
Distribution List management.

MORE INFORMATION
================

1. Create a new Microsoft Visual Basic standard EXE project.

2. In the Project References dialog box, check the references for the following:

   - Active DS Type Library
   - ADsSecurity 2.5 Type Library

3. On the default Form1, add three CommandButtons: CommandButton1,
  CommandButton2, and CommandButton3.

4. Add the following code to the form's code module:

  Option Explicit

  Const RIGHT_DS_MODIFY_USER_ATT = &H2
  Const RIGHT_MAIL_SEND_AS = &H8
  Const RIGHT_MAIL_RECEIVE_AS = &H10

  ' Custom type for Distribution List properties.
  Private Type DLTemplate
      Domain as String        ' Network domain name.
      Server As String        ' Microsoft Exchange server name.
      Org As String           ' Microsoft Exchange Organization name.
      Site As String          ' Microsoft Exchange Site name,
      Container As String     ' such as Recipients.
      Name As String
      DisplayName As String   ' DL's Display name.
      Alias As String         ' DL's Alias name.
      DirectoryName As String ' DL's Directory name.
      UserName As String      ' User's logon name and domain.
      Password As String      ' User's domain password.
      SMTPAddr As String
      X400Addr As String
      Owner As String
      Hide As Boolean         ' Hide DL from address book.
      OtherAddresses(10) As String
      Members(10) As String   ' Increase array elements for more members.
  End Type

  Dim NewDL As DLTemplate     ' Public to all procedures in module.

  Private Sub CommandButton1_Click()
  ' Fill Distribution List "template".

  ' Set vars to get container.
  '*** change Domain, Server, Org and Site information as appropriate.
  NewDL.Domain = "myDomain.com"
  NewDL.Server = "myServer"      
  NewDL.Org = "Orgname"     ' Microsoft Exchange Organization
  NewDL.Site = "sitename"   ' Microsoft Exchange Site

  ' Place new DL in Recipients container.
  NewDL.Container = "Recipients"

  '*** set DL named properties as appropriate.
  NewDL.Alias = "DL47"
  NewDL.DisplayName = "Distribution List 47"
  NewDL.SMTPAddr = NewDL.Alias & "@" & NewDL.Domain 
  NewDL.Hide = False  'default is False
  ' Use distinguished name of owner.
  NewDL.Owner = "cn=user1,cn=Recipients,ou=NORTHAMERICA,o=DS Messaging"

  'Set required X400 address
  'Create a DL manually and use properties as a guide
  '   Country (c) is required
  '   ADMD (a) is required, the default is a zero-length string ""
  '   PRMD (p) is not required, but if included the default is a zero-length string
  '   Surname (s) is required
  '   the trailing semi-colon is required
  NewDL.X400Addr = "c=US;a= ;p=" & CStr(NewDL.Org) & ";o=" & Str(NewDL.Site)&
   ";s=" & CStr(NewDL.Alias) & ";"

  NewDL.OtherAddresses(0) = CStr("MS$" + NewDL.Org + "/" + NewDL.Site + "/" + NewDL.Alias)
  NewDL.OtherAddresses(1) = CStr("CCMAIL$" + NewDL.Alias + " at " + NewDL.Site)

  ' Fill array of addresses (i.e. MSMail, CCMail).
  Dim OtherAddresses(1)
  OtherAddresses(0) = CStr("MS$" + NewDL.Org + "/" + NewDL.Site + "/" + NewDL.Alias)
  OtherAddresses(1) = CStr("CCMAIL$" + NewDL.Alias + " at " + NewDL.Site)

  ' Set Members using their Distinguished Names.
  NewDL.Members(0) = "LDAP://myServer/o=myOrg/ou=mySite/cn=Recipients/cn=confrm11"
  NewDL.Members(1) = "LDAP://myServer/o=myOrg/ou=mySite/cn=Recipients/cn=confrm12"
  NewDL.Members(1) = "LDAP://myServer/o=myOrg/ou=mySite/cn=Recipients/cn=confrm13"

  End Sub

  Private Sub CommandButton2_Click()
  ' This routine creates a distribution list, but it does not add members.

  ' Call function to create DL.
  ' Uses global DLTemplate previously filled.

  Dim lResult As Long
  lResult = CreateDL(NewDL, True)
  Select Case lResult
      Case -1: ' Succeeded.
          MsgBox "DL created"
      Case 0: ' Failed, unknown error.
          MsgBox "Unknown error creating DL"
      
      Case Else: ' Get error number from return value.
          
          Select Case Hex(lResult)
              Dim strText As String, strTitle As String
              Case 80071392: 'already exists
                  strText = " - Item Alredy Exists"
              Case Else: 'unknown error
                  strText = "- Unknown"
          End Select
          strTitle = "Error creating DL"
          strText = "Error: " & lResult & " (" & Hex(lResult) & ")" & strText
          MsgBox strText, vbExclamation, strTitle
  End Select

  End Sub

  Private Sub CommandButton3_Click()

  ' Call function to add DL members.
  ' Uses DLTemplate previously filled.

  Dim lResult As Long
  lResult = AddDLMembers(NewDL, True)
  Select Case lResult
      Case -1: ' Succeeded.
          MsgBox "Members added"
      Case 0: ' Failed, unknown error.
          MsgBox "Unknown error adding members"
      Case Else: ' Get error number from return value.
          MsgBox "Error adding members: " & lResult
  End Select

  End Sub

  Private Function AddDLMembers(DL As DLTemplate, DebugMode As Boolean) As Long

  AddDLMembers = 1  'default value

  Dim strADsPath As String
  Dim DistList As Object

  On Error GoTo Error_AddMembers

  '-- Build adspath to container, usually Recipient container:
  '   LDAP://myserver/O=Org/OU=Site/CN=Recipients
  strADsPath = "LDAP://" + DL.Server
  strADsPath = strADsPath + "/O=" & DL.Org
  strADsPath = strADsPath + "/OU=" & DL.Site
  strADsPath = strADsPath + "/CN=" & DL.Container
  strADsPath = strADsPath + "/CN=" & DL.Alias

  If DebugMode Then Debug.Print "ADsPath: " & strADsPath

  ' Get reference to Distribution List.
  Set DistList = GetObject(strADsPath)
  Debug.Print DistList.Name

  ' Loop through Members array until null found.
  Dim i As Integer
  Do Until DL.Members(i) = ""
      ' Add user to Distribution List.
      DistList.Add DL.Members(i)
      ' If the entry is already a member of the DL,
      ' error 8007200d occurs.
      If DebugMode Then
          Debug.Print "Adding Member: " & i
          Debug.Print "Member: " & DL.Members(i)
          If Err.Number = 0 Then
              Debug.Print "Member added"
          Else: ' Error.
              Debug.Print "Error occurred: " & Err.Number
              Debug.Print "Reason: " & Err.Description
          End If
      End If
      i = i + 1
  Loop

  Exit_AddDLMembers:
  ' Explicitly release objects.
  Set DistList = Nothing
  Exit Function

  Error_AddMembers:
      ' Set return value for known error.
      If Err.Number = &H8007200D Then
          MsgBox "Already a member: " & DL.Members(i)
      End If
      AddDLMembers = Err.Number
      If DebugMode Then
          Debug.Print "Error in AddDLMembers(): " & Err.Number
          Debug.Print "Error Description: " & Err.Description
      End If
      Resume Exit_AddDLMembers

  End Function

  Private Function CreateDL(DL As DLTemplate, DebugMode As Boolean) As Long

  ' Vars used to get container.
  Dim strADsPath As String
  Dim objContainer As Object
  Dim objNewDL As Object

  ' Set default value for failed, unknown error.
  CreateDL = 0

  On Error GoTo Err_CreateDL

  '-- Build adspath to container, usually Recipient container:
  '   LDAP://myserver/O=Org/OU=Site/CN=Recipients
  strADsPath = "LDAP://" + DL.Server
  strADsPath = strADsPath + "/O=" & DL.Org
  strADsPath = strADsPath + "/OU=" & DL.Site
  strADsPath = strADsPath + "/CN=" & DL.Container

  If DebugMode Then Debug.Print "ADsPath: " & strADsPath

  ' Get container.
  Set objContainer = GetObject(strADsPath)
  If DebugMode Then Debug.Print "Got container: " & objContainer.Name

  ' Create a new DL in the container.
  ' A Distribution List is known as a "groupOfNames".
  Set objNewDL = objContainer.Create("groupOfNames", "cn=" & DL.Alias)
  If DebugMode Then Debug.Print "objNewDL created for: " & DL.Alias

  ' Set the DL props from the custom type structure.
  objNewDL.Put "cn", CStr(DL.DisplayName)   ' cStr() may be required to make Unicode values.
  If DebugMode Then Debug.Print "set DisplayName property: " & DL.DisplayName

  objNewDL.Put "uid", CStr(DL.Alias)
  If DebugMode Then Debug.Print "set Alias property: " & DL.Alias
  objNewDL.Put "Owner", CStr(DL.Owner)
  If DebugMode Then Debug.Print "set Owner property: " & DL.Owner

  ' Alternate way to set property using simplest assignment technique.
  objNewDL.mail = DL.SMTPAddr
  If DebugMode Then Debug.Print "set SMTP Address property: " & DL.SMTPAddr

  'set required X400 address
  objNewDL.Put "textencodedORaddress", CStr(DL.X400Addr)
  If DEBUGMODE Then Debug.Print "set X400 Address property: " & DL.X400Addr

  ' Set optional properties only if not default values.
  If DL.Hide Then objNewDL.Put "Hide-DL-Membership", DL.Hide
  If DebugMode Then Debug.Print "set Hide from GAL property: " & DL.Hide

  ' Use array to hold multi-value property values.
  ' Use Put to set the multi-values (overwrite whatever was there).
  ' Use PutEx to append additional values, etc.

  ' Create other addresses (ie. MSMail, CCMail).

  'objNewDL.PutEx ADS_PROPERTY_APPEND, "otherMailbox", aOtherMailbox
  'objNewDL.Put "OtherMailbox", DL.OtherAddresses

  Dim aOtherMailbox(1)
  aOtherMailbox(0) = CStr(DL.OtherAddresses(0))
  aOtherMailbox(1) = CStr(DL.OtherAddresses(1))
  'objNewDL.PutEx ADS_PROPERTY_APPEND, "otherMailbox", aOtherMailbox
  objNewDL.Put "OtherMailbox", aOtherMailbox
  If DebugMode Then Debug.Print "set other addresses property"

  ' NOTE: members are not added as the DL is created.
  ' Members are added separately.

  objNewDL.SetInfo  ' Save changes.
  If DebugMode Then Debug.Print "New DL saved"
     
  '------------------------------------------------------------------------
  '-- SET PERMISSION ON THE OWNER TO MODIFY AND SEND AS/RECEIVE
  '-- REQUIRES THE ADSI RESOURCE TOOL KIT INSTALL (IADsSecurity)
  '-------------------------------------------------------------------------
  strADsPath = strADsPath + "/cn=" & DL.Alias
  If DebugMode Then Debug.Print "Add owner: " & DL.Owner

  Dim sec As New ADsSecurity
  ' You can also use -- Set sec = CreateObject("ADsSecurity") for late binding.
  Dim sd As IADsSecurityDescriptor
  Dim dacl As IADsAccessControlList
  Dim ace As New AccessControlEntry

  Set sd = sec.GetSecurityDescriptor(strADsPath)
  ' An error in the ADsPath may cause a fatal error in Set dacl line!
  If DebugMode Then Debug.Print "SD.Owner: " & sd.Owner
  Set dacl = sd.DiscretionaryAcl

  ace.AccessMask = RIGHT_DS_MODIFY_USER_ATT Or RIGHT_MAIL_SEND_AS Or RIGHT_MAIL_RECEIVE_AS
  ace.Trustee = "mydomain\user2"
  dacl.AddAce ace
  sd.DiscretionaryAcl = dacl
  sec.SetSecurityDescriptor sd

  ' Set return value for success.
  CreateDL = -1

  Exit_CreateDL:
  ' Explicitly release objects.
  Set sd = Nothing
  Set dacl = Nothing
  Set objNewDL = Nothing
  Set objContainer = Nothing
  Exit Function

  Err_CreateDL:
      ' Set return value for known error.
      CreateDL = Err.Number
      If DebugMode Then
          Debug.Print "Error in CreateDL(): " & Err.Number & " (" & Hex(Err.Number) & ")"
          Debug.Print "Error Description: " & Err.Description
      End If
      Resume Exit_CreateDL

  End Function

5. In the CommandButton1_Click procedure, change the values for server,
  organization, site, and so on, to match your environment and the properties
  of the Distribution List to be created.

6. Save and then run the project. Use the first button to fill the Distribution
  List template. Use the second button to create the Distribution List, without
  members. Use the third button to add members.

Additional query words: xmrp

======================================================================
Keywords          : kbADSI kbMsg kbVBp kbGrpDSMsg kbDSupport 
Technology        : kbAudDeveloper kbExchangeSearch kbADSISearch kbExchange550 kbZNotKeyword2 kbADSI250
Version           : :2.5,5.5
Issue type        : kbhowto

=============================================================================

THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Copyright Microsoft Corporation 1986-2002.