Windows authorization: Adding/Removing users from local groups
Posted by jpluimers on 2011/08/10
Though there are API ways to add users to local groups in Delphi, you need the JwaLmAccess unit from the JEDI API library project to import those.
Sometimes that is not feasible, and sometimes you want to just script things.
For those, you can use the Windows net localgroup command (if you have sufficient privileges, you can even apply it to the local groups on your domain controller by appending it with the /domain parameter, or use the net group /domain command to execute on global domain groups instead of local groups).
Sample of using this in a cmd script:
net localgroup Guests Me Myself I ACME\BugsBunny /delete
It will remove the local users Me, MySelf and I, and remove the domain user ACME\BugsBunny from the local group Guests.
Sample source of using this in Delphi:
program ADConsoleProject;
{$APPTYPE CONSOLE}
uses
ShellUnit in '..\src\ShellUnit.pas',
LocalGroupUnit in '..\src\LocalGroupUnit.pas';
begin
RemoveUsersFromLocalGroup(GuestsLocalGroup, ['Me', 'Myself', 'I', 'ACME\BugsBunny']);
end.
These are the underlying Delphi units:
LocalGroupUnit: the implementation for AddUsersToLocalGroup and RemoveUsersFromLocalGroup.
unit LocalGroupUnit;
interface
const
GuestsLocalGroup = 'Guests'; // do not translate
AdministratorsLocalGroup = 'Administrators'; // do not translate
procedure AddUsersToLocalGroup(const LocalGroup: string; const Users: array of string);
procedure RemoveUsersFromLocalGroup(const LocalGroup: string; const Users: array of string);
implementation
uses
SysUtils, ShellUnit, Windows;
const
AddToLocalGroupComand = 'net localgroup %s %s /add';
RemoveFromLocalGroupComand = 'net localgroup %s %s /delete';
// alternative: http://stackoverflow.com/questions/1584275/add-current-selected-user-to-a-group
procedure AddUsersToLocalGroup(const LocalGroup: string; const Users: array of string);
var
Command: string;
User: string;
begin
for User in Users do
begin
Command := Format(AddToLocalGroupComand, [LocalGroup, User]);
RunProcessAndWait(Command, SW_NORMAL, False, nil);
end;
end;
procedure RemoveUsersFromLocalGroup(const LocalGroup: string; const Users: array of string);
var
Command: string;
User: string;
Result: LongWord;
begin
for User in Users do
begin
Command := Format(RemoveFromLocalGroupComand, [LocalGroup, User]);
RunProcessAndWait(Command, SW_NORMAL, False, nil);
end;
end;
end.
ShellUnit (it also contains some other parts I recently needed: shell folders):
unit ShellUnit;
interface
uses
ShlObj, Windows;
function GetSystemPath(Folder: Integer): string;
type
TShellFolders = array[1..39] of Integer;
const
ShellFolders: TShellFolders = (
CSIDL_DESKTOP,
CSIDL_INTERNET,
CSIDL_PROGRAMS,
CSIDL_CONTROLS,
CSIDL_PRINTERS,
CSIDL_PERSONAL,
CSIDL_FAVORITES,
CSIDL_STARTUP,
CSIDL_RECENT,
CSIDL_SENDTO,
CSIDL_BITBUCKET,
CSIDL_STARTMENU,
CSIDL_DESKTOPDIRECTORY,
CSIDL_DRIVES,
CSIDL_NETWORK,
CSIDL_NETHOOD,
CSIDL_FONTS,
CSIDL_TEMPLATES,
CSIDL_COMMON_STARTMENU,
CSIDL_COMMON_PROGRAMS,
CSIDL_COMMON_STARTUP,
CSIDL_COMMON_DESKTOPDIRECTORY,
CSIDL_APPDATA,
CSIDL_PRINTHOOD,
CSIDL_LOCAL_APPDATA,
CSIDL_ALTSTARTUP,
CSIDL_COMMON_ALTSTARTUP,
CSIDL_COMMON_FAVORITES,
CSIDL_INTERNET_CACHE,
CSIDL_COOKIES,
CSIDL_HISTORY,
CSIDL_PROFILE,
CSIDL_CONNECTIONS,
CSIDL_COMMON_MUSIC,
CSIDL_COMMON_PICTURES,
CSIDL_COMMON_VIDEO,
CSIDL_CDBURN_AREA,
CSIDL_COMPUTERSNEARME,
CSIDL_PROFILES
);
function RunProcessAndWait(const FileName: string; const ShowCmd: DWORD; const wait: Boolean; const ProcID: PDWORD): Longword;
implementation
uses
SysUtils,
Forms,
ActiveX,
ComObj;
// http://www.scalabium.com/faq/dct0106.htm
// http://www.delphipages.com/forum/showthread.php?t=158704
function GetSystemPath(Folder: Integer): string;
var
ItemIDList: PItemIDList;
AMalloc: IMalloc;
begin
if SHGetMalloc(AMalloc) = NOERROR then
begin
ItemIDList := nil;
OleCheck(SHGetSpecialFolderLocation(Application.Handle, Folder, ItemIDList));
try
SetLength(Result, MAX_PATH);
SHGetPathFromIDList(ItemIDList, PChar(Result));
SetLength(Result, Length(PChar(Result)));
finally
AMalloc.Free(ItemIDList);
end;
end;
end;
// http://www.codeguru.com/forum/archive/index.php/t-235106.html
// http://sites.google.com/site/delphibasics/home/delphibasicssnippets/createprocessandwaitforexit
// http://stackoverflow.com/questions/268208/delphi-gracefully-closing-created-process-in-service-using-tprocess-createp
// http://www.codeproject.com/KB/winsdk/runsilent.aspx
function RunProcessAndWait(const FileName: string; const ShowCmd: DWORD; const wait: Boolean; const ProcID: PDWORD): LongWord;
var
StartupInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
begin
FillChar(StartupInfo, SizeOf(StartupInfo), #0);
StartupInfo.cb := SizeOf(StartupInfo);
StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow := ShowCmd;
if not CreateProcess(nil,
@Filename[1],
nil,
nil,
False,
NORMAL_PRIORITY_CLASS,
nil,
nil,
StartupInfo,
ProcessInfo)
then
Result := WAIT_FAILED
else
begin
if wait = FALSE then
begin
if ProcID <> nil then
ProcID^ := ProcessInfo.dwProcessId;
result := WAIT_FAILED;
exit;
end;
WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
GetExitCodeProcess(ProcessInfo.hProcess, Result); // should always succeed as the process has exited
end;
if ProcessInfo.hProcess <> 0 then
CloseHandle(ProcessInfo.hProcess);
if ProcessInfo.hThread <> 0 then
CloseHandle(ProcessInfo.hThread);
end;
end.
As an added bonus, this will list the local groups on your domain controller if you have enough rights to read those:
net localgroup /domain
–jeroen
PS: As a reminder to self, some other handy commands like NET, DSTOOLS, NETSH, NETDOM, WMIC, PSTOOLS, et cetera are described by Rob van der Woude.






Robert H said
Very useful. Thanks for posting.