WinBatch Tech Support Home

Database Search

If you can't find the information using the categories below, post a question over in our WinBatch Tech Support Forum.

TechHome

ADSI LDAP CDO
plus
plus

Can't find the information you are looking for here? Then leave a message over on our WinBatch Tech Support Forum.

Exchange 2000, AD, CDO, and ADSI to Create a Mailbox

Keywords:   Exchange 2000 AD ADSI  msExchMailboxSecurityDescriptor

Question:

I have successfully created an AD User account with an Exchange 2000 mailbox using the ADSI extender (If anyone is interested I'll post it). Unfortunately, I can't figure out how to set the msExchMailboxSecurityDescriptor property. I'm confused on security descriptors anyway, but this is an odd one. On an Exchange 2000 server/domain, the user accounts have an "Exchange Advanced" tab. On this tab is a "Mailbox Rights" button. This button pulls up an ACL for the mailbox. It seems to be related to the msExchMailboxSecurityDescriptor property for the account, but I can't see how one would set it.

Anything you can tell me about this property would be helpful.

I have attempted to add the security descriptor: msExchMailboxSecurityDescriptor

After the program creates the user account, this property is found to be blank and I believe this is why I can't use the MMC to look at the mailbox rights. The program is my attempt to create a descriptor. Everything appears to work, but the dsSetProperty gives a 1062 error. I can find no error list for this extender. The message says: There was a constraint violation

Answer:

1062...
//
// MessageId: ERROR_SERVICE_NOT_ACTIVE
//
// MessageText:
//
// The service has not been started.
//
#define ERROR_SERVICE_NOT_ACTIVE 1062L
for a list of error codes see...
http://msdn.microsoft.com/library/psdk/psdkref/errlist_9usz.htm 
When you stated that the copy failed do you mean that do you mean that the function return an error and if so, what error? Or do you mean that the copied SD could not be set on the object property? For the rest of the post I will assume the latter.

Copying security objects is the same no matter the security object type. As indicated in the help file, you pass any "security object" in and the function returns a copy. As with all other ADSI functions the underlying object must support the operations for the function to succeed. Also, copying a security object does not guarantee that it will work. A security object may only make sense on the directory object that has it as a property value.

That said, the Winbatch.ini file error indicates that you have the error "The security descriptor is invalid." This is a win32 error. When ever the ADSI extender encounters an error that is not documented, it checks to see if the OS understands what is going on. When ever the extender does this and the OS returns an error message, the extender prepends the text "ADSI error:" to the OS generated error message. If you set error mode to @CANCEL in your script, you will most likely see the error message "ADSI error: The security descriptor is invalid."

The question then becomes: what is invalid about your SD? I do not know the answer. SD's are very temperamental things and not always well documented by the vender. I suggest using the ADSI extender to get a valid SD for an Exchange 2000 mailbox and then dump all of its properties, including the ACLs and all the ACEs in the ACLs. This may give some hint to the problem. I have included a script at the bottom of this post that I use to dump the SD's of Exchange 5.5 mailboxes. It needs some modification to work with Exchange 2000 but it should give you a start.

Also, I will do a documents search and try to identify the unique needs of Ex2k security descriptors.

"SD Dump" script example:

; Mailbox path pre-defined variables required.
sMailBoxPath = "LDAP://%server%/cn=%suserName%,cn=Recipients,OU=%Site%,O=""%Org%"""

; Get the security descriptor property.
sd = dsgetProperty(sMailBoxPath, "NT-Security-Descriptor")

; Display all SD properties.
ncontrol = dsGetSecProp(sd, "Control")
message("Control", ncontrol)
nRevision = dsGetSecProp(sd, "Revision")
message("Revision",nRevision)
Owner = dsGetSecProp(sd, "owner")
message("Owner", owner)
Ownerdefaulted = dsGetSecProp(sd, "OwnerDefaulted")
message("Ownerdefaulted ", Ownerdefaulted )
Group = dsGetSecProp(sd, "Group")
message("Group ", Group )
Groupdefaulted = dsGetSecProp(sd, "GroupDefaulted")
message("Groupdefaulted ", Groupdefaulted ) 
Dacldefaulted = dsGetSecProp(sd, "DaclDefaulted")
message("Dacldefaulted ", Dacldefaulted ) 
SaclDefaulted = dsGetSecProp(sd, "SaclDefaulted")
message("SaclDefaulted ", SaclDefaulted ) 
dacl = dsGetSecProp(sd, "DiscretionaryAcl")
ACLRevision = dsGetSecProp(dacl, "ACLRevision")

; ACL propreties
message("ACLRevision",ACLRevision)
AceCount = dsGetSecProp(dacl, "AceCount")
message("AceCount",AceCount)

;Step through each ACE 
; This is really tedious if you have a lot of them.
ACES = dsACLGetAces(dacl, 3)
nCount = ItemCount( ACES, @TAB)
for i=1 to nCount

ace = ItemExtract(i, ACES, @Tab)

; Display the ACE's properties.
AccessMask = dsGetSecProp(ace, "AccessMask")
message("AccessMask",AccessMask)
AceType = dsGetSecProp(ace, "AceType")
message("AceType",AceType)
AceFlags = dsGetSecProp(ace, "AceFlags")
message("AceFlags",AceFlags) 
Flags = dsGetSecProp(ace, "Flags")
message("Flags",Flags)
ObjectType = dsGetSecProp(ace, "ObjectType")
message("ObjectType",ObjectType)
InheritedObjectType = dsGetSecProp(ace, "InheritedObjectType")
message("InheritedObjectType",InheritedObjectType)
Trustee = dsGetSecProp(ace, "Trustee")
message("Trustee",Trustee)
next

Question (cont'd):

Thanks for the reply. Let me answer your question first - I When I attempted to copy this particular security descriptor, I got the same message I have saw when trying to create a security descriptor from scratch - Error 1062 There was a constraint violation. So, is 1062 anything in particular and what contraint is being referred to?

You said the winbatch.ini shows an invalid Security Descriptor. I saw only a message about a constraint. Are these one in the same?

Answer:

The 1062 constraint error is an ADSi error reported directly by the extender. The winBatch.ini file error (that can be deciphered based on the line "ErrorCode=8354") is an OS error. The error is written as additional information to the "ini" file when you get the 1062 error and it does indicate an invalid SD.

The scripts in the help file work for Exchange 5.5 and Win2k but are not written to work with Exchange 2000. In theory to create a mailbox for Ex2k you will need to create a Win2k user with ADSI and then associate the user with a mailbox using CDO as I mentioned in the previous post. I have not tried this so I am not familiar with all the gory details.

I have some good news and some bad news.

First the bad news:
According to MS you can not create a mailbox with ADSI on Exchange 2000. This means you can not use the ADSI extender to create the mailbox either. You can create a user but you can not create the Exchange mailbox for the user. You need to use the CDO function "CreateMailBox" to create the mailbox.

The good news:
Winbatch's ole functions support CDO objects and the "CreateMailBox" method. You may want to search the tech support database http://techsupt.winbatch.com for CDO examples.

I have not tried it so it would be interesting to hear how it works.

So you have to use OLE/CDO. It cannot be done with ADSI alone.

Question (cont'd):

I found this on the MSDN site. Here's an example of using ADSI to create an AD account, then CDO to create the mailbox. It's the best example I've seem. I will mess around with this, but I'm clueless when it comes to objects. Here's the code in VB:
Function ADSICreateMailBoxRecipient(ServerName As String, _
DomainName As String, _
emailname As String, _
FirstName As String, _
LastName As String) As Integer

'ServerName is something like "MyServer6"
'DomainName is something like "DC=MYDOMAIN3,DC=microsoft,DC=com"
'emailname is something like "jamessmith"

'this assumes the MDB to be "Private MDB"

Dim objUser As IADsUser
Dim objContainer As IADsContainer
Dim objMailbox As CDOEXM.IMailboxStore
Dim recipname As String, recip As String

recip = "CN=" & emailname

' get the container
Set objContainer = GetObject("LDAP://" + ServerName + "/" + _
"CN=users," + DomainName)

' create a recipient
Set objUser = objContainer.Create("User", recip)
objUser.Put "samAccountName", emailname
objUser.Put "sn", LastName
objUser.Put "givenName", FirstName
objUser.Put "userPrincipalName", emailname

objUser.SetInfo
objUser.SetPassword "password" 'let user change it later
objUser.AccountDisabled = False

Set objMailbox = objUser

'Create a mailbox for the recipient
'You cannot create a mailbox using ADSI, so use CDOEXM
objMailbox.CreateMailbox "LDAP://" + ServerName + _
"/CN=Private MDB" + _
",CN=First Storage Group,CN=InformationStore,CN=" + _
ServerName + _
",CN=Servers,CN=First Administrative Group," + _
"CN=Administrative Groups,CN=First Organization," + _
"CN=Microsoft Exchange,CN=Services," + _
"CN=Configuration," + DomainName
objUser.SetInfo

End Function
Here's the URL:
"http://msdn.microsoft.com/library/psdk/exchsv2k/_cdo_imailboxstore_createmailbox.htm
I know the ojbMailBox.createmailbox refers to an object/method contained within CDOEXM.DLL. CDOEXM is Collaborative Data Objects for Exchange Management and is installed on all Exchange 2000 servers under the Exchsrv\bin dir. I have no idea how to get Winbatch to make this call, but I will start trying to understand objects and go from there.

I am trying to use the IMailStore method in the CDOEXM.DLL to create a mailbox for an AD user that already exists. I know little about objects, so this may be a dumb question - How does winbatch access a method in a DLL? The VB sample code shows a "DIM var as CDOEXM.IMailboxstore". This seems to be where the link to the CDOEXM object is made.

The CDOEXM DLL is registered on the server on which my code is executing. An Object viewer tool I found lists this method and interface as being present on the system. It seems I am only lacking how to open this object by name. I tried:

objmailbox=Objectopen("CDOEXM.IMailboxstore")
ObjMailbox.createmailbox(xxx)
and the objectopen() failed to initiate.

So, is there a way to access this method without using IADS objects first?

THIS REPORTED SOMETIME LATER...

I was able to open the CDO.Person object, open a particlar user account and read properties:

UserPath = "LDAP://cn=%displayname%,cn=users,%domain%"

objperson=objectopen("CDO.Person")
OPdatasource=objperson.datasource
opdatasource.open(userpath)

thisfn=objperson.firstname
thisln=objperson.lastname
Where userpath is the same LDAP url value used for with the ASDI extender calls.

I'm still very unclear about the object-interface-method naming convention. I can't find any documentation of the COM objects to tell me where objperson.datasource.open() comes from. The "Open" method under CDO.Person object is found under the IDatasource interface. This implies you drop the "I" and use the interface name when accessing an object. That means to access the createmailbox method under the IMailboxStore interface you'd do the following:

opMB=objperson.mailboxstore
opMB.createmailbox=(xxx)
but the first line fails with a OLE bad name error. That means to me the naming convention we spoke about doesn't apply. I tried various object names and got the same error - bad name. My COM Object Viewer showed createmailbox as a method under IMailboxStore interface under the CDO.Person object, but that is the only indicator that the MS object is CDO.Person.mailboxstore.createmailbox or CDO.person.IMailboxstore.createmailbox.

I have found no sample code other than what we have here.

Here's one more url that says the IMailboxStore interface is under the cdo.person object, but there's no examples of syntax:

http://msdn.microsoft.com/library/psdk/exchsv2k/_cdoex_cdo_for_exchange_2000_server.htm
I'll ask my buddy about the syntax for these objects and about that set command where the mailbox object is set to the person object.

Answer:

There seems to be some kind of GetInterface method. Here is some sample code I found in my searches, that should be helpful...
FirstName="First Name"
LastName="Last Name"
LoginName="login"
objPerson = ObjectOpen("CDO.Person")
objPerson.FirstName=FirstName
objPerson.LastName=LastName
objPersonFields = objPerson.Fields
objPersonFields("mailNickname")= LoginName
objPersonFields("userPrincipalName")= LoginName
objPersonFields("userAccountControl")= 512
objPersonFields("userPassword")= LoginName
objPersonFields("samAccountName")= LoginName
objPersonFields.Update
objPersonDS = objPerson.DataSource
objPersonDS.SaveTo(strcat("LDAP://exchange2000/cn",FirstName," ",LastName,",oubizOA Evaluation Accounts,dcbizoa,dccom")
objMailbox=objPerson.GetInterface("IMailboxStore")

str1 = "LDAP://EXCHANGE2000/CNMailbox Store(EXCHANGE2000),CNLocal HDD(36GB),CNInformationStore,CNEXCHANGE2000"
str2 = ",CNServers,CNbizOA Administrative Group,CN­ministrative Groups,CNMediaSolv,CNMicrosoftExchange,CNServices,"
str3 = "CNConfiguration,DCbizoa,DCcom"

LDAPstuff = strCat(str1,str2,str3)

objMailbox.CreateMailbox(LDAPstuff)

str1 = "LDAP://EXCHANGE2000/CNMailbox Store(EXCHANGE2000),CNFirst StorageGroup,"
str2 = "CNInformationStore,CNEXCHANGE2000,CNServers,CNFirst AdministrativeGroup,CN­ministrative Groups"
str3 = ",CNMediaSolv,CNMicrosoft Exchange,CNServices,CNConfiguration,DCbizoa,DCcom"

LDAPstuff = strCat(str1,str2,str3)
objMailbox.CreateMailbox(LDAPstuff)
objPersonDS.Save
The line - "objMailbox=objPerson.GetInterface("IMailboxStore")" does work. I have tried it on our local E2k machine. The return value is the mailbox interface with the desired "CreateMailbox" method. MS is a bit unclear on when you can use the "dot" notation instead of the "GetInterface" method to obtain a different interface on an object. All in all, however, the CDO COM server has a much, much cleaner design than the ADSI COM design. They at least try to preserve the interfaces idea in the scripting context with CDO.

Anyway, I have not been able to create a mailbox locally yet. I use the ADSI extender to create a user then use WinBatch's Createobject to create a person. I attach my ADSI extender to the persion object and get the IMailbox interface but the Createmailbox method fails. It appears I do not have the correct common names in the passed in URL.

Grrrrr.

Question:

Success at last!

Thank you all! Here the code that I works:

After using the ADSI extender to create the user and set all properties, the following is executed, the "open" accesses the user account (userpath is the same values used in the ADSI extender to create the user account.

objperson=objectopen("CDO.Person")
OPdatasource=objperson.datasource
opdatasource.open(userpath)

thisfn=objperson.firstname
thisln=objperson.lastname

objMailbox=objPerson.GetInterface("IMailboxStore")

homeMDB="CN=Mailbox Store (%homeserver%),CN=First Storage
Group,CN=InformationStore,CN=%homeserver%,CN=Servers,CN=First Administrative
Group,CN=Administrative Groups,CN=%subdom%,CN=Microsoft
Exchange,CN=Services,CN=Configuration,%domain%"

objmailbox.CreateMailbox(homemdb)
HomeMDB was taken from an account & mailbox made with the MMC on my domain. I suggest you'll need to do the same to get it to work on your side. I have no clue where the "getinterface" came from, but I'll take it. Be aware that it is my experience that the mailbox does not show up in the mmc until an e-mail is sent to it - so you may have already created mailboxes successfully.

I know this isworking because I can now access the security discriptor under the "Mailbox Rights" button. Note also that this descriptor does not have the same ACL as when it is made by the MMC wizard, but I'll start work on that next. Once this is done as we can make an account that looks just like one made by the MMC wizard, you might want to more completely document it so that others will be able to use it without much effort.

Answer:

I too have success. I found a typo in the homeDB URL and everything worked fine once it was fixed. The ADSI extender should be able to modify your Mailbox rights for you as long as you use credentials with sufficient privileges. MS does have some documentation on CDO on their Web Storage system SDK and on their MSDN site under the .net area. I am not sure if the WSS SDK is a free download or not.

Question (cont'd):

Progress so far:
I can create user accounts with mailboxes that can recieve mail. There is no longer an error When the user's mailbox rights are accessed with the MMC. When mailbox rights are accessed after account creation, an ACL appears that contains inherited permissions and one non-inherited ACE - SELF, but all permissions are unchecked for this ACE. I cannot explain this, but when I examined the ACE using the Advanced button, the SELF ACE shows Full Mailbox rights in a non-inherited ACE!!!!

When the MMC new user wizard is run to create an account and mailbox - SELF is the only ACE and it has the Full Mailbox right and read permissions right. When the user logs on for the first time, this ACL gains all the inherited permission seen when the mailbox is created programmatically.

The Good News:
A user can log on and use e-mail with the account in this condition. I only wonder why it is this way and what harm it is to create thousands of accounts like this.

It seems that with the ADSI extender one should be able to set this security descriptor however one wants - and clean this mess up left by defaults. The problem is I haven't been able to do this. I had assumed the User Account property msExchMailboxSecurityDescriptor housed this ACL (sounds like it should), but it doesn't seem to work that way.

More Info:
Prior to using the MMC to access mailbox rights, the user property msExchMailboxSecurityDescriptor has no value. After doing so, it has a value. It seems this value is applied by the Recpients Update Service. A technet article states that the Mailbox Security Descriptor is not created until the user logs into the mailbox or the mailbox receives mail. See:

http://support.microsoft.com/support/kb/articles/q272/1/53.asp
Using the ADSI extender, I found that the msExchmailboxSecurityDescriptor had no value until after the user logged into mail (sending e-mail showed up in the mailbox, but there was still no value in msExhcMailboxSecurityDescriptor) or I pulled up the Mailbox Rights ACL (this worked only after a time had passed after creating the account & mailbox).

It seems the msExchMailboxSecurityDescriptor property is not actually the security Descriptor the MMC sets under Mailbox Rights. I believe this because When I use the MMC to change an ACE (e.g. the Self ACE), the msExchMaiboxSecurityDescriptor show the change made (checking one box results in a one bit change in the AccesssControl security property for that ACE). Unfortunately, when one makes the same or any change to this property programmatically, it does not show up under the MMC mailbox rights button, but it does show up when the property is re-queried programmatically. So, I conclude the MMC or some other agent is updating the msExchMailboxSecurityDescriptor property, but there must be some other security descriptor that is the source for this.

It could be that the mailbox is actually a separate object from the user and the Security Descriptor seen under the MMC are ACLs for the mailbox itself - perhaps the NTsecuritydescriptor property. I have looked all over for the mailbox object path but have had no luck.

Does anyone know if a mailbox is a separate object and if so, where it is in the directory?

More to the point, does anyone have a clue where the Mailbox Rights ACL seen in the MMC is actually stored. If we can't set this ACL programmatically, it will be difficult to create Win2K/Exchange2K accounts in Winbatch.

Thanks again for all the help.

Answer:

ACE inheritance is not automatically perform on Active Directory objects so it does not surprise me that you do not see inherited ACEs showing up until the user logs in. The .net area on MS's MSDN www site has information on the schema modifications made to Active Directory by the installation of Exchange 2000. This is probably the best place to look for info. about E2k related security properties.

Question (cont'd):

I've been testing what I have and I am finding that it is working acceptably. I have been working in vain to set the Mailbox Rights ACL to look like an account made manually - mostly because the SELF ACE had no permissions checked when you examine it with the MMC. It turns out, however, if you access the Advanced button one the MailBox Rights dialog, you'll find that the SELF ACE has Full Mailbox rights. The difference between the progam-made account and the hand-made account is that the ACE for the program-made account specifies "this object only" for the Full Mailbox Rights permission while the hand-made account specifies "this object and subcontainers". Also missing is "read permissions", but that is granted by inheritance for Everyone.

I don't know what subcontainers can exist under a User Account Object or Mailbox (if it's a separate object), but my test have shown everything works fine from the desktop.

I'll test everything more thoroughly and let you know if we go ahead with this as-is. It still makes no sense that I can't edit that ACL. There are 30 examples showing how to do that in Exchange 5.5, so we'll have to be able to modify eventually.

PS - It's a real bear when you can't believe what you see in an ACL w/o going to the Advanced button. I believe I've seen the same nasty behavior in NTFS file security. Ain't Win2K wonderful?

Answer:

  1. Please post the final version of the code when done.

  2. Yes, there seem to be a log of MS band-aids around ACLs. They have been giving us remarkable fits also.

Resolution:

Here's the working version of the NewUser program. Thanks again for all the help you provided.

A few caviats:


NEWUSER.WBT

;generic debug setting
progname=itemextract(1,winexename(""),".")

gosub dbgchk
gosub constants

addextender("WWADS34I.DLL")
addextender("wwwnt34i.dll")
AddExtender("wwwsk34i.dll ")

IntControl( 52, 0, 0, 0, 0 )

types="User Templates (*.usr)|*.usr|Text Files (*.txt)|*.txt|All Files (*.*)|*.*|"

ACCESS_ALLOWED = 0            ;  AceType
GENERIC_ALL    = 268435456    ; AccessMask

currentdir = dirget()
;load default user template
filename=strcat(currentdir,"default.usr")
gosub loadtemplate

while @true

   gosub getinfo

; Define some constants.
   MANDATORY  = 1
   OPTIONAL   = 2
   MANANDOPT  = 3

   sAdsiPath = "LDAP://rootDSE"

   sValue = dsGetProperty(sAdsiPath , "defaultNamingContext")             

   sDomainDNS = "LDAP://%sValue%"

   domain = dsGetProperty("LDAP://rootDSE"  , "defaultNamingContext")

   UsersPath = "LDAP://cn=users,%domain%"

   xxx=itemextract(1,domain,",")
   xxx=itemextract(2,xxx,"=")
   yyy=itemextract(2,domain,",")
   yyy=itemextract(2,yyy,"=")
   subdom=xxx
   dnsdomain1=strcat(xxx,".",yyy)
   dnsdomain2=strcat(xxx,".com")

   if MI <> ""
      if strsub(MI,strlen(MI),1) == "." 
         displayname=strcat(FirstName," ",MI," ",LastName)
      else
         displayname=strcat(FirstName," ",MI,". ",LastName)
      endif
   else
      displayname=strcat(FirstName," ", LastName)
   endif

   displayname=strtrim(displayname)
   username=strlower(strtrim(strfixchars(strcat(strsub(firstname,1,1),lastname)," ",15)))
;   Memberof="CN=Home_%homeserver%_G,CN=Users,DC=tc1,DC=priv"

   ; List all objects in the user container of the default domain.
   ;lValues = dsGetChldPath(UsersPath, "")
   ;message("Object in Users", lValues)

      ; Create a user in the default user container on a 
   ; Windows 2000 server with Active directory.

   ; Create a new user object
   sObjectClass = "user"
   
   while @true
      UserPath = "LDAP://cn=%displayname%,cn=users,%domain%"
      err=dsIsObject(UserPath)
      if err==1 
         newname = askline("User Name Already Exists","User %displayname% already exists.  Please enter a new Full Name for this user account.", displayname)
         if newname <> ""
            displayname= newname 
         endif
      else
         break
      endif
   endwhile
   
   while @true
      err=dsFindPath(UsersPath,"SamAccountName=%username%")

      if err<>"" 
         newname = askline("User Name Already Exists","User %Username% already exists.  Please enter a new User Name for this user account.", username)
         if newname <> ""
            username= newname 
         endif
      else
         break
      endif
   endwhile

   profilepath=strcat("\\",homeserver,"\profiles$\",username)
   sObjectPath  = dsCreateObj(UsersPath, sObjectClass, strcat("cn=",displayname))

   mail=strcat(username,"@",dnsdomain2)
   mailnickname=username

   ;
   ; Set the mandatory property. 
   dsSetProperty(sObjectPath, "samAccountName", username)

   ;set optional properties
   dsSetProperty(sObjectPath, "c", "US")
   dsSetProperty(sObjectPath, "co", "United States")
   if companyname   <> "" then dsSetProperty(sObjectPath, "company", CompanyName)
   dsSetProperty(sObjectPath, "CountryCode", "840" )
   if department    <> "" then dsSetProperty(sObjectPath, "Department", department)
   if description   <> "" then dsSetProperty(sObjectPath, "description", description)
   if displayname   <> "" then dsSetProperty(sObjectPath, "displayname", displayname)
   if faxnumber     <> "" then dsSetProperty(sObjectPath, "FacsimileTelephoneNumber", faxnumber )
   if firstname     <> "" then dsSetProperty(sObjectPath, "givenname", FirstName )
   if homephone     <> "" then dsSetProperty(sObjectPath, "HomePhone", homephone)
   if MI            <> "" then dsSetProperty(sObjectPath, "initials", MI)
   if city          <> "" then dsSetProperty(sObjectPath, "L", city)
   ;dsSetProperty(sObjectPath, "MemberOf", memberof)
   if mobilephone   <> "" then dsSetProperty(sObjectPath, "Mobile", mobilephone)
   if PagerNumber   <> "" then dsSetProperty(sObjectPath, "Pager", pagernumber)
   if zipcode       <> "" then dsSetProperty(sObjectPath, "PostalCode", ZipCode)
   if profilepath   <> "" then dsSetProperty(sObjectPath, "profilepath", profilepath)
   if logonscript   <> "" then dsSetProperty(sObjectPath, "scriptpath", logonscript)
   if lastname      <> "" then dsSetProperty(sObjectPath, "SN", lastname)
   if state         <> "" then dsSetProperty(sObjectPath, "ST", State)
   if address       <> "" then dsSetProperty(sObjectPath, "StreetAddress", Address)
   if workphone     <> "" then dsSetProperty(sObjectPath, "TelephoneNumber", WorkPhone)
   if mail          <> "" then dsSetProperty(sObjectPath, "userprincipalname", mail)

   ;e-mail properties:
   homeMDB="CN=Mailbox Store (%homeserver%),CN=First Storage Group,CN=InformationStore,CN=%homeserver%,CN=Servers,CN=First Administrative Group,CN=Administrative Groups,CN=%subdom%,CN=Microsoft Exchange,CN=Services,CN=Configuration,%domain%"
   homeMTA="CN=Microsoft MTA,CN=%homeserver%,CN=Servers,CN=First Administrative Group,CN=Administrative Groups,CN=%subdom%,CN=Microsoft Exchange,CN=Services,CN=Configuration,%domain%"
   LegacyExchangeDN="/o=%subdom%/ou=First Administrative Group/cn=Recipients/cn=%username%"
   msExchHomeServerName="/o=%subdom%/ou=First Administrative Group/cn=Configuration/cn=Servers/cn=%homeserver%"
   mdbUseDefaults="1"

   if mail              <> "" then dsSetProperty(sObjectPath, "mail",mail)
   if mailnickname  <> "" then dsSetProperty(sObjectPath, "MailNickName", MailNickName)

   if homeMDB           <> "" then dsSetProperty(sObjectPath, "homemdb", homemdb)
   if homeMTA           <> "" then dsSetProperty(sObjectPath, "homemta", homemta)
   if legacyExchangeDN  <> "" then dsSetProperty(sObjectPath, "legacyexchangedn", legacyexchangedn)
   if msExchHomeServerName <> "" then dsSetProperty(sObjectPath, "msExchHomeServerName", msExchHomeServerName)
   if mdbUseDefaults       <> "" then dsSetProperty(sObjectPath, "mdbUseDefaults",mdbUseDefaults)
 
   ; Now, commit the object to the DS
   dsSetObj(sObjectPath)     

   ;password may not be accepted by domain
   errormode(@off)
   While @true
      lasterror()
      dsSetPassword(sObjectPath, "", password)
      if lasterror() == 1026
         gosub fixpwd
      else
         break
      endif       
   endwhile
   errormode(@notify)

   ;can't enable the account until the password is set 
   dsSetProperty(sObjectPath, "UserAccountControl", "512")
   dsSetProperty(sObjectPath, "msExchUserAccountControl","0")
   
   debugtrace(1,"%progname%.log") 

   ; now switch to CDO COM object to create the mailbox
   ; Open the  CDO.Person object and go to the account just created
   objperson=objectopen("CDO.Person")
   OPdatasource=objperson.datasource
   opdatasource.open(userpath)

   thisfn=objperson.firstname         ; unnecessary, but it shows you are on the correct user account
   thisln=objperson.lastname

   ;don't know where the getinterface method came from or why we need to call it, but it works
   objMailbox=objPerson.GetInterface("IMailboxStore")
   ;Use the same homeMDB used when creating the account
   objmailbox.CreateMailbox(homemdb)

   ;no need to call the save method, we changed nothing, so just close the object

   objectclose(objperson)

   ; give the e-mail server a chance to create the mailbox - come back to this later
   
   ;Now add group membership:
   groupname = "Home_%homeserver%_G"
   grouppath = "LDAP://cn=%groupname%,cn=users,%domain%"
   dsAddtoGrp(GroupPath, UserPath)

   ;Make home dir
   err=dirmake("\\%homeserver%\home$\%username%")
   ;make required dirs
   err=dirmake("\\%homeserver%\home$\%username%\My Documents")
   err=dirmake("\\%homeserver%\home$\%username%\Application Data")

   ;Make profile dir
   err=dirmake("\\%homeserver%\profiles$\%username%")

   ;Boxopen("Checking For Server Synchronization","")
   errormode(@off)
   
   ;wait for the user to show up at the server
   ;this may be unnecessary if the server is a Win2K DC
   ;but can be needed if homeserver is not the DC changes are
   ;being made on - AD can take a few secs to replicate

   t=0
   tm = 15 ;secs
   x = 0
 
   while t==0
      groups=wntMemberGrps("\\%homeserver%",username,@GLOBALGROUP,0)

      err=lasterror()

      if err == 562 
         x=x+(tm/60.0) ;min

         ;boxtext("Waiting for %param2% account%@CRLF%to appear on %param1%%@crlf%Time waited So Far: %x% min")
         timedelay(tm)

      else 
         t=1
      endif

   endwhile
   ;boxshut()
   errormode(@notify)


   ;  Homeserver knows about the new user account, so  we can now set ACLS
   err=wntAccessAdd(homeserver, "\\%homeserver%\home$\%username%", username, 303, "Win2000:Modify")
   err=wntAccessAdd(homeserver, "\\%homeserver%\profiles$\%username%", username, 303, "Win2000:Modify")

   ; send a message to this mailbox to make sure it gets created now, and to create the ACL
   ; Messages sent to this account immediately after account creation seem to fail.
   ; shell hidden program to send mail in 3 minutes
   ; I choose SMTP so I need no e-mail client.  This program will be run from the e-mail server.
   ; Server must be able to accept SMPT messages from internal addresses.
   ; Server must be able to resolve the from address (in most cases)
   ;  could use MAPI, POSTIE or SMTP.
   ; param1 = full server name
   ; param2 = users Internet e-mail address
   ; param3 = delay time 

   tm = timeymdhms()
   x=strcat(homeserver,".",dnsdomain1)
   y=mail

   err=runshell("emailwelcome.exe","%x% %mail% 180","",@hidden,@nowait)
   
   department=""
   description=""
   FirstName=""
   LAstName=""
   MI=""
   workphone=""
   pagernumber=""
   faxnumber=""
   homephone=""
   mobilephone=""
   pw_password=""
   password=""
   pw_confirm=""
   confirm=""
   Title=""   

endwhile   ;
exit

;===================================================================
:getinfo

MyDialogFormat=`WWWDLGED,5.0`

MyDialogCaption=`UnaVia New User`
MyDialogX=187
MyDialogY=109
MyDialogWidth=359
MyDialogHeight=230
MyDialogNumControls=47

MyDialog01=`130,0,92,DEFAULT,STATICTEXT,DEFAULT,"Create a New User Account"`
MyDialog02=`14,20,40,DEFAULT,STATICTEXT,DEFAULT,"First Name:"`
MyDialog03=`8,32,46,DEFAULT,STATICTEXT,DEFAULT,"Middle Initial:"`
MyDialog04=`14,44,40,DEFAULT,STATICTEXT,DEFAULT,"Last Name:"`
MyDialog05=`6,56,48,DEFAULT,STATICTEXT,DEFAULT,"Home Server:"`
MyDialog06=`8,68,46,DEFAULT,STATICTEXT,DEFAULT,"Logon Script:"`
MyDialog07=`10,80,44,DEFAULT,STATICTEXT,DEFAULT,"Work Phone:"`
MyDialog08=`8,128,46,DEFAULT,STATICTEXT,DEFAULT,"Home Phone:"`
MyDialog09=`4,92,50,DEFAULT,STATICTEXT,DEFAULT,"Pager Number:"`
MyDialog10=`12,140,42,DEFAULT,STATICTEXT,DEFAULT,"Department:"`
MyDialog11=`10,116,44,DEFAULT,STATICTEXT,DEFAULT,"FAX Number:"`
MyDialog12=`6,104,48,DEFAULT,STATICTEXT,DEFAULT,"Mobile Phone:"`
MyDialog13=`12,152,42,DEFAULT,STATICTEXT,DEFAULT,"Description:"`
MyDialog14=`22,164,36,DEFAULT,STATICTEXT,DEFAULT,"Address:"`
MyDialog15=`36,176,36,DEFAULT,STATICTEXT,DEFAULT,"City:"`
MyDialog16=`34,188,36,DEFAULT,STATICTEXT,DEFAULT,"State:"`
MyDialog17=`22,200,36,DEFAULT,STATICTEXT,DEFAULT,"Zip Code:"`
MyDialog18=`56,18,134,DEFAULT,EDITBOX,FirstName,"FirstName"`
MyDialog19=`56,30,36,DEFAULT,EDITBOX,MI,"MI"`
MyDialog20=`56,42,134,DEFAULT,EDITBOX,LastName,"LastName"`
MyDialog21=`200,30,134,DEFAULT,EDITBOX,PW_password,""`
MyDialog22=`200,54,134,DEFAULT,EDITBOX,PW_Confirm,""`
MyDialog23=`56,54,134,DEFAULT,EDITBOX,HomeServer,"HomeServer"`
MyDialog24=`56,66,134,DEFAULT,EDITBOX,logonscript,"LogonScript"`
MyDialog25=`56,78,134,DEFAULT,EDITBOX,WorkPhone,"WorkPhone"`
MyDialog27=`56,90,134,DEFAULT,EDITBOX,Pagernumber,"PagerNumber"`
MyDialog28=`56,102,134,DEFAULT,EDITBOX,MobilePhone,"MobilePhone"`
MyDialog29=`56,114,134,DEFAULT,EDITBOX,FAXNumber,"FAXNumber"`
MyDialog30=`56,126,134,DEFAULT,EDITBOX,HomePhone,"HomePhone"`
MyDialog31=`56,138,134,DEFAULT,EDITBOX,Department,"Department"`
MyDialog32=`56,150,134,DEFAULT,EDITBOX,Description,"Description"`
MyDialog33=`56,162,134,DEFAULT,EDITBOX,Address,"Address"`
MyDialog35=`56,174,134,DEFAULT,EDITBOX,City,"City"`
MyDialog36=`56,186,36,DEFAULT,EDITBOX,State,"State"`
MyDialog37=`56,198,134,DEFAULT,EDITBOX,ZipCode,"ZipCode"`
MyDialog38=`56,210,134,DEFAULT,EDITBOX,CompanyName,"Company Name"`
MyDialog39=`196,192,74,DEFAULT,PUSHBUTTON,DEFAULT,"&Create Account",1`
MyDialog40=`278,192,74,DEFAULT,PUSHBUTTON,DEFAULT,"&Load Template",2`
MyDialog41=`278,210,74,DEFAULT,PUSHBUTTON,DEFAULT,"&Save Template",3`
MyDialog42=`196,210,74,DEFAULT,PUSHBUTTON,DEFAULT,"Ca&ncel",4`
MyDialog43=`20,212,36,DEFAULT,STATICTEXT,DEFAULT,"Company:"`
MyDialog44=`202,18,36,DEFAULT,STATICTEXT,DEFAULT,"Password:"`
MyDialog45=`202,42,36,DEFAULT,STATICTEXT,DEFAULT,"Confirm:"`
MyDialog46=`202,66,36,DEFAULT,STATICTEXT,DEFAULT,"Title:"`
MyDialog26=`200,78,134,DEFAULT,EDITBOX,Title,"Title"`
MyDialog47=`202,150,36,DEFAULT,STATICTEXT,DEFAULT,"P.O. Box:"`
MyDialog34=`200,162,136,DEFAULT,EDITBOX,POBOX,"POBox"`

ButtonPushed=Dialog("MyDialog")


  
select buttonpushed

   case 1
      password = pw_password
      confirm  = pw_confirm
      if password <> confirm
         message("Password error","Password Not Confirmed.  Please re-enter password and confirm.")
         break
      endif
      return
   case 2
      filename=AskFileName("Load User Template", currentdir, types, "Default.usr", 1)
      if filename == "" then break
      password = ""
      confirm = ""
      gosub loadtemplate
      break
   case 3 
      gosub savetemplate
      break
   case 4
      exit
endselect

goto getinfo

;===================================================================
:dbgchk

if fileexist("%progname%.dbg") 
   debugtrace(1,"%progname%.log") 
else
   debugtrace(0,"%progname%.log")
endif 

return
;====================================================================
:loadtemplate

if fileexist(filename)
   hndl=fileopen(filename,"read")

   while @true
      xxx=fileread(hndl)
      if xxx == "*EOF*" then break
      ;line in file is of form propname = "value"
      %xxx%
   endwhile

   err=fileclose(hndl)
endif

return
;====================================================================
:savetemplate
filename=AskFileName("Save User Template", currentdir, types, "Default.usr", 0)

if filename == "" then return

hndl=fileopen(filename,"Write")

err=filewrite(hndl,'firstname = "%firstname%"')
err=filewrite(hndl,'MI = "%mi%"')
err=filewrite(hndl,'lastname = "%lastname%"')
err=filewrite(hndl,'homeserver = "%homeserver%"')
err=filewrite(hndl,'logonscript = "%logonscript%"')
err=filewrite(hndl,'workphone = "%workphone%"')
err=filewrite(hndl,'pagernumber = "%pagernumber%"')
err=filewrite(hndl,'mobilephone = "%mobilephone%"')
err=filewrite(hndl,'faxnumber = "%faxnumber%"')
err=filewrite(hndl,'HomePhone = "%HomePhone%"')
err=filewrite(hndl,'Department = "%Department%"')
err=filewrite(hndl,'Description = "%description%"')
err=filewrite(hndl,'Address = "%address%"')
err=filewrite(hndl,'city = "%city%"')
err=filewrite(hndl,'State = "%state%"')
err=filewrite(hndl,'zipcode = "%zipcode%"')
err=filewrite(hndl,'Companyname = "%companyname%"')
err=filewrite(hndl,'title = "%title%"')
err=filewrite(hndl,'POBox = "%pobox%"')



;err=filewrite(hndl,' = "%%"')

err=fileclose(hndl)
return

;====================================================================
:fixpwd
while @true

MyDialogFormat=`WWWDLGED,5.0`

MyDialogCaption=`Password Does Not Pass Minimum Requirements`
MyDialogX=89
MyDialogY=115
MyDialogWidth=293
MyDialogHeight=110
MyDialogNumControls=9

MyDialog01=`12,8,174,DEFAULT,STATICTEXT,DEFAULT,"The password you entered did not pass the minimum"`
MyDialog02=`186,8,100,DEFAULT,STATICTEXT,DEFAULT,"requirements for this domain."`
MyDialog03=`12,20,184,DEFAULT,STATICTEXT,DEFAULT,"The password may be too short or not complex enough."`
MyDialog04=`12,32,142,DEFAULT,STATICTEXT,DEFAULT,"Please Enter a new password and confirm:"`
MyDialog05=`12,48,38,DEFAULT,STATICTEXT,DEFAULT,"Password:"`
MyDialog06=`12,60,132,DEFAULT,EDITBOX,pw_password,""`
MyDialog07=`12,76,36,DEFAULT,STATICTEXT,DEFAULT,"Confirm:"`
MyDialog08=`12,88,132,DEFAULT,EDITBOX,pw_confirm,""`
MyDialog09=`210,88,64,DEFAULT,PUSHBUTTON,DEFAULT,"&OK",1`

ButtonPushed=Dialog("MyDialog")


   password = pw_password
   confirm  = pw_confirm
   if password <> confirm
      message("Password error","Password Not Confirmed.  Please re-enter password and confirm.")
      continue
   endif
   break

endwhile 
return

;=====================================================================================

:Constants
; This file contains a list of constants commonly used with the ADSI extender.


; Meaning of bits in userFlags properties of a WinNT: and the userAccountControl property of a LDAP: namespaces' user object.
; Operating System = Windows NT 4.0 / Windows 2000
; Namespace    	 = WinNT, LDAP
; object class 	 = user
; properties     	 = userFlags (WinNT), userAccountControl (LDAP on Windows 2000)

;  The following used with both userFlags and  userAccountControl
UF_SCRIPT                     = 1		  ; The logon script will be executed.  
UF_ACCOUNTDISABLE             = 2		  ; The user's account is disabled. 
UF_HOMEDIR_REQUIRED           = 8		  ; The home directory is required. 
UF_LOCKOUT                    = 16		  ; The account is currently locked out. 
UF_PASSWD_NOTREQD             = 32		  ; No password is required. 
UF_PASSWD_CANT_CHANGE         = 64		  ; The user cannot change the password. You can read this flag 	 
													  ; but you cannot set it directly. 
UF_DONT_EXPIRE_PASSWD         = 65536	  ; The password, which should never expire on the account.  
UF_TEMP_DUPLICATE_ACCOUNT     = 256		  ; This is an account for users whose primary account is in another domain. 
													  ; This account provides user access to this domain, but not to any domain 
													  ; that trusts this domain. Sometimes it is referred to as a local user account. 
UF_NORMAL_ACCOUNT             = 512		  ; This is a default account type that represents a typical user. 
UF_INTERDOMAIN_TRUST_ACCOUNT  = 2048	  ; This is a permit to trust account for a system domain that trusts other domains. 
UF_WORKSTATION_TRUST_ACCOUNT  = 4096	  ; This is a computer account that is a member of this domain. 
UF_SERVER_TRUST_ACCOUNT       = 8192	  ; This is a computer account for a system backup domain controller that is a member of this domain. 

; The following used with userFlags only.
UF_ENCRYPTED_PASSWORD_ALLOWED = 128      ; The user can send an encrypted password.   (Windows 2000 only)
UF_MNS_LOGON_ACCOUNT          = 131072	  ; This is an MNS logon account. 
UF_SMARTCARD_REQUIRED         = 262144	  ; When set, this flag will force the user to log on using smart card.  (Windows 2000 only)
UF_TRUSTED_FOR_DELEGATION     = 524288	  ; When set, the service account (user or computer account), under which a service runs, 
													  ; is trusted for Kerberos delegation. Any such service can impersonate a client requesting 
													  ; the service. To enable a service for Kerberos delegation, you must set this flag on the 
													  ; userAccountControl property of the service account.  (Windows 2000 only)
UF_NOT_DELEGATED              = 1048576  ; When set, the security context of the user will not be delegated to a service even 
													  ; if the service account is set as trusted for Kerberos delegation


; Guid used to prevent a user from changing their password.
; Operating System  = Windows 2000
; Namespace    	  = LDAP:
; Object class 	  = user
; Property     	  = ntSecurityDescriptor
; Security object   = ACE
; Security Property = ObjectType

CHANGE_PASSWORD_GUID = "{ab721a53-1e2f-11d0-9819-00aa0040529b}"


; Constant used it indicate that a user has unlimited disk storage rights.
; Operating System = Windows NT 4.0 / Windows 2000
; Namespace    	 = LDAP:
; object class 	 = user
; properties     	 = maxStorage 

USER_MAXSTORAGE_UNLIMITED = -1  ; Use all available disk space. 


; Meaning of bits in groupType property of a LDAP namespace's group object.
; Operating System = Windows 2000
; Namespace    	 = LDAP
; object class 	 = group
; properties     	 = groupType

GLOBAL_GROUP         = 2          ; Group that contains only accounts and other account groups from its own domain. 
							             ; This group may be exported to a different domain. 
DOMAIN_LOCAL_GROUP   = 4          ; Group that can contain accounts and universal groups from any domains. It may 
                                  ; not be included in either access-control lists of resources in other domains or 
							             ; groups other than global groups in the same domain. 
LOCAL_GROUP 			= 4          ; This bit is for the WinNT provider as the DOMAIN_LOCAL_GROUP bit 
                                  ; is for the LDAP provider. 
UNIVERSAL_GROUP 	   = 8          ; Group that can contain accounts and account groups from any domains, but not domain local groups. 
SECURITY_ENABLED 	   = 2147483648 ; If this bit is set, the group is a security group. If this bit is not set, 
											 ; the group is a distribution group. 


; The following values are for security objects all accessed through the ntSecurityDescriptor property.
; Some can also be used with the NT-Security-Descriptor property of a mailbox object

; Current revision of security descriptor .
; Operating System = Windows 2000
; Namespace    	 = LDAP
; Security object  = Security Descriptor 
; Property         = Revision

ACL_REVISION       = 2
ACL_REVISION_DS    = 4   ; If the DACL contains an object-specific ACE you must use this.


; Current revision of Access Control List.
; Operating System = Windows 2000
; Namespace    	 = LDAP
; Security object  = Access Control List 
; Property         = AclRevision

ACL_REVISION       = 2
ACL_REVISION_DS    = 4  ; If the ACL contains an object-specific ACE you must use this.


; Bit values associated with the Security Descriptor Control property.
; Operating System = Windows 2000
; Namespace    	 = LDAP
; Security object  = Security Descriptor 
; Property         = Control

OWNER_DEFAULTED		   = 1		; A default mechanism, rather than the the original provider of the 
											; security descriptor, provided the security descriptor's owner security identifier (SID). 
GROUP_DEFAULTED			= 2      ; A default mechanism, rather than the the original provider of the security descriptor, 
											; provided the security descriptor's group SID. 
DACL_PRESENT				= 4		; Indicates a security descriptor that has a DACL. If this flag is not set, or if this 
											; flag is set and the DACL is NULL, the security descriptor allows full access to everyone. 
DACL_DEFAULTED				= 8  		; Indicates a security descriptor with a default DACL. For example, if an 
									  		; object's creator does not specify a DACL, the object receives the default DACL 
									  		; from the creator's access token. 
SACL_PRESENT				= 16	   ; Indicates a security descriptor that has a DACL.  This flag is used to hold the
   										; security information specified by a caller until the security descriptor is associated 
											; with a securable object.
SACL_DEFAULTED				= 32	   ; A default mechanism, rather than the the original provider of the security descriptor, provided the SACL. 
DACL_AUTO_INHERIT_REQ	= 256	   ; The DACL of the security descriptor must be inherited. 
SACL_AUTO_INHERIT_REQ	= 512	   ; The SACL of the security descriptor must be inherited.
DACL_AUTO_INHERITED		= 1024	; Indicates a security descriptor in which the DACL is set up 
										   ; to support automatic propagation of inheritable ACEs to existing child objects. 
SACL_AUTO_INHERITED		= 2048   ; The SACL of the security descriptor supports automatic propagation of inheritable 
											; ACEs to existing child objects.
DACL_PROTECTED				= 4096   ; The security descriptor will not allow inheritable ACEs to modify the DACL.
SACL_PROTECTED				= 8192   ; The security descriptor will not allow inheritable ACEs to modify the SACL.
SELF_RELATIVE		      = 32768  ; The security descriptor is of self-relative format with all the security information in 
											; a continuous block of memory.


; Bit values associated with an Access Control Entry's AccessMask property.
; Operating System = Windows 2000
; Namespace    	 = LDAP
; Security object  = ACE
; Property         = AccessMask 

DELETE                 = 65536       ; The right to delete the object.
READ_CONTROL           = 131072      ; The right to read information from the security descriptor of the object, 
												 ; not including the information in the SACL. 
WRITE_DAC              = 262144      ; The right to modify the discretionary access-control list (DACL) in the 
												 ; object's security descriptor.
WRITE_OWNER            = 524288      ; The right to assume ownership of the object. The user must be a trustee 
												 ; of the object. The user cannot transfer the ownership to other users. 
SYNCHRONIZE            = 1048576     ; The right to use the object for synchronization. This enables a thread 
												 ; to wait until the object is in the signaled state. 
ACCESS_SYSTEM_SECURITY = 16777216    ; The right to get or set the SACL in the object's security descriptor. 
GENERIC_READ           = 2147483648  ; The right to read from the security descriptor, examine the object as 
												 ; well as its children, and read all properties. 
GENERIC_WRITE          = 1073741824  ; The right to write all the properties and write to the DACL. The user 
												 ; can add and remove the object to and from the directory.
GENERIC_EXECUTE        = 536870912   ; The right to list children of this object. 
GENERIC_ALL            = 268435456   ; The right to create or delete children, delete a subtree, read and write 
												 ; properties, examine children and the object itself, add and remove the 
												 ; object from the directory, and read or write with an extended right. 
DS_CREATE_CHILD        = 1           ; The right to create children of the object. The ObjectType member of an 
												 ; ACE can contain a GUID that identifies the type of child object whose 
												 ; creation is being controlled. If ObjectType does not contain a GUID, the 
												 ; ACE controls the creation of all child object types. 
DS_DELETE_CHILD        = 2           ; The right to delete children of the object. The ObjectType member of an 
												 ; ACE can contain a GUID that identifies a type of child object whose 
												 ; deletion is being controlled. If ObjectType does not contain a GUID, the 
												 ; ACE controls the deletion of all child object types. 
ACTRL_DS_LIST          = 4           ; The right to list children of this object. 
DS_SELF                = 8           ; The right to modify the group membership of a group object. 
DS_READ_PROP           = 16          ; The right to read properties of the object. The ObjectType member of an 
												 ; ACE can contain a GUID that identifies a property set or property. If 
												 ; ObjectType does not contain a GUID, the ACE controls the right to read 
												 ; all of the object's properties. 
DS_WRITE_PROP          = 32          ; The right to write properties of the object. The ObjectType member of 
												 ; an ACE can contain a GUID that identifies a property set or property. 
												 ; If ObjectType does not contain a GUID, the ACE controls the right to 
												 ; write all of the object's properties. 
DS_DELETE_TREE         = 64          ; The right to delete all children of this object, regardless of the 
												 ; permission on the children. 
DS_LIST_OBJECT         = 128         ; The right to list a particular object. If the user is not granted such 
												 ; a right, the object is hidden from the user. 
DS_CONTROL_ACCESS      = 256		    ; The right to perform an operation controlled by an extended access right. 
												 ; The ObjectType member of an ACE can contain a GUID that identifies the 
												 ; extended right. If ObjectType does not contain a GUID, the ACE controls 
												 ; the right to perform all extended right operations associated with the object. 

;************************************Exchange 5.5 only**********************************************
; The Exchange 5.5's mailbox security descriptor has different meanings for several AccessMask bits.
; Operating System = Windows 2000, NT 4.0
; Namespace    	 = LDAP
; Security object  = ACE
; Property         = AccessMask 
EXCH_MODIFY_USER_ATT   = 2		       ; Modify User Attributes 
EXCH_MAIL_SEND_AS      = 8			    ; Send As
EXCH_MAIL_RECEIVE_AS   = 16			 ; Mailbox Owner

; The Exchange 5.5 secuirty descriptors for container objects have these bit values associated with
; their Access Control Entry's AccessMask property.
; Operating System = Windows 2000, NT 4.0
; Namespace    	 = LDAP
; Security object  = ACE
; Property         = AccessMask 
RIGHT_DS_ADD_CHILD 			= 1
RIGHT_DS_MODIFY_USER_ATT 	= 2
RIGHT_DS_MODIFY_ADMIN_ATT 	= 4
RIGHT_DS_DELETE				= 65536
RIGHT_MAIL_SEND_AS 			= 8
RIGHT_MAIL_RECEIVE_AS 		= 16
RIGHT_MAIL_ADMIN_AS 			= 32
RIGHT_DS_REPLICATION 		= 64
RIGHT_DS_MODIFY_SEC_ATT		= 128
RIGHT_DS_SEARCH 				= 256
;***********************************End Exchange 5.5 only*******************************************


; Values associated with an Access Control Entry's AceType property.
; Operating System = Windows 2000
; Namespace    	 = LDAP
; Security object  = ACE
; Property         = AceType  

ACCESS_ALLOWED           = 0  ; The ACE is of the standard ACCESS ALLOWED type, where the ObjectType and 
										; InheritedOjectType fields are NULL. 
ACCESS_DENIED            = 1  ; The ACE is of the standard ACCESS_DENIED type, where the ObjectType and 
										; InheritedObjectType fields are NULL. 
SYSTEM_AUDIT             = 2  ; The ACE is of the standard system type, where the ObjectType and 
										; InheritedObjectType fields are NULL. 
ACCESS_ALLOWED_OBJECT    = 5  ; The ACE is of the ADSI extension of the ACCESS ALLOWED type, where either 
    								   ; ObjectType or InheritedObjectType or both contain a GUID. 
ACCESS_DENIED_OBJECT     = 6  ; The ACE is of the ADSI extension of the ACCCESS_DENIED type, where either 
										; ObjectType or InheritedObjectType or both contain a GUID. 
SYSTEM_AUDIT_OBJECT      = 7  ; The ACE is of the ADSI extension of the system type, where either ObjectType
										; or InheritedObjectType or both contain a GUID. 


; Bit values associated with an Access Control Entry's AceFlag property.
; Operating System = Windows 2000
; Namespace    	 = LDAP
; Security object  = ACE
; Property         = AceFlags  

INHERIT_ACE              = 2   ; Child objects will inherit this access-control entry (ACE). The inherited 
										 ; ACE is inheritable unless the NO_PROPAGATE_INHERIT_ACE flag is set. 
NO_PROPAGATE_INHERIT_ACE = 4	 ; ADSI will clear the INHERIT_ACE flag for the inherited ACEs of 
									    ; child objects. This prevents the ACE from being inherited by subsequent 
										 ; generations of objects. 
INHERIT_ONLY_ACE         = 8   ; Indicates an inherit-only ACE that does not exercise access control on the 
										 ; object to which it is attached. If this flag is not set, the ACE is an 
										 ; effective ACE that exerts access control on the object to which it is attached. 
INHERITED_ACE            = 16  ; Indicates whether or not the ACE was inherited. The system sets this bit. 
VALID_INHERIT_FLAGS      = 31  ; Indicates whether the inherit flags are valid. The system sets this bit. 
SUCCESSFUL_ACCESS        = 64  ; Generates audit messages for successful access attempts, used with ACEs that 
										 ; audit the system in a system access-control list (SACL). 
FAILED_ACCESS            = 128 ; Generates audit messages for failed access attempts, used with ACEs that audit 
										 ; the system in a SACL. 

  
; Bit values associated with an Access Control Entry's Flags  property.
; Operating System = Windows 2000
; Namespace    	 = LDAP
; Security object  = ACE
; Property         = Flags   

OBJECT_TYPE_PRESENT             = 1 ; The ObjectType field is present in the ACE, but InheritedObjectType is not. 
INHERITED_OBJECT_TYPE_PRESENT   = 2 ; The InheritedObjectType field is present in the ACE, but ObjectType is not. 

; Possible values for the Authentication method (3rd) parameter of the dsSetCredentx function.
; Use these values to control the 
CLEAR_TEXT             = 0    ; Use basic authentication to bind to directory service objects.
SECURE_AUTHENTICATION  = 1    ; Requests secure authentication. When this flag is set, the WinNT provider uses NTLM 
										; to authenticate the client. Active Directory will use Kerberos, and possibly NTLM, 
										; to authenticate the client. When the user name and password are NULL, the extender
										; binds to the object using the security context of the user account under which 
										; WinBatch is running. 
USE_ENCRYPTION         = 2	   ; Use encryption for data exchange over the network. 
USE_SSL                = 2	   ; Data will be encrypted using SSL.
READONLY_SERVER        = 4	   ; For a WinNT provider, the extender  tries to connect to a primary domain 
										; controller (PDC) or a backup domain controller (BDC). For Active Directory, this 
										; flag indicates that a writeable server is not required for a serverless binding. 
PROMPT_CREDENTIALS     = 8	   ; Not supported.
NO_AUTHENTICATION      = 16   ; The providers may attempt to bind the client to an object, as an anonymous user.
										; The WinNT provider does not support this flag.
FAST_BIND              = 32   ; This flag is not supported by the extender.
USE_SIGNING            = 64	; Verifies data integrity to ensure the data received is the same as the data sent. 
										; The SECURE_AUTHENTICATION flag must be set also. 
USE_SEALING            = 128  ; Encrypts data using Kerberos. The SECURE_AUTHENTICATION flag must be set also. 
USE_DELEGATION         = 256  ; Enables the extender to delegate the user's security context, which is necessary 
									   ; for moving objects across domains. 
SERVER_BIND            = 512  ; Windows 2000 SP1 and later: Specify this flag when using the LDAP provider if your
   								   ; ADsPath includes a server name. Do not use this flag for paths that include a domain 
										; name or for serverless paths. If you specify a server name without also specifying 
										; this flag, unnecessary network traffic is the result. 

; The SECURE_AUTHENTICATION flag can be used in combination with other flags such as READONLY_SERVER, 
; PROMPT_CREDENTIALS, FAST_BIND, USE_SIGNING, USE_SEALING AND SERVER_BIND.



return

EMAILWELCOME.WBT

;EmailWelcome 
;  Post the first message to UnaVia mailbox
;  have to wait to give the E-mail server time to create the mailbox.
;  Param1 = server
;  param2 = user's email address (user1@tc1.com)
;  param3 = time delay in seconds

;generic debug setting
progname=itemextract(1,winexename(""),".")

if fileexist("%progname%.dbg") 
   debugtrace(1,"%progname%.log") 
else
   debugtrace(0,"%progname%.log")
endif 

AddExtender("wwwsk34i.dll ")

tm = timeymdhms()
x=param1
y=param2
z=param3

timedelay(param3)

err=smtpSendText(param1, "UnaViaUserSupport@netwaysolutions.com", param2, "Welcome to UnaVia", "Your E-mail Account was created on %tm%")
err=wxGetLastErr()

exit

WB and CDO when NOT running from Exchange Server

The above code only get works if run from the exchange server, but that's not a possiblity for me ... I have the cdoexm.dll file and I can get it to work in VB, so it leads me to believe that my problem is making winbatch recognize that it can use CDO

Where does winbatch look for the ole object specified in objectopen("object")?

Answer:

Try opening regedit and searching for 'CDOEXM' or 'CDO'. Then try to find a registry subkey called ProgID nearby. Use the field in the ProgId (default) dataitem in the ObjectOpen.

Resolution:

Good idea, I found the answer in the registry... The winbatch equivalent of:
 
dim objectmailbox as cdoexm.imailboxstore 
appears to be:
 
objectmailbox=objectopen("CDOEXM.MailboxstoreDB")
so to create a mailbox in Exchange 2000 for a user *correctly* you will need to use the following code to set the homemdb property
homemdb="correctly formatted homemdb string for your organization"
sObjectPath="full ldap path to user"
objUser=ObjectOpen(sObjectPath)
objMailbox=ObjectOpen("CDOEXM.MailboxStoreDB")
objMailbox=objUser
ObjMailbox.CreateMailbox(homemdb)
objUser.SetInfo
objectClose(objMailbox)
objectClose(objUser)

Article ID:   W14917
File Created: 2013:06:19:15:16:32
Last Updated: 2013:06:19:15:16:32