Active Directory: E-mail Notification for Newly Added Users and Computers

Filed under: Active Directory, Quick Code, VBScript — Written by Chrissy on Monday, February 12th, 2007 @ 8:26 am

Recently, someone asked if there was a way to be notified when servers have been added to an Active Directory domain. I looked around the Internet and it seems there's not a direct way to do this without some large software package. So in lieu of having an instant notification, I created a script that tallies up newly added user and computer accounts and emails the admin with all the info. This script is initially setup to be run once a day, but you can modify to whatever frequency you want.

I really expected this to take at most a couple hours to write but parsing through all the data turned out to take a heckofa lot of time. From converting the desired comparison date to UTC to parsing the AD attribute memberOf, I spent a good day working on this. I learned a lot, though and found this page which details the AD schema to be very helpful.

If you need anything more than this, you may want to consider an enterprise-type administration package such as Microsoft's MOM.

ADAddedUsersNComputers.vbs

'****************************************************************************
' This script created by Chrissy LeMaire (clemaire@gmail.com)
' Website: http://netnerds.net/
'
' ADAddedUsersNComputers.vbs
'
' This script Checks AD for any additions made to Users or Computers
' in the past 24 hours. The time interval to check can be changed below.
'
' NO WARRANTIES, USE THIS AT YOUR OWN RISK, etc.
'*****************************************************************************
 
'Please modify these four settings
strSMTPServer = "myexchangeserver"
strEmailFrom = "Administrator  <me@mydomain.com>"
strEmailTo = "Administrator <me@mydomain.com>"
 
strTimeInUTC = CompareDateUTCConvert("h",-24) 'This is the same syntax as dateAdd(). The example will get new users/computers added in the past 24 hours.
 
'Unless you want to change the domain to check or the format of the emailed info, nothing below really needs to be modified.
On Error Resume Next
numPersonCount = 0
numComputerCount = 0
 
Set objAdRootDSE = GetObject("LDAP://RootDSE")
Set objRS = CreateObject("adodb.recordset")
  varConfigNC = objAdRootDSE.Get("defaultNamingContext")
  strConnstring = "Provider=ADsDSOObject"
  strWQL = "SELECT ADsPath FROM 'LDAP://" & varConfigNC & "' WHERE createTimeStamp > '" & strTimeInUTC & "' and (objectCategory = 'Person' or objectCategory = 'Computer')"
 
  objRS.Open strWQL, strConnstring
      Do until objRS.eof
        Set objADUserOrComputer = GetObject(objRS.Fields.Item(0))
        strObjectCategory = ParseDN(objADUserOrComputer.objectCategory)
 
        Select Case strObjectCategory
        Case "Person"
        numPersonCount = numPersonCount + 1
            If Len(objADUserOrComputer.displayName) > 0 Then strUserMsg = strUserMsg & vbCrLf & "displayName = " & objADUserOrComputer.displayName
            'strUserMsg = strUserMsg & vbCrLf & "distinguishedName = " & objADUserOrComputer.distinguishedName
            strUserMsg = strUserMsg & vbCrLf & "sAMAccountName = " & objADUserOrComputer.sAMAccountName
            strUserMsg = strUserMsg & vbCrLf & "sAMAccountType = " & SAMAccountTypetoName(objADUserOrComputer.sAMAccountType)
            strUserMsg = strUserMsg & vbCrLf & "whenChanged = " & objADUserOrComputer.whenChanged
            strUserMsg = strUserMsg & vbCrLf & "whenCreated = " & objADUserOrComputer.whenCreated
            strUserGroups = ParseMemberOf(objADUserOrComputer.memberOf,objADUserOrComputer.PrimaryGroupID)
            strUserMsg = strUserMsg & vbCrLf & "Member Of: " & strUserGroups
            If Len(objADUserOrComputer.userPrincipalName) > 0 Then strUserMsg = strUserMsg & vbCrLf & "userPrincipalName = " & objADUserOrComputer.userPrincipalName
            strUserMsg = strUserMsg & vbCrLf
         Case "Computer"
         numComputerCount = numComputerCount + 1
            strCompMsg = strCompMsg & vbCrLf & "dNSHostName = " & objADUserOrComputer.dNSHostName
            strCompMsg = strCompMsg & vbCrLf & "isCriticalSystemObject = " & objADUserOrComputer.isCriticalSystemObject
            strCompMsg = strCompMsg & vbCrLf & "operatingSystem = " & objADUserOrComputer.operatingSystem
            strCompMsg = strCompMsg & vbCrLf & "operatingSystemServicePack = " & objADUserOrComputer.operatingSystemServicePack
            strCompMsg = strCompMsg & vbCrLf & "operatingSystemVersion = " & objADUserOrComputer.operatingSystemVersion
            If InStr(objADUserOrComputer.rIDSetReferences,"Domain Controller") > 0 Then strCompMsg = strCompMsg & vbCrLf & "Domain Controller = Yes"
            If Len(objADUserOrComputer.description) > 0 Then strCompMsg = strCompMsg & vbCrLf & "description = " & objADUserOrComputer.description
             If Len(objADUserOrComputer.machineRole) > 0 Then strCompMsg = strCompMsg & vbCrLf & "machineRole = " & objADUserOrComputer.machineRole
            If Len(objADUserOrComputer.physicalLocationObject) > 0 Then strCompMsg = strCompMsg & vbCrLf & "physicalLocationObject = " & ParseDN(objADUserOrComputer.physicalLocationObject)
            strCompMsg = strCompMsg & vbCrLf
         End Select
        objRS.movenext
        Set objADUserOrComputer = Nothing
      Loop
    objRS.close
Set objRS = Nothing
Set objAdRootDSE = Nothing
 
If Len(strUserMsg) > 0 Then strEmailMessage = strEmailMessage & "--------- USERS ---------" & vbCrLf & strUserMsg & vbCrLf
If Len(strCompMsg) > 0 Then strEmailMessage = strEmailMessage & "--------- COMPUTERS ---------" & vbCrLf & strCompMsg
If Len(strUserMsg) = 0 And Len(strCompMsg) = 0 Then strEmailMessage = "No users or computers have been added in the last 24 hours."
 
Set objCDO = CreateObject("CDO.Message")
    objCDO.Subject = "Users Added: " & numPersonCount & ". Computers Added: " & numComputerCount & "."
    objCDO.From = strEmailFrom
    objCDO.To = strEmailTo
    objCDO.TextBody = strEmailMessage
    objCDO.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2 'cdoSendUsingPort (1 = local, 3 = Exchange)
    objCDO.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = strSMTPServer
    objCDO.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
    objCDO.Configuration.Fields.Update
    objCDO.Send
    set objCDO = Nothing
 
Function CompareDateUTCConvert(dateAddInterval,compareNumber)
'Wow, this is a lil complex. So createTimestamp is in UTC format.
'So first we grab your machine's time bias and then apply it.
'Next, we adjust the date to the one you specified above (now()-24hours by default)
'Finally, we parse the final date to UTC format ie. 20070207032200.0Z
 
Set objSWbemServices = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\.\root\cimv2")
  Set colTimeZone = objSWbemServices.ExecQuery("SELECT * FROM Win32_TimeZone")
    For Each objTimeZone in colTimeZone
     strBias = objTimeZone.Bias
    Next
  Set colTimeZone = Nothing
Set objSWbemServices = Nothing
  strCompareDate = dateAdd(dateAddInterval,compareNumber,now())
  strUTCCompare = DateAdd("n",strBias*(-1),strCompareDate)
  CurrentUTC = Year(strUTCCompare) & Right("0" & Month(strUTCCompare),2) & Right("0" & Day(strUTCCompare),2)
  CurrentUTC = CurrentUTC & Right("0" & Hour(strUTCCompare),2) & Right("0" & Minute(strUTCCompare),2) & Right("0" & Second(strUTCCompare),2) & ".0Z"
  CompareDateUTCConvert = CurrentUTC
End Function
 
Function ParseDN(strDN)
'Take a DN and extract what we want then make it pretty.
  arrDN = split(strDN,",") 'CN=Example-Thing,CN=Whatever,CN=Etc
  strDN = right(arrDN(0),len(arrDN(0))-3) 'CN=Example-Thing -> Example-Thing
  strDN = replace(strDN,"-"," ") 'Example Thing
  ParseDN = strDN
End Function
 
Function ParseMemberOf(memberof,primarygroupid)
'This shows what groups a person belongs to.
'The output of memberof changes depending on
'how many groups the user is a member of, etc.
  Select Case TypeName(memberof)
    Case "String" ParseMemberOf = ParseDN(memberof)
    Case "Empty" ParseMemberOf = PrimaryGroupIDtoName(primarygroupid,varConfigNC)
    Case "Variant()"
      For each groupDN in memberof
         strUserGroups = strUserGroups & vbCrLf & ParseDN(groupDN)
      Next
      ParseMemberOf = strUserGroups
    Case Else ParseMemberOf = "Unknown"
  End Select
End Function
 
Function SAMAccountTypetoName(theType)
'Just makin it more useful...
  Select Case theType
    Case 268435456 SAMAccountTypetoName = "Group Object"
    Case 268435457 SAMAccountTypetoName = "Non-Security Group Object"
    Case 536870912 SAMAccountTypetoName = "Alias Object"
    Case 536870913 SAMAccountTypetoName = "Non-Security Alias Object"
    Case 805306368 SAMAccountTypetoName = "Normal User Account"
    Case 805306369 SAMAccountTypetoName = "Machine Account"
    Case 805306370 SAMAccountTypetoName = "Trust Account"
    Case 1073741824 SAMAccountTypetoName = "App Basic Group"
    Case 1073741825 SAMAccountTypetoName = "App Query Group"
    Case 2147483647 SAMAccountTypetoName = "Account Type Max"
    Case Else SAMAccountTypetoName = "Unknown"
  End Select
End Function
 
Function PrimaryGroupIDtoName(PGID,varConfigNC)
'Ugh.. the alternative to this function can be found here:
'http://support.microsoft.com/kb/297951
'both are kinda nasty.
  Set objRSPGID = CreateObject("adodb.recordset")
    Connstring = "Provider=ADsDSOObject"
    strSQL = "<ldap://" & varConfigNC & ">;(objectCategory=group);distinguishedName,primaryGroupToken,name;subtree"
     objRSPGID.Open strSQL, Connstring
      If not objRSPGID.eof and not objRSPGID.bof Then
        Do until objRSPGID.eof Or Len(strGroupName) > 0
        If PGID = objRSPGID("primaryGroupToken") Then strGroupName = objRSPGID("name")
         objRSPGID.movenext
        Loop
      End If
     objRSPGID.close
  Set objRSPGID = Nothing
  If Len(strGroupName) = 0 Then strGroupName = "Unknown"
  PrimaryGroupIDtoName =   strGroupName
End Function

To schedule this, save the above code as ADAddedUsersNComputers.vbs in C:\scripts (for ex.) and use Scheduled Tasks to run the following command: %windir%\system32\wscript.exe C:\scripts\ADAddedUsersNComputers.vbs. I suggest running it daily at the end of each workday.

13 Comments   -
  • Comment by Wayne Hall | March 6, 2007 @ 3:58 pm

    This is a great script - thanx much. I found it while looking for a script that notifies people of changes to groups. Ideally, I'd like a script to run at some interval, record the members of each group, and if members have been added or removed, notify a specific person (or the groups' owner object).

    On the front of it, it should be pretty easy to do in perl or php and use a diff-type of function, and I can easily see it extending to a SQL database or some storage mechanism. Or maybe putting it into something like PBNJ, which uses nmap to discover new hosts and open ports on the network and notify of changes. This would be the same thing, but for Active Directory objects.

    Got any tips for what I'm looking for?

  • Comment by Bernard | March 30, 2007 @ 3:01 pm

    What timezone does the program use...When running, the time the account is added is wrong

  • Comment by Chrissy | March 30, 2007 @ 5:08 pm

    Hey Bernard,
    It's initially GMT but then a bias is added depending on your computer's timezone setting. How many hours difference do you see?

    As for your request wayne, that seems like a lot of work. After taking a long ass time to write the script above (the parsing was a pain), I'd look for a small package from some lil company. Maybe you can look into software from the company that makes RestoreADmin. That program is rad...it stores the AD scheme in a sql db.

  • Comment by Bernard | April 2, 2007 @ 12:50 pm

    Thanks for your response Chrissy, I see a four hour difference. we are in the central time zone. Should I just adjust the bias -4?

  • Comment by Adrian Schmitz | April 10, 2007 @ 12:57 pm

    Very handy script.. Thanks for sharing!

  • Comment by Mirza A. Baig | June 7, 2007 @ 5:19 am

    Hello,
    I have been using this script for months now. It has been really handy. All I really need is for it to send email notification once a computer account is created. It was working, but now it is not. Nothing within the script has changed. It does still sends me notifications, but it says that 0 new users and 0 new computers have been added. Can you please assist?

  • Comment by Mirza A. Baig | June 7, 2007 @ 5:21 am

    Do you have a script that will send email notification once a computer account has been moved to a particular OU?

  • Comment by David Sipp | July 11, 2007 @ 12:35 am

    Exactly what I need. But I can not get a result. I am a domain admin and know the names and DNS are good. Scrip runs error free, just never recieve the results email? Here is what i have done to the variables section:
    'Please modify these four settings
    strSMTPServer = "daltfccxm01"
    strEmailFrom = "David Sipp "
    strEmailTo = "User "

    strTimeInUTC = CompareDateUTCConvert("h",-72)

    What can I check?

    Thanks,

    Dsipp

  • Comment by David Sipp | July 11, 2007 @ 4:39 am

    Ok, I figured it out. AV software caused the block. DOHHH!

  • Comment by Phil | August 10, 2007 @ 12:06 am

    Any chance you have some code that will email when any AD user or computer account data is changed?

    Thanks!

  • Comment by RT | September 19, 2007 @ 10:31 pm

    Is there anyway you can modify the script so that it ONLY emails if there are changes in the AD. I manage many servers and I do not need emails for servers that did not change.

    Thx.

  • Comment by Mohamed Ghaleb | April 7, 2008 @ 5:45 am

    Hi, I got Windows Script Host Error in line 90 Char 1, Unspecified error, code 80004005, source: (null)

    I am in need to that script any help would be really great.

    Thank you.

  • Comment by Mohamed Ghaleb | April 7, 2008 @ 5:46 am

    By the way, I saved it on the DC it self, and running it with a Domain admin account.

Leave your comment