Monday, September 3, 2012

Who Deleted That Active Directory Object?

Have you ever encountered that an AD object be it an user, computer etc is deleted and no one owns up?
In this situation, the usual questions will be when did it happened, where did it happened (Which DC?), who is the culprit.
You can say that you will be able to trace the culprit by pouring over the security logs if you had enabled auditing but picture this, what if you have over 80 domain controllers and you have no idea when did the deletion take place.
The following procedure will help you provide you the means to get the answers

To get the Distinguished Name of the Delete Object

1)  First open up LDP and connect to a server.
2) Next, bind to the DC you are connected to, click connections and then bind again.
(If all the fields are blank, it will bind with the user credentials that you are currently logged on as)
3) Click on Browse and then Search. Make sure that the control to return deleted objects is properly configured so that the deleted objects will be returned
4) Now we will need to search for the deleted objects. If you go to View and then Tree and leave it blank, it will go to the default naming context which by default is the domain naming context. Once this shows up in the left hand side, expand it then go to the deleted objects container , alt click and then choose search. With this, you will just search for that container and we can look for an attribute that we are looking for.
5) Once the object is located, copy the DN and it will be used in the next step

Who, When , Where?
To gather the information to which when and where, repadmin can be used as below

Repadmin /showobjmeta “DN which was copied in step 5 earlier”

You will get the information as below but what we are actually looking for here is in fact the isdeleted attribute.
This will tell you when the object was deleted and from which domain controller

For the who, you may go to the Domain Controller identified earlier to run through the security logs.
The event ID to look for will be Event ID 630

Thursday, August 16, 2012

A meeting update message or a meeting cancellation message from an Exchange 2003 user is not delivered to external attendee

It was brought to my attention that there is a random issue when a user cancels a meeting and some of the attendees are not notified via a cancellation. Upon digging further, the users that are not receiving these cancellation are found to be external users.
Conversation with the IT folks in these users' companies are that their messaging infrastructure is running on Lotus Notes and Exchange 2007.
The issue has been found to be that of the below and the procedures listed can be used to check if the issue is affecting your Exchange 2003 setup.
When sending meeting changes or cancellations to another mail server outside of your exchange 2003 organization messages get stuck in the queue and if the diagnostics logging of the MSExchangeTransport component is set to maximum, the following warning is logged:
Event Type: Warning
Event Source: MSExchangeTransport
Event Category: Exchange Store Driver
Event ID: 327
If an administrator tries to open the message in the Exchange System Manager console, the administrator may receive the following error message:Unable to open for delivery
To verify this is the issue follow these steps on the message that is stuck.
  1. Launch MFCMAPI and select OK. (MFCMAPI can be downloaded from
  2. Choose Session –> Logon –> Display Store Table
  3. Select the proflle used to open the mailbox
  4. In the returned items look for the row that has "Mailbox – <username>" and double click to open the row
  5. In the new "Mailbox – <username>" window expand the Root – Mailbox folder
  6. Expand the IPM_SUBTREE (or the mailbox) folder
  7. Open the calendar folder by double clicking on it.
  8. In the new "Calendar" window navigate to the appointment item (you can sort by Subject by clicking the Subject column)
  9. Right click the appointment item and choose "Display Recipient Table" from the menu
  10. In the recipients table scroll to the right until you can view the column named "PR_RECIPIENT_TRACKSTATUS"
  11. Note the number value for each recipient and this will indicate their tracking status on the item.
  12. If the value is 0 then it means that the tracking status is not available.
The solution
A hotfix is released by Microsoft to correct this issue as the KB below

Wednesday, August 8, 2012

Token Size report for users

I was posed a question as to how can we tell what is the Token Size for each users in the environment.
Microsoft provides a tool for use to calculate 1 user ( But this tool from what I know only allows calculation of the token size for the current logon user and if computation of token size for another user is needed, the user's credentials will have to be provided (Which is a challenging task).
With that I set out to create a vbs for this and the below is the output that I would like to share.
The computation formula is based on 

Following formula to determine whether it is necessary to modify the MaxTokenSize value or not
TokenSize = [12 X number of user rights] + [token overhead] + [40 X number of group memberships] + 8s
This formula uses the following values:
·         d:  The number of domain logical groups a user is a member of plus the number of universal groups outside the user’s account domain plus the number of groups represented in SID history.
·         s:  The number of security global groups that a user is a member of plus the number of universal groups in a user’s account domain.
·         User rights include rights such as “Log on locally” or “Access this Computer from the network”. The only user rights that are added to an access token are those user rights that are configured on the server that hosts a secured resource.  Most of the users are likely to have only two or three user rights on the Exchange server. Administrators may have dozens of user rights. Each user right requires 12 bytes to store it in the token.
·         Token overhead includes multiple fields such as the token source, expiration time, and impersonation information. For example, a typical domain user has no special access or restrictions; token overhead is likely to be between 400 and 500 bytes.
·         Estimated value for ticket overhead can vary depending on factors such as DNS domain name length, client name and other factors.
·         Each group membership adds the group SID to the token together with an additional 16 bytes for associated attributes and information. The maximum possible size for SID is 68 bytes.  Therefore, each security group to which a user belongs typically adds 44 bytes to the user’s token size.

*** Assumes that [12 X number of user rights] + [token overhead] = 1200

The script is as below. To user copy and save the file as vbs

'Start of VBS

Const ForReading = 1


GlobalGroup = 0
I = 2

' Clean up.
Set objGroupList = Nothing
Set objUser = Nothing


Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile(INPUT_FILE_NAME, FOR_READING)
strSite = objFile.ReadAll

' Create Excel Spreadsheet
Set app = CreateObject("Excel.Application")
Set wb = app.Workbooks.Add
Set ws = wb.Sheets.Add

On Error Resume Next
app.Visible = True

ws.Cells(1,1).Value = "Display Name"
ws.Columns(1).ColumnWidth = 30
ws.cells(1,2).value = "SAMACCountName"
ws.columns(1,2).columnwidth = 30
'ws.Cells(1,3).Value = "Total Groups"
'ws.Columns(3).ColumnWidth = 10
ws.Cells(1,4).Value = "Local group"
ws.Columns(4).ColumnWidth = 10
ws.Cells(1,5).Value = "Universal group"
ws.Columns(5).ColumnWidth = 10
ws.Cells(1,6).Value = "Global group"
ws.Columns(6).ColumnWidth = 10

ws.Cells(1,6).Value = "Token Size"
ws.Columns(6).ColumnWidth = 10

'Replace the domain with your domain in your environment
Strdomain = "DC=contoso,DC=msft"

Set objConnection = CreateObject("ADODB.Connection")
Set objCommand =   CreateObject("ADODB.Command")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"

Set objCOmmand.ActiveConnection = objConnection
objCommand.CommandText = _
    "Select SAMAccountname,distinguishedname,displayname from 'LDAP://" & strDomain & "' " _
        & "Where objectcategory='user' AND SAMAccountname = '*'"
objCommand.Properties("Page Size") = 1000
objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE
Set objRecordSet = objCommand.Execute

Do Until objRecordSet.EOF

ws.cells(I,1).Value = objRecordSet.Fields("displayname").Value
ws.cells(I,2).value = objRecordSet.Fields("SAMAccountname").Value
  On Error Resume Next
wscript.echo objRecordSet.Fields("distinguishedname").Value

' Bind to the user object in Active Directory with the LDAP provider.
Set objUser = GetObject("LDAP://" & objRecordSet.Fields("distinguishedname").Value)

' Bind to dictionary object.
Set objGroupList = CreateObject("Scripting.Dictionary")

' Enumerate group memberships.
Call EnumGroups(objUser)
' ws.cells(I,3).value = TotalGroups
ws.cells(I,4).value = LocalGroup
ws.cells(I,5).value = UniversalGroup
ws.cells(I,6).value = GlobalGroup
'Token Size Computation based on
Tokensize = 1200 + localgroup*40 + 8*(UniversalGroup + GlobalGroup)
ws.cells(I,7).value = Tokensize

'To reset the numbers after each user
GlobalGroup = 0

I = I + 1


'You may wish to change this to any path as needed.
ws.SaveAs "D:\Scripts\CalculateTokenSize\" & "usertoken.xlsx"


Sub EnumGroups(objADObject)
' Recursive subroutine to enumerate user group memberships.
' Includes nested group memberships.
Dim colstrGroups, objGroup, j
objGroupList.CompareMode = vbTextCompare
colstrGroups = objADObject.memberOf

If IsEmpty(colstrGroups) Then
Exit Sub
End If

If TypeName(colstrGroups) = "String" Then
Set objGroup = GetObject("LDAP://" & colstrGroups)
If Not objGroupList.Exists(objGroup.sAMAccountName) Then
objGroupList(objGroup.sAMAccountName) = True
Select Case objGroup.GroupType
    Case 2
        GlobalGroup = GlobalGroup +1
    Case 4
localgroup = Localgroup +1
    Case 8
UniversalGroup=UniversalGroup +1
    Case -2147483646
GlobalGroup = GlobalGroup +1
    Case -2147483644
        localgroup = Localgroup +1
    Case -2147483640
UniversalGroup=UniversalGroup +1
End Select

Call EnumGroups(objGroup)
End If
Set objGroup = Nothing
Exit Sub
End If

For j = 0 To UBound(colstrGroups)
Set objGroup = GetObject("LDAP://" & colstrGroups(j))
If Not objGroupList.Exists(objGroup.sAMAccountName) Then
objGroupList(objGroup.sAMAccountName) = True

Select Case objGroup.GroupType
    Case 2
        GlobalGroup = GlobalGroup +1
    Case 4
localgroup = Localgroup +1
    Case 8
UniversalGroup=UniversalGroup +1
    Case -2147483646
GlobalGroup = GlobalGroup +1
    Case -2147483644
        localgroup = Localgroup +1
    Case -2147483640
UniversalGroup=UniversalGroup +1
End Select

Call EnumGroups(objGroup)

End If


Set objGroup = Nothing
End Sub

'End of VBS