delphi3000.com Article

Creating windows accounts using ADSI (Active Directory)
Undertitle:
URL:http://www.delphi3000.com/article.asp?ID=1219
Category:COM+
Uploader:Philip Jespersen
 
Question:How can I create a Windows user account with Delphi and ADSI?
Answer:In order to create Windows user accounts in Delphi you can use the ADSI (Active Directory Services Interface) from Microsoft. You might think that ADSI is only available for Windows 2000 - maybe due to it's name - but actually ADSI is available for all Win32 platforms. You may need to download ADSI for your Windows version (check http://www.microsoft.com/adsi for complete details). ADSI is included with Windows 2000.

ADSI is a very large topic. I'm only stratching the top surface in this article. ADSI is a kind of a generic interface to many different computer (mostly directory based) services. Some of the standard ADSI Providers (COM interfaces which can use in your programs) are WinNT, IIS, LDAP and NDS. The WinNT provider can hereby be used to create user accounts, modify user account settings or modify groups (among a lot of other things).

The following small program shows you the steps necessary to create user accounts under NT/2000 using ADSI:

First you need import the ADSI Type Library (Menu Project/Import Type Library). The Type Library can be found in the system32 subdirectory (for example C:\WINNT\system32\activeds.tlb). The file required is named 'activeds.tlb'. If you can't find the file check if you've got ADSI installed correctly. After successfully importing the Type Library you'll find a new file in the Delphi Imports directory named activeds_tlb.pas (..\Delphi5\Imports\activeds_tlb.pas for example). Basically you need to include this file in your uses clause in order to enable ADSI programming with Delphi.

On to the user creation example with ADSI. You need to replace [computername] with the actual computer name which you are using. The same applies to [accountname]. I've tested the example with
WindowsNT 4.0 and Windows 2000.

...

uses ActiveX,        // used for the COM Moniker stuff...
     ActiveDs_TLB,   // the created type library
     ComObj;         // used for OleCheck and other COM functions

implementation

procedure TForm1.BtnCreateUserClick(Sender: TObject);
var
  Usr:  IADsUser;
  Comp: IADsContainer;
begin
  try
    Comp := GetObject('WinNT://[computername],computer') as
      IADsContainer;
    Usr := Comp.Create('user', '[accountname]') as IADsUser;
    Usr.SetInfo;
  except
    on E: EOleException do begin
      ShowMessage(E.Message);
    end;
  end;
end;


procedure TForm1.BtnSetPasswordClick(Sender: TObject);
var
  Usr: IADsUser;
begin
  try
    Usr := GetObject('WinNT://[computername]/[accountname],user')
      as IADsUser;
    Usr.SetPassword('thenewpassword');
  except
    on E: EOleException do begin
      ShowMessage(E.Message);
    end;
  end;
end;


// GetObject is a implementation of the VB GetObject call
// I've found this code (GetObject) on the Usenet.  
//
// With GetObject can you bind to an existing ADSI provider
// using a 'ADSIPath' (for example WinNT://.... or
// IIS://localhost).

function TForm1.GetObject(const Name: string): IDispatch;
var
  Moniker: IMoniker;
  Eaten: integer;
  BindContext: IBindCtx;
  Dispatch: IDispatch;
begin
  OleCheck(CreateBindCtx(0, BindContext));
  OleCheck(MkParseDisplayName(BindContext,
                              PWideChar(WideString(Name)),
                              Eaten,
                              Moniker));
  OleCheck(Moniker.BindToObject(BindContext, NIL, IDispatch,
            Dispatch));

  Result := Dispatch;
end;

end.


Over ADSI you can also modify the settings of a user account. The following code can be used to change the 'Password never expires' flag of any account:

procedure TFormMain.ButtonNeverExpiresClick(Sender: TObject);
var
  Usr: IADsUser;
begin
  try
    Usr := GetObject('WinNT://[computername]/[acccoutname],user') as
      IADsUser;

  // Check the Checkbox State...
  if CheckBoxPasswordNeverExpires.Checked then
    Usr.Put('UserFlags', Usr.Get('UserFlags') OR 65536)
    // 65536 is defined as UF_DONT_EXPIRE_PASSWORD in iads.h  
    // from the ADSI SDK available from Microsoft
  else
    Usr.Put('UserFlags', Usr.Get('UserFlags') XOR 65536);  
    Usr.SetInfo;

  except
    on E: EOleException do begin
      ShowMessage(E.Message);
    end;
  end;
end;


From here...

In order to get into the deeper parts of ADSI you need to check
out the actual interfaces provided like IADsUser or IADsContainer.
I recommend that you work with the ADSI SDK from Microsoft and by inspecting the created Type Library.

Copyright 2000 delphi3000.com
Contact: delphi3000@bluestep.com'

Comments to this article
Write a new comment
Need help
    Biplab Saha (Apr 13 2007 9:04AM)

Helio guys i nedd some help i am trying it so many days but i cant implement it.If possible then please help me .My problem is that i have to implement a form using delphi 7 and i have to show  "List of all group of the current loogin user in ative directory"

So i am requesting all of guys that plz give me a solution how i implement it.
Thanks

Biplab Kumar Saha

Respond

<%If Session("sSecurityLevel") >= 2 Then%>Additional Admin-Function:
Delete This Article-Comment! Delete this Comment!<%End If%>
Activi directory
    David escobar beristain (Jan 2 2007 10:52PM)

tengo problemas al declarar la variable, con los ejemplos que se muestran:

Var
  salesOu: IADsContainer;
  usr: IADsUser;
Try
    
    salesOU:= GetObject('LDAP://OU=OU MDF,OU=OU Personal del CECA,OU=OU Centro de Computo Academico,DC=yyy,DC=com') As IADsContainer;
    usr:= salesOU.Create('user', 'CN=Donald Duck') As  IADsUser;
    usr.Put('sAMAccountName','dd');
    usr.Put('userPrincipalName','dd@yyy.com');
    usr.Put('title','Marketing Manager');
    usr.Put('givenName','Donald');
    usr.Put('sn','Duck');
    usr.Put('DisplayName','Donald Duck');
    usr.Put('initials','DD');
    usr.Put('Description','Funny duck');
    usr.Put('scriptPath','dd\logon.cmd');
    usr.SetInfo;
    usr.SetPassword('Password');
    usr.AccountDisabled:= False;
    usr.SetInfo;
  Except
    On E: EOleException Do
      Begin
        ShowMessage(E.Message);
      End;

  End;


El error que me manda es el siguiente
[Error] Unit1.pas(118): Undeclared identifier: 'IADsContainer'
[Error] Unit1.pas(119): Undeclared identifier: 'IADsUser'

por favor alguien me podra ayudar.
Respond

RE: Activi directory
frank dahlin (Jan 9 2007 3:15PM)

Might help if you used english...

But guess you are missing  ActiveDs_TLB in your "uses" section.
(And perhaps the actual  ActiveDs_TLB.pas file...)
Respond

<%If Session("sSecurityLevel") >= 2 Then%>Additional Admin-Function:
Delete This Article-Comment! Delete this Comment!<%End If%>
One-level query to Active Directory
    Marina Pryadko (Jun 9 2005 11:16AM)

Hi!
What properties shoud I set to query select only those data which are one level lower then the container determined in query. For example:

Company.com
  - Department1
       - Department11
       - Department12
  - Department2
       - Department21

Select distinguishedName From 'LDAP://DC=Company, DC=com' WHERE ObjectClass='OrganizationalUnit'
returns
OU=Department1,DC=Company, DC=com
OU=Department11,OU=Department1,DC=Company, DC=com
OU=Department12,OU=Department1,DC=Company, DC=com
OU=Department2,DC=Company, DC=com
OU=Department21,OU=Department2,DC=Company, DC=com

I want the query to return
OU=Department1,DC=Company, DC=com
OU=Department2,DC=Company, DC=com

Please help me with my problem
Respond

RE: One-level query to Active Directory
juliano freitas (Apr 11 2006 11:28AM)

Select distinctdistinguishedName From 'LDAP://DC=Company, DC=com' WHERE ObjectClass='OrganizationalUnit'
Respond

RE: RE: One-level query to Active Directory
juliano freitas (Apr 11 2006 11:29AM)

Opa. Foi malz. Ficou sem o espaço.

Select distinct distinguishedName From 'LDAP://DC=Company, DC=com' WHERE ObjectClass='OrganizationalUnit'
Respond

<%If Session("sSecurityLevel") >= 2 Then%>Additional Admin-Function:
Delete This Article-Comment! Delete this Comment!<%End If%>
ADSI
    delpai (May 25 2005 7:45AM)

I get an error with this code. Can anyone help me with it? How can I get rid off the error?

  ADsGetObject('WinNT://' + Domain + '/' + disp.Name, IADsUser, usr);
  try
    node.Strings[colEmail]       := usr.EmailAddress;
  ...

Respond

<%If Session("sSecurityLevel") >= 2 Then%>Additional Admin-Function:
Delete This Article-Comment! Delete this Comment!<%End If%>
i can't create user account
    gela charkviani (Apr 19 2005 10:34PM)

i can't create user accounts :( anyone knows why?

begin
  try
    Comp := GetObject('WinNT://' + GetCompName + ',computer') as
      IADsContainer;
    Usr := Comp.Create('user3', 'MyName') as IADsUser;
    Usr.SetInfo;
............................
and i get an EOleException telling that one or more arguments ar invalid. Usr := Comp.Create('user3', 'MyName') as IADsUser; <-- something is wrong here. anyone can help me please?
Respond

<%If Session("sSecurityLevel") >= 2 Then%>Additional Admin-Function:
Delete This Article-Comment! Delete this Comment!<%End If%>
Reseting/Changing Passwords
    Donald Neisler (Oct 28 2004 5:54PM)

If write a app to reset/change passwords, doesn't it need to run as a administrator or someone that has rights to do so? Is there a way to mask a user so that a non admin can run this program to reset/change a password?

TIA
Donald
Respond

<%If Session("sSecurityLevel") >= 2 Then%>Additional Admin-Function:
Delete This Article-Comment! Delete this Comment!<%End If%>
Adding New Users to Group.
    Martyn MacCabe (Sep 7 2004 12:35PM)

I have a script that creates a batch of new users based on information provided in a spreadsheet. I now wish to create a new script or update my existing script so that it will add all of the users to a list of specified global groups. Looking for a bit of help to start me off or even if anyone can point me to an example script it would be much obliged. Thanks
Respond

<%If Session("sSecurityLevel") >= 2 Then%>Additional Admin-Function:
Delete This Article-Comment! Delete this Comment!<%End If%>
password
    yannick LANCHEC (Sep 2 2004 12:55AM)

How can i change the user's password with ADSI to be blank  ?

i have try ADS_UF_PASSWD_NOTREQD with the userAccountControl

but i always have an error 80070057

thanks !!

Respond

<%If Session("sSecurityLevel") >= 2 Then%>Additional Admin-Function:
Delete This Article-Comment! Delete this Comment!<%End If%>
finding a user
    ALEXANDRE NISHIKAWA (Aug 17 2004 10:39PM)

I have a lot of Directory and I need to search a user in a diferents context, is have a function?
Respond

<%If Session("sSecurityLevel") >= 2 Then%>Additional Admin-Function:
Delete This Article-Comment! Delete this Comment!<%End If%>
New properties
    Emma (Dec 5 2003 1:03PM)

Is is possible to add new properties på AD-users through ldap? And if it is, how do I do this?


thanks,
  Emma
Respond

<%If Session("sSecurityLevel") >= 2 Then%>Additional Admin-Function:
Delete This Article-Comment! Delete this Comment!<%End If%>
Add User To Group
    Thomas Olsen (Aug 15 2003 6:20PM)

procedure TForm1.AddToGroupClick(Sender: TObject);
Var
  grp: IADsGroup;
begin
  Try
    grp:= GetObject('LDAP://CN=Management,OU=Sales,DC=T-point,DC=Dk') as IADsGroup;
    grp.Add('LDAP://CN=Donald Duck,OU=Sales,DC=T-point,DC=Dk');
  Except
    On E: EOleException Do
      Begin
        ShowMessage(E.Message);
      End;
  End;
end;
Respond

<%If Session("sSecurityLevel") >= 2 Then%>Additional Admin-Function:
Delete This Article-Comment! Delete this Comment!<%End If%>
Create Group
    Thomas Olsen (Aug 15 2003 6:20PM)

procedure TForm1.CreateGroupClick(Sender: TObject);
Var
  Ou: IADsContainer;
  Grp: IADsGroup;
begin
  Try
    ou:= GetObject('LDAP://OU=Sales,DC=T-point,DC=Dk') As IADsContainer;
    Grp:= ou.Create('group', 'CN=Management') Ss IADsGroup;
    Grp.Put('samAccountName', 'mgmt');
    Grp.Put('groupType', ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP Or ADS_GROUP_TYPE_SECURITY_ENABLED);
    Grp.SetInfo;
  Except
    On E: EOleException Do
      Begin
        ShowMessage(E.Message);
      End;
  End;
end;
Respond

<%If Session("sSecurityLevel") >= 2 Then%>Additional Admin-Function:
Delete This Article-Comment! Delete this Comment!<%End If%>
Create User
    Thomas Olsen (Aug 15 2003 6:19PM)

procedure TForm1.CreateUserClick(Sender: TObject);
Var
  salesOu: IADsContainer;
  usr: IADsUser;
begin
  Try
    salesOU:= GetObject('LDAP://OU=Sales,DC=T-Point,DC=Dk') As IADsContainer;
    usr:= salesOU.Create('user', 'CN=Donald Duck') As  IADsUser;
    usr.Put('sAMAccountName','dd');
    usr.Put('userPrincipalName','dd@t-point.dk');
    usr.Put('title','Marketing Manager');
    usr.Put('givenName','Donald');
    usr.Put('sn','Duck');
    usr.Put('DisplayName','Donald Duck');
    usr.Put('initials','DD');
    usr.Put('Description','Funny duck');
    usr.Put('scriptPath','dd\logon.cmd');
    usr.SetInfo;
    usr.SetPassword('Password');
    usr.AccountDisabled:= False;
    usr.SetInfo;
  Except
    On E: EOleException Do
      Begin
        ShowMessage(E.Message);
      End;
  End;
end;
Respond

RE: Create User
Mongol Kublai (Jun 30 2010 12:49PM)

Hi.
I am new to Delphi. My task is to create local windows user and assign it to a specific group. I have read whole article and I have tried the code as well. It creates local windows user but I am unable to assign a user group to that user.
I can can see the user in computer management tool.but user does not belong to any existing group .I need help. Thanks in advance
Respond

<%If Session("sSecurityLevel") >= 2 Then%>Additional Admin-Function:
Delete This Article-Comment! Delete this Comment!<%End If%>
Create OU
    Thomas Olsen (Aug 15 2003 6:18PM)

procedure TForm1.CreateOUClick(Sender: TObject);
Var
  Dom: IADsContainer;
  salesOu: IADsOu;
begin
  Try
    dom:= GetObject('LDAP://DC=T-Point,DC=Dk') As IADsContainer;
    salesOu:= dom.Create('organizationalUnit', 'OU=Sales') As IADsOu;
    salesOu.Put('description', 'Sales Headquarter');
    salesOu.Put('wwwHomePage', 'http://www.t-point.dk');
    salesOu.SetInfo;
  Except
    On E: EOleException Do
      Begin
        ShowMessage(E.Message);
      End;
  End;
end;

Respond

<%If Session("sSecurityLevel") >= 2 Then%>Additional Admin-Function:
Delete This Article-Comment! Delete this Comment!<%End If%>
Active Directory - Organizational Unit
    Marsha Thompson (Jan 14 2003 10:44PM)

After looking at article, I was able to connect to domain and create a user in the domain, however, I need, to enhance this,  to create the user in an Organizational Unit instead of root level.  Could you help me with this?  I am real close, however, get an error with my syntax when try to add OrganizationalUnit. Could you tell me how to add Organizational Unit.
Here is syntax that works.

//Domain=FCA.ORG
//OrganizationalUnit = Users

procedure TForm1.btnCreateNewUserClick(Sender: TObject);
var adComp : IADsContainer;
var adUser : IADsUser;
begin
//Connect to Active Directory FCA Domain
adComp := GetObject('LDAP://FCA.ORG') as IADsContainer;
Messagedlg('Just got adComp-this works fine', mtInformation, [mbOK], 0);
adUser := adComp.Create('user', 'CN=Jay Adams') as IADsUser;
Messagedlg('Just created User', mtInformation, [mbOK], 0);
adUser.Put('samAccountName', 'jayadams');
adUser.Put('userPrincipalName', 'jayadams@fca.org');
adUser.Put('title', 'Marketing Manager');
adUser.SetInfo;
Messagedlg('Just added a User-this works fine', mtInformation, [mbOK], 0);
end;

function  TForm1.GetObject(Name: string): IDispatch;
var
   Moniker: IMoniker;
   Eaten: integer;
   BindContext: IBindCtx;
   Dispatch: IDispatch;
begin
   OleCheck(CreateBindCtx(0, BindContext));
   OleCheck(MkParseDisplayName(BindContext,
                               PWideChar(WideString(Name)),
                               Eaten,
                               Moniker));
   OleCheck(Moniker.BindToObject(BindContext, NIL, IDispatch, Dispatch));
   Result := Dispatch;
end;
end.
Respond

RE: Active Directory - Organizational Unit
DCleminson (Feb 26 2005 9:29AM)

You just need to bind to the correct OU using your bind Query via LDAP.

procedure TForm1.btnCreateNewUserClick(Sender: TObject);
var adComp : IADsContainer;
var adUser : IADsUser;
begin
//Connect to Active Directory FCA Domain
adComp := GetObject('LDAP://OU=Users, DC=FCA, DC=ORG') as IADsContainer;
//This will bind you to the users container.

This should resolve your issue......
Messagedlg('Just got adComp-this works fine', mtInformation, [mbOK], 0);
adUser := adComp.Create('user', 'CN=Jay Adams') as IADsUser
Respond

<%If Session("sSecurityLevel") >= 2 Then%>Additional Admin-Function:
Delete This Article-Comment! Delete this Comment!<%End If%>
Using LDAP for create user at Windows 2000 ?
    anonymus (Nov 20 2001 11:56AM)

I heared that if we use WinNT Provider, we cannot placed new user in any container or organizational unit and if we want to placed new user in any Container or Organizational unit we must use LDAP Provider

Would you please inform me how to use LDAP to create and delete new user ?
Thank you very much

Respond

<%If Session("sSecurityLevel") >= 2 Then%>Additional Admin-Function:
Delete This Article-Comment! Delete this Comment!<%End If%>
You can also use CoGetObject
    Martijn Houtman (Nov 18 2000 9:04PM)

function TForm1.GetObject(const Name: string): IDispatch;
var
  Dispatch: IDispatch;
begin
  OleCheck(CoGetObject(PWideString(WideString(Name)), nil, IDispatch, @Dispatch));
  Result := Dispatch;
end;

Respond

RE: You can also use CoGetObject
Vaclav Junek (Dec 3 2001 4:38PM)

This code don´t run at consloe aplication :o(
Form Application :o) All goods friends
Respond

RE: RE: You can also use CoGetObject
Wil Lierop (Jan 22 2006 2:39PM)

unit main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Edit2: TEdit;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function ValidateUserLogonAPI(const UserName : string;
                                              const Domain : string;
                                              const PassWord : string) : boolean;
var Retvar : boolean;
    LHandle : THandle;
begin
  Retvar := LogonUser(PChar(UserName),
                                PChar(Domain),PChar(PassWord),
                                LOGON32_LOGON_NETWORK,
                                LOGON32_PROVIDER_DEFAULT,
                                LHandle);
  if Retvar then CloseHandle(LHandle);
  Result := Retvar;
end;


procedure TForm1.Button1Click(Sender: TObject);
begin

if ValidateUserLogonAPI(Edit1.Text, 'thuis', edit2.Text) then
  ShowMessage('you have logged on succesfully')
  else
  ShowMessage('failed to login');

end;

end.

//note: this code does give a false positive if a non existend user is checked
Respond

<%If Session("sSecurityLevel") >= 2 Then%>Additional Admin-Function:
Delete This Article-Comment! Delete this Comment!<%End If%>
How can I know the current login user information ?
    Aven (Oct 16 2000 3:10AM)

How can I know the current login user information ?
Respond

RE: How can I know the current login user information ?
Daniel Hernandez (Oct 19 2000 3:05PM)

you can use this to do so:


var
  username: PChar;
  size: Dword;
begin
  if GetUserName(username, size) then
     // do something...

Respond

<%If Session("sSecurityLevel") >= 2 Then%>Additional Admin-Function:
Delete This Article-Comment! Delete this Comment!<%End If%>
Enumeration and other functions
    Deepak Shenoy (Aug 6 2000 1:25PM)

This is a good beginning - but if you need to enumerate the users in a domain or group, you`ll need to use IEnumVariant. There are some standard ADSI functions for enumeration, located in ADsHlp.h in the PlatformSDK. I`ve converted them to Delphi - to get them mail me at shenoy@agnisoft.com. I even have a paper on it at www.agnisoft.com/adsi - I`ve still got to spend some time updating the site though :)



Respond

DNS
Claudinei Rodrigues (Aug 16 2000 7:57AM)

I need create a DNS. Is it possible ? How ? I need a lot this information.

Thanks !
Respond

RE: DNS
Roberto Plácido Teixeira (Aug 20 2000 5:10PM)

If you have the ODBC engine installed, you can call the ODBC32.DLL API entry calls directly loading this DLL library in the memory. It's a way. I'll remember this case, and will send a further email to you. For this, send me an email to know yours, to "betobyte@igcom.br". I'll talk in portuguese, if you desire. Find for help files at your \WINDOWS directory containing "ODBC". Search for "ODBC"at www.microsoft.com. It's ease.
Respond

<%If Session("sSecurityLevel") >= 2 Then%>Additional Admin-Function:
Delete This Article-Comment! Delete this Comment!<%End If%>