Aside from traveling around migrating companies to Exchange, I write articles to describe my findings in the field and run an online Outlook and Exchange resource site named OutlookExchange.com. Initially, I needed a way to allow anyone subscribe to the newsletter that is sent out once a week. The choice of manually entering data in the Active Directory was as appealing as a slobbering dog and my budget couldn’t afford a dog anyway. Moreover, I wanted to keep it in-house and have some control of the list itself.
Obviously, my Active Directory and Exchange 2000 signals went off and this infrastructure guy put on his fledgling development cap and went to work. I decided to give the Workflow Event sink a go since it could offer some flexibility later in respect to authorization and approval for deletions. I will first go over what the listserver workflow does and how to use it, and then I will give a brief overview of how to install and configure it. If there is some, I will go over some of the code as well.
To begin with, there is a new version of the Exchange Workflow Designer from Microsoft. The Office Developer CD for Office XP includes the new .NET flavor of the Exchange Workflow Designer. For this exercise, we will use the Exchange Workflow Designer that comes with Office 2000. With this version, the entire workflow script can be exported and imported as an XML file. We are providing that file as a downloadable resource associated with this article. I will walk you through the installation and setup.
So here is how it works; a user sends a message to a mail-enabled Exchange 2000 public folder. In order to be processed, the subject line of the message must contain the word subscribe followed by the name of the list they wish to join. The Workflow Event Sink then processes the new folder item by identifying the sender’s SMTP address and extracting the display name fields from the header. Using the extracted SMTP field, the Workflow Event Sink locates an AD Global Catalog and searches the email address field for a match. If a match exists, the sink adds the existing account to the distribution group. If there is no account with that match, the sink creates a new hidden contact in a specific AD OU that we identify in the code and then adds that new account to the group. The Sink then sends a confirmation to the sender that they have been added to the group. Unsubscribe works in the same manner only the code never removes a contact. Also, because the entries are written into the public folders, there is a record of all requests.
To get things setup, first we need to make sure you have the necessary rights to install and execute the workflow sink. Open the component services and verify that your account has permissions to install the code. In my example, I also create a new account named Workflow and gave it the necessary permissions to add and remove accounts in the Active Directory.
Using the Component Services, open the properties of the Workflow Sink and change the account information in the Identity tab to assign the workflow account’s credentials.
In order to set up the Active Directory, simply create an OU named Lists in the root of your Active Directory (or modify the code in the workflow to point to a different OU). Next, we need to enable workflow in a mail-enabled folder and import the sample XML script. For my example, I used the Exchange System Manager to create a folder named Lists. I then modified the properties of the folder to mail-enable it. By default, anonymous has contributor rights so anyone can send mail to the folder from the Internet. In my example, lists@outlookexchange.com is used for my new subscription folder.
Now we need to workflow enable the folder and import the script. Using the Microsoft Workflow Designer for Exchange 2000 that comes on the Office 2000 Developers CD, connect to the public folder you created; public/lists in my example. Launch the Designer, and verify the server name, then type public/ in the folder box and click the browse button to view the folders.
Expand the folder list and double-click the public/lists/ folder and click OK. Click OK again to accept the choice and connect to the folder.
You can either import the sample code or follow this process to manually recreate the list server process. To manually rebuild the process, create a new blank workflow process from the file menu and past the following code into the shared code section:
Function IsSubscribe() Dim sTest IsSubscribe = False sTest = LCase(Trim(workflowsession.fields("urn:schemas:httpmail:subject").Value)) If InStr(sTest, "subscribe") > 0 And InStr(sTest, "unsubscribe") = 0 Then IsSubscribe = True End Function '**************************************************************************************** Function IsUnSubscribe() Dim sTest IsUnSubscribe = False sTest = LCase(Trim(workflowsession.fields("urn:schemas:httpmail:subject").Value)) If InStr(sTest, "unsubscribe") > 0 Then IsUnSubscribe = True End Function '**************************************************************************************** Sub CreateContactAndSubscribeIt On Error Resume Next dim group group = LCase(Trim(mid(workflowsession.fields("urn:schemas:httpmail:subject").Value, 10))) dim folderemail folderemail = "lists@theproexchange.com" dim email dim name email = workflowsession.fields( "urn:schemas:httpmail:fromemail" ).value name = workflowsession.fields( "urn:schemas:httpmail:fromname" ).value Dim oConnection Dim oRecordset Dim strQuery Dim stremail Dim oCont Dim oGC Dim strADsPath Set oCont = GetObject("GC:") For Each oGC In oCont strADsPath = oGC.ADsPath Next Set oConnection = CreateObject("ADODB.Connection") Set oRecordset = CreateObject("ADODB.Recordset") oConnection.Provider = "ADsDSOObject" 'The ADSI OLE-DB provider oConnection.Open "ADs Provider" strQuery = "<" & strADsPath & ">;(&(objectCategory=person)(mail=" & email & "));mail,cn,distinguishedName;subtree" Set oRecordset = oConnection.Execute(strQuery) Set grp = GetObject("LDAP://CN="+group+", OU=lists, DC=BRYANTFAMILY, DC=COM") If oRecordset.EOF And oRecordset.BOF Then Set ou = GetObject("LDAP://OU=lists, DC=BRYANTFAMILY, DC=COM") Set usr = ou.Create("contact", "CN="+email) usr.Put "description", "Created via email subscription" usr.Put "mail", email usr.Put "displayname", name usr.Put "internetencoding", "1310720" usr.Put "legacyExchangeDN", "/o=First Organization/ou=First Administrative Group//cn=Recipients/cn="+email usr.Put "mailnickname", email usr.Put "MapiRecipient", "FALSE" usr.Put "msExchAlObjectVersion", "23" usr.Put "msExchHideFromAddressLists", "TRUE" usr.Put "targetaddress", "SMTP:"+email usr.SetInfo grp.Add ("LDAP://CN="+email+", OU=lists, DC=BRYANTFAMILY, DC=COM") Else While Not oRecordset.EOF grp.Add ("LDAP://"+oRecordset.Fields("distinguishedName")) oRecordset.MoveNext Wend End If Set oCont = Nothing Set oGC = Nothing Set oRecordset = Nothing Set oConnection = Nothing set msg = createobject("CDO.message") msg.To = email msg.From = folderemail msg.subject = "Welcome to "+group+cn msg.textbody = name+", thanks for subscribing to "+group+". If for any reason you would like to unsubscribe, send a message to "+folderemail+" and place the word UNSUBSCRIBE in the subject line. Thanks again! " msg.send End Sub '**************************************************************************************** Sub DeleteContactAndUnSubscribeIt On Error Resume Next dim group group = LCase(Trim(mid(workflowsession.fields("urn:schemas:httpmail:subject").Value, 12))) dim folderemail folderemail = "lists@theproexchange.com" email = workflowsession.fields( "urn:schemas:httpmail:fromemail" ).value name = workflowsession.fields( "urn:schemas:httpmail:fromname" ).value set msg = createobject("CDO.message") msg.To = email msg.From = folderemail msg.subject = "Goodbye" msg.textbody = name+", If for any reason you would like to subscribe again, send a message to "+folderemail+" and place the word SUBSCRIBE in the subject line." msg.send Set grp = GetObject("LDAP://CN="+group+", OU=lists, DC=BRYANTFAMILY, DC=COM") Dim oConnection Dim oRecordset Dim strQuery Dim stremail Dim oCont Dim oGC Dim strADsPath Set oCont = GetObject("GC:") For Each oGC In oCont strADsPath = oGC.ADsPath Next Set oConnection = CreateObject("ADODB.Connection") Set oRecordset = CreateObject("ADODB.Recordset") oConnection.Provider = "ADsDSOObject" 'The ADSI OLE-DB provider oConnection.Open "ADs Provider" strQuery = "<" & strADsPath & ">;(&(objectCategory=person)(mail=" & email & "));mail,cn,distinguishedName;subtree" Set oRecordset = oConnection.Execute(strQuery) Set grp = GetObject("LDAP://CN="+group+", OU=lists, DC=BRYANTFAMILY, DC=COM") If oRecordset.EOF And oRecordset.BOF Then Else While Not oRecordset.EOF grp.Remove ("LDAP://"+oRecordset.Fields("distinguishedName")) oRecordset.MoveNext Wend End If Set oCont = Nothing Set oGC = Nothing Set oRecordset = Nothing Set oConnection = Nothing End Sub
There are few alterations you will need to make to the shared code to run. All references to the domain and email addresses must be modified to match your environment. The text and message lines can be modified at will to change the messages in the notifications.
To create the process, build the following workflow process and create two actions:
Create two create actions with the following settings:
The Create action should call the IsSubscribe function for the condition execute the CreateContactAndSubscribeIt sub if true.
The Remove action should call the IsUnSubscribe function for the condition execute the DeleteContactAndUnSubscribeIt sub if true.
Next, activate the code from the General tab of the List folder from the Designer.. Lastly, set the workflow as the default for the folder by selecting the General tab of the workflow process itself from within the Designer program.
Save the settings and launch the Active Directory Users and Computers to add some sample groups you can use to test. For my tests, I created a couple of sample groups named Lista and listb. To test, I email a message to the public folder with the words subscribe lista then another with subscribe listb. Experiment with unsubscribe as well. Now remember there is a limit of accounts that can be contained in a single distribution group in the Active Directory.
When I get some time, I will probably build a little management page similar to that of the AutoDL tool in the resource kit and a feature that would allow users to find out the available lists by sending a message with the subject line of request lists or something along those lines. If anything, I hope this sample shows you some of the strengths of the Workflow Sink in Exchange 2000 and some of the other tasks that are possible with some light coding.