Sunday, July 14, 2013

Export Active Directory Groups with their Members

Many times I have seen situations when there is a need to audit Active Directory Groups.

In this blog I have included a batch script which will export all AD groups with their members into .csv file which you can open in Microsoft Excel and apply different filters.

:::: Batch Script Start ::::
SETLOCAL EnableDelayedExpansion

SET FileName=C:\Report.csv

FOR /F %%T IN ('DSQuery * -Filter "(&(objectClass=Group))" -Limit 0') DO SET /a AG+=1 >NUL
FOR /F %%T IN ('DSQuery * -Filter "(&(objectClass=Group)(^!member=*))" -Limit 0') DO SET /a EG+=1 >NUL
SET /a NE=!AG!-!EG!

ECHO Total Groups in Active Directory %AG% out of them %EG% are empty.&&ECHO.
ECHO Group,Members>"!FileName!"
TITLE Exporting !NE! Non-Empty AD Groups.

FOR /F "delims=" %%G IN ('DSQuery * -Filter "(&(objectClass=Group)(member=*))" -Limit 0') DO (
    FOR /F "delims=" %%v IN ('DSQuery * %%G -l -q -Attr Name -Limit 0') DO SET GN=%%v
    SET /a CT+=1 >NUL
    ECHO !CT!. Exporting: !GN!
    FOR /F "delims=" %%M IN ('DSGET Group %%G -Members') DO (
        FOR /F "delims=" %%U IN ('DSQuery * %%M -l -q -Attr displayName') DO (
        ECHO !GN!,%%U>>"!FileName!")))

TITLE Export complete.
ECHO.&&ECHO Export complete, please check '!FileName!' file.
:::: Batch Script End ::::
Copy above script and paste into Notepad and save it with any name having .cmd extension and execute it from command line.

Following one liners can be used to list group members.

To display group's distinguished name:

DSQuery Group -name GroupName

To display Group members:

DSQuery Group -name GroupName |DSGet Group -Members

To list group members's display name or usernames:

DSQuery Group -name GroupName |DSGet Group -Members |DSGet User -c -samID -display

If your group contains any other group (nested) then you might get message like following:

dsget failed: <group distinguished name> :The object class of the target does not match the one specified on the command line.

So to ignore and continue command operation I have included -c switch with above DSGet User statement.

If you want to include members of all nested groups then you can use -expand switch with DSGet Group statement which will recursively expanded list of members of the group.

DSQuery Group -name GroupName |DSGet Group -Members -expand |DSGet User -c -samID -display

To save output into file you can use dos redirection operator > with file name.

DSQuery Group -name GroupName |DSGet Group -Members -expand |DSGet User -c -samID -display >GroupMembers.txt

Wednesday, June 5, 2013

Who Changed Exchange Server Configuration?

Exchange 2010 has introduced new feature called Administrator Audit Logging. This feature keeps track of what, when and who made changes to Exchange organization. 

Microsoft Exchange provides two types of audit logging:
  • Administrator audit logging records any action, based on a Windows PowerShell cmdlet, performed by an administrator. This can help you troubleshoot configuration issues or identify the cause of security- or compliance-related problems.
  • Mailbox audit logging records whenever a mailbox is accessed by someone other than the person who owns the mailbox. This can help you determine who has accessed a mailbox and what they have done.
This blog covers only Administrator Audit Logging.

Audit log keeps record of almost all Cmdlets (except Get-, Search- and Test- Cmdlets) that are either executed directly using Exchange Management Shell or using Exchange Management Console (EMC) or Exchange Web management interface.

You can search the administrator audit logs to discover who made changes to organization, server, and recipient configuration. This can be helpful when trying to track the cause of unexpected behavior, to identify a malicious administrator, or to verify that compliance requirements are being met.

In Exchange 2010 SP1 Administrator Audit Logging is enabled by default and keeps 90 days worth of entries. After 90 days, the audit log entry is deleted. You can change the audit log age limit using the -AdminAuditLogAgeLimit parameter with Set-AdminAuditLogConfig.

Caution: If you set the age limit to 0, Exchange deletes all the entries in the audit log.

Use the Get-AdminAuditLogConfig cmdlet to view the administrator audit logging configuration settings.

Click Start -> All Programs -> Microsoft Exchange Server 2010 -> Exchange Management Shell -> Get-AdminAuditLogConfig

If you found administrator audit logging is disabled (AdminAuditLogEnabled :False), then you can always enable it by applying following statement:

Set-AdminAuditLogConfig -AdminAuditLogEnabled:$true

Admin logs can be retrieved using (a) Search-AdminAuditLog cmdlet, (b) New-AdminAuditLogSearch cmdlet and using (C) Exchange Control Panel (ECP) Auditing Reports page. You can't use the EMC to search for audit log entries.

When you run the Search-AdminAuditLog cmdlet, the audit log entries that match the search criteria that you specify are returned. Without any criteria Search-AdminAuditLog returns all Cmdlets entries.  By default it returns 1,000 log entries. You can use ResultSize parameter to specify up to 250,000 log entries.

Click Start -> All Programs -> Microsoft Exchange Server 2010 -> Exchange Management Shell -> Search-AdminAuditLog

 In above search result you can see user Farhan Kazi modified Internet Receive Connector on 3rd June 2013.

ObjectModified: This field contains the object that was modified by the cmdlet specified in the CmdletName field.

CmdletName: This field contains the name of the cmdlet that was run by the user in the Caller field.

ModifiedProperties: This field contains the properties that were modified on the object in the ObjectModified field.

Caller: This field contains the user account of the user who ran the cmdlet in the CmdletName field.

You can always filter display fields:

Search-AdminAuditLog |fl Caller, CmdletName, ObjectModified

We can specify different search criteria, like following:

Search-AdminAuditLog -Cmdlet Set-ReceiveConnector

Above statement will only query records that includes any modification to Receive Connector.

Search-AdminAuditLog -StartDate "06/03/2013 07:00" -EndDate "06/03/2013 16:00"

Above will search records with date criteria.

Search-AdminAuditLog -Cmdlet Set-ReceiveConnector -StartDate (Get-Date).AddDays(-3) -EndDate (Get-Date)

Above statement will display last 3 days records.

You can use |Out-File to send pipelined output directly to a text file rather than displaying that output on screen.

Search-AdminAuditLog |Out-File C:\ExchAuditLog.txt

By default Out-File saves the data exactly the way that data appears in your Windows PowerShell console. That means that some of the data could end up truncated. To avoid that just include the -width parameter and specify a different line width (in characters) for the text file.

You can also use |Export-Csv cmdlet which makes it easy to export data as a comma-separated values (CSV) file.

Search-AdminAuditLog |Export-Csv C:\ExchAuditLog.csv

You can use New-AdminAuditLogSearch cmdlet to search for audit log entries that meet the criteria you specify, and then send those results to a recipient you specify as an XML file attachment. The results are sent to the recipient within 15 minutes.

After the New-AdminAuditLogSearch cmdlet is run, the report is delivered to the mailboxes you specify within 15 minutes. The log is included as an XML attachment on the report e-mail message. The maximum size of the log that can be generated is 10 MB.

New-AdminAuditLogSearch -Name "Receive Connector Audit" -Cmdlet Set-ReceiveConnector -StartDate "06/03/2013 07:00" -EndDate "06/03/2013 16:00" -StatusMailRecipients ""

This example finds all the administrator audit log entries that match the above criteria and sends the results to specified email address.

Once the report has been received, we can save the attached XML file and open it up in an XML Editor.  I chose to use XML Notepad (

You can also use the Exchange Control Panel (ECP) to export the administrator audit log. On the Auditing tab in the Exchange Control Panel, you can search for and export entries from the administrator audit log and the mailbox audit log.
  1. Log on to Outlook Web App.
  2. Click Options, and then click See All Options.
  3. In the drop-down list box next to Mail > Options, click My Organization from the Select what to manage list.
  4. Click Roles & Auditing from left side panel, and then click Auditing tab.
From there, we can see that we can view some Auditing Reports.You can select "Export the administrator audit log.." - this allows you to search for and export information about configuration changes made in your organization.

The report can take several minutes and even longer depending on how much of a time period we are searching through. Once the report has been received, we can save the attached XML file and open it up in an XML Editor. 

Saturday, April 27, 2013

Creating Custom Active Directory Attributes

This will guide on how to create custom Active Directory attributes where an existing attribute is not available. For example, creating an attribute to hold user's "Medicare Card Number".

Adding custom attributes involves modification in AD schema which requires you to be a member of Schema Administrators and Enterprise Administrators groups. By default, the administrator account is a member of the Schema Administrator group.
Or as an alternative to extending the schema there are existing attributes called ExtensionAttribute1 through to ExtensionAttribute15, which can be used for storing custom data without extending the schema.

You can view user object attributes through Attribute Editor tab in user properties.

Before you add attribute into schema you need to register Schema snap-in because by default Active Directory Schema its not available in management console.
  • Navigate to Start > Run >mmc
  • Open File > Add/Remove Snap-in..
  • You will notice there is no "Active Directory Schema"
  • To register Schema snap-in type RegSvr32 SchmMgmt.dll in Run text box and hit OK.
  • On successful SchmMgmt.dll registration Windows will show information message box.
  •  Open Schema snap-in. Start > Run > mmc.exe > File >Add/Remove Snap-in >Active Directory Schema > Add
  • Expand Active Directory Schema, right-click Attributes and click on "Create Attribute.."

  • Click on Continue, if you receive schema object creation warning message.

In order to proceed with the next step, you will need to generate an Object Identifier (OID) for the Unique X500 Object ID field.
You can generate OID either using by PowerShell or VBScript.

Generating OID using PowerShell (Microsoft Link):

Navigate to StartAll Programs > Accessories > Windows PowerShell > Windows PowerShell
Copy and Paste following statements on PowerShell window.

Copy OID string (dot separated numeric string) and paste into Unique X500 Object ID field.

Generating OID using VBScript (Microsoft Link):
Open following link in Web browser and copy VB script code and paste into Notepad.
Save notepad file with "OIDGen.vbs" (enclosed with double quotes, otherwise it will suffix .txt after .vbs) name on C: drive

Open command prompt and run this script.  Start > Run > Cmd.exe > CScript.exe C:\OIDGen.vbs
Copy OID string (dot separated numeric string) and paste into Unique X500 Object ID field. 

  • In Create New Attribute dialog box enter Common Name (in this case Medicare Number)
  • LDAP Display Name field will automatically populate from Common Name (without space)
  • Paste OID string that we generate in previous steps into Unique X500 Object ID field.
  • Write Description in text box.
  • Choose attribute type (in this case Medicare Number is a numeric value) by selecting appropriate Syntax from drop down list. This could be of a different type and depends on the usage of each attribute)
  • Click OK

Custom attribute medicareNumber is created.

  • We will now add/associate this new attribute to the User class.  Navigate to the Classes leaf and select the User class.
  • Right-click User, click on Properties.
  • Navigate to the Attributes tab. Click on Add.
  • Locate the medicareNumber attribute and click OK, and again OK.
  • Just to confirm that the attribute has been associated with User, right-click User, properties and navigate to the Attributes tab. The medicareNumber attribute should be present in the list of Optional attributes.
This completes the creation of a custom attribute.

Open Active Directory Users and Computers snap-in and check user properties for custom attribute.

 You can set this attribute's value by clicking Edit button and entering appropriate value.

To view all users with Medicare Card number set, you can run following command line statement.
DSQuery * -Filter (medicareNumber=*) -Attr Name, medicareNumber

Monday, February 22, 2010

Find Empty Active Directory Groups

Following one-liners will find Active Directory Groups that have no users.

** To find empty Global Security groups:
Click Start -> Run -> Cmd.exe -> OK -> Copy and Paste following statement

DSQuery * -Filter "(&(sAMAccountType=268435456)(!member=*))" -Limit 0

** You can save the output to a text file by using Dos redirection operator > with file name.

DSQuery * -Filter "(&(sAMAccountType=268435456)(!member=*))" -Limit 0 >C:\EmptyGroups.txt

Above statement will create EmptyGroups.txt file on C: drive root listing all empty security groups.

** To find empty Local Security groups:

DSQuery * -Filter "(&(sAMAccountType=536870912)(!member=*))" -Limit 0

** To find empty Distribution groups:

DSQuery * -Filter "(&(sAMAccountType=268435457)(!member=*))" -Limit 0

 ** To find ALL empty groups (either local, global Security or Distribution groups):

DSQuery * -Filter "(&(objectClass=group)(!member=*))" -Limit 0

Monday, February 15, 2010

Windows 2003 Domain Rename Guide

Domain rename operations are a serious business and involve extensive planning and lab work before implementing this process in production.

Following items must be considered before applying the procedure in production environment:
  • It is extremely important and highly recommended that you test the domain rename procedure 2 to 3 times prior to performing it in a production environment. First, perform the domain rename procedure described in this document in a test environment that has a minimum of two domains and few client machines.
  • You must ensure that you have a functioning and current backup of your Active Directory infrastructure and you have tested the recovery plan in mind if domain domain rename fail.
  • Microsoft cannot guarantee that any third-party software that is installed will function after changing the domain name. You should test and work with third-party application to ensure you understand how product will react to domain name change.
  • A domain rename will work just fine IF you know what you are doing. We have renamed test domain (2 DC's, Trust, child domain and 2 member servers). Please note this is a group project that may require a month with testing, testing and testing.
      1)    Farhan's Guide for Domain Rename
      2)    Step-by-Step Guide to Implementing Domain Rename
      3)    Implementing an Active Directory Domain Rename Operation

Note: If you encounter any problem in downloading the attachments kindly leave comment.

Hope this helps!

Wednesday, February 10, 2010

Servers Shared Folders Auto Backup Batch

Following batch script will make incremental backup of network share folders from different servers to one particular server. The only thing that you need is to list all shares from different servers into a text file (Shares.txt). Script will copy all data with structure to a target server share that you will provide inside the script (TargetPath variable).

Sample shares.txt file:

SETLOCAL EnableDelayedExpansion
TITLE :: Auto Backup Batch ::

SET MM=%DATE:~4,2%
SET DD=%DATE:~7,2%
SET vDate=%DD%_%MM%_%YYYY%.txt

::User Editable Variables - START
SET SLFile=Shares.txt
:: User Editable Variables - END

IF NOT EXIST "%SLFile%" ECHO ERROR: '"%SLFile%"' file not found. &PAUSE &GOTO DisconnectDrives
IF NOT EXIST "%TargetPath%" ECHO ERROR: '"%TargetPath%"' location does not exist. &PAUSE &GOTO DisconnectDrives
IF NOT EXIST "ExcludeList.txt" ECHO \NONE\>"ExcludeList.txt"

FOR /F "delims=" %%A IN ('TYPE Shares.txt') DO (
    SET LogFile=%%A
    SET LogFile=!LogFile:\\=!
    SET LogFile=!LogFile:\=_!_!vDate!.txt
    FOR %%I IN ("%%A") DO ECHO. &ECHO ======== EXECUTING BACKUP JOB FOR %%~nI ======== &ECHO.
    :: Source drive mapping
    CALL :GetFreeDrive
    IF /I !FreeDrv!==FALSE (
        ECHO ERROR: No free drive letter available.
        GOTO :ExitScript
    ) ELSE (SET SDrive=!FreeDrv!)
    ECHO Mapping source %%A with drive letter ^(!SDrive!^)
    IF NOT EXIST "%%A" ECHO ERROR: Invalid source ^(%%A^) path. &PAUSE &GOTO DisconnectDrives
    :: Target drive mapping
    CALL :GetFreeDrive
    IF /I !FreeDrv!==FALSE (
        ECHO ERROR: No free drive letter available.
        GOTO :ExitScript
    ) ELSE     (SET TDrive=!FreeDrv!)
    ECHO Mapping target !TargetPath! with drive letter ^(!TDrive!^)
    IF NOT EXIST "!TargetPath!\%%~nA" MD "!TargetPath!\%%~nA"
    NET USE !TDrive! "!TargetPath!\%%~nA" /PERSISTENT:NO
    :: Copying files
    ECHO Copying files from drive !SDrive! to !TDrive!, please wait...
    XCOPY !SDrive! !TDrive! /D /E /C /S /H /R /Y /EXCLUDE:ExcludeList.txt
    ECHO. &ECHO File Copying Finish.
    ECHO Closing Network Connection... &ECHO.
    PING -n 20 -l 5 >NUL
    IF EXIST "!LogFile!" (
        FOR %%R IN ("!LogFile!") DO IF %%~zR EQU 0 DEL /F /Q "!LogFile!")
    CALL :DisconnectDrives)
GOTO :ExitScript

SET FreeDrv=
SET DriveLtrs=C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z

FOR %%a IN (!DriveLtrs!) DO (
    SET FreeDrv=TRUE
    FOR /F "delims=:" %%b IN ('WMIC LOGICALDISK GET Name ^|FIND ":"') DO IF /I "%%a"=="%%b" SET FreeDrv=FALSE
    IF /I "!FreeDrv!"=="TRUE" (
        SET FreeDrv=%%a:
        EXIT /B 0))
IF EXIST !SDrive! ECHO Disconnecting Mapped Drive (!SDrive!) &NET USE !SDrive! /DELETE /Y
IF EXIST !TDrive! ECHO Disconnecting Mapped Drive (!TDrive!) &NET USE !TDrive! /DELETE /Y


Thursday, October 8, 2009

Check Internet Explorer Version of Client Computers

Following batch script requires two variables to be set with actual values.
Set 'InputFile' variable with a file name with path from where it will pick computer names.
This file should contain computer name on each line, like:


Set "OutputFile" variable with a file name including path where it will store the results.
So here you go...

SET InputFile=C:\Computers.txt
SET OutputFile=C:\IEInfo.txt

IF NOT EXIST "%InputFile%" ECHO **ERROR** "%InputFile%" file does not exist. &GOTO :ExitScript
FOR %%R IN ("%InputFile%") DO IF %%~zR EQU 0 ECHO **ERROR** "%InputFile%" file is empty! &GOTO :ExitScript
FOR /F "delims=" %%C IN ('TYPE "%InputFile%"') Do (
    PING -n 1 -l 10 -w 100 %%C |FIND /I "TTL" >NUL
        ECHO Processing: %%C
        FOR /F "tokens=3" %%v IN ('REG QUERY "\\%%C\HKLM\SOFTWARE\Microsoft\Internet Explorer" /v Version ^|FIND /I "REG_SZ"') DO (
            ECHO Computer: %%C    IE Version: %%v>>"%OutputFile%"))ELSE (ECHO **ERROR** Unable to connect %%C))
ECHO. &ECHO Script finish, check "%OutputFile%" file for result.