seaninman
asked on
Script to automate some AD and FIle clean up
Was wondering if anyone could help me with this task.... I'm not a scripting guru, but I think it will be two scripts the first one will be ran manually, and the second one I will need to schedule it to run. Now for the details...
Script 1 (when script is ran, have it ask for the username to be processed)
1.Create log file that contains the users:
Date Script was executed
Last Login Date & Time
Group Memberships (Security & Distribution)
2.Export users mailbox to users Home Directory [exchange 2007], if home directory doesnt exist write to directory script is being executed from
3.Disable account, and move it to the Suspect Account OU
Script 2 (use a scheduler to run)
1.Delete below after 30days: NOTE: (not sure if script can be written to only look at a specific OU and see when the account was disabled and if its been disabled for 30+ days delete it, and if the account is deleted to delete the users home directory.)
Users Home Directory
Network Account
Terminal Services Profile
2.Write a log file that captures what user account(s) and home directory was removed
Script 1 (when script is ran, have it ask for the username to be processed)
1.Create log file that contains the users:
Date Script was executed
Last Login Date & Time
Group Memberships (Security & Distribution)
2.Export users mailbox to users Home Directory [exchange 2007], if home directory doesnt exist write to directory script is being executed from
3.Disable account, and move it to the Suspect Account OU
Script 2 (use a scheduler to run)
1.Delete below after 30days: NOTE: (not sure if script can be written to only look at a specific OU and see when the account was disabled and if its been disabled for 30+ days delete it, and if the account is deleted to delete the users home directory.)
Users Home Directory
Network Account
Terminal Services Profile
2.Write a log file that captures what user account(s) and home directory was removed
ASKER
Looks good and works. Is there a way we can clean up the log file to list the memberships? I have attached a script that has been used in the past for parts of this task to maybe assist with the Last Login and output file. What do you think??
Option Explicit
Dim strUserID, lPrintAll, lPrintOnce, objLogonList,objIDList
Dim objShell, lngBias, lngBiasKey, k, arrMemberOf, blnFirstServer
Dim objRootDSE, strConfig, arrDesc, strDesc, strItem, blnFirstDone
Dim strDNSDomain, adoCommand, adoConnection, adoRecordset
Dim strBase, strFilter, strAttributes, strQuery
Dim objDC, arrstrDCs(), strDN, strKey, strDisplayName, strUser
Dim dtmDate, objDate, lngHigh, lngLow, w, ws
Dim Group, i, objGroup,intGroupType,arrGroupName(),temp
const ForReading = 1
const ForWriting = 2
const ForAppending = 8
Const TristateFalse = 0
Const ADS_GROUP_TYPE_GLOBAL_GROUP = &h2
Const ADS_GROUP_TYPE_LOCAL_GROUP = &h4
Const ADS_GROUP_TYPE_UNIVERSAL_GROUP = &h8
Const ADS_GROUP_TYPE_SECURITY_ENABLED = &h80000000
'******************
' If no user name is input, all users will be listed but groups membership will not be listed
'******************
If WScript.Arguments.Count = 1 Then
strUserID = WScript.Arguments.Item(0)
lPrintAll = 0
else
strUser = InputBox ("User Name to list Members:","Enter UserName")
if strUser = "" then
strUserID = "AllADUsers"
lPrintAll = 1
else
lPrintAll = 0
strUserID = strUser
end if
end if
lPrintOnce = 1
'========================
' Use a dictionary object to track latest lastLogon for each user.
'========================
Set objLogonList = CreateObject("Scripting.Dictionary")
Set objIDList = CreateObject("Scripting.Dictionary")
objLogonList.CompareMode = vbTextCompare
objIDList.CompareMode = vbTextCompare
'==== Obtain local Time Zone bias from machine registry. ====
Set objShell = CreateObject("Wscript.Shell")
lngBiasKey = objShell.RegRead("HKLM\System\CurrentControlSet\Control\" _
& "TimeZoneInformation\ActiveTimeBias")
If (UCase(TypeName(lngBiasKey)) = "LONG") Then
lngBias = lngBiasKey
ElseIf (UCase(TypeName(lngBiasKey)) = "VARIANT()") Then
lngBias = 0
For k = 0 To UBound(lngBiasKey)
lngBias = lngBias + (lngBiasKey(k) * 256^k)
Next
End If
'========================
' Determine configuration context and DNS domain from RootDSE object.
'========================
Set objRootDSE = GetObject("LDAP://RootDSE")
strConfig = objRootDSE.Get("configurationNamingContext")
strDNSDomain = objRootDSE.Get("defaultNamingContext")
'========================
' Use ADO to search Active Directory for ObjectClass nTDSDSA.
' This will identify all Domain Controllers.
'========================
Set adoCommand = CreateObject("ADODB.Command")
Set adoConnection = CreateObject("ADODB.Connection")
adoConnection.Provider = "ADsDSOObject"
adoConnection.Open "Active Directory Provider"
adoCommand.ActiveConnection = adoConnection
strBase = "<LDAP://" & strConfig & ">"
strFilter = "(objectClass=nTDSDSA)"
strAttributes = "AdsPath"
strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"
adoCommand.CommandText = strQuery
adoCommand.Properties("Page Size") = 100
adoCommand.Properties("Timeout") = 60
adoCommand.Properties("Cache Results") = False
Set adoRecordset = adoCommand.Execute
'========================
' Enumerate parent objects of class nTDSDSA. Save Domain Controller
' AdsPaths in dynamic array arrstrDCs.
'========================
k = 0
Do Until adoRecordset.EOF
Set objDC = GetObject(GetObject(adoRecordset.Fields("AdsPath").Value).Parent)
ReDim Preserve arrstrDCs(k)
arrstrDCs(k) = objDC.DNSHostName
k = k + 1
adoRecordset.MoveNext
Loop
adoRecordset.Close
'***********************
' Retrieve lastLogon attribute for each user on each Domain Controller.
'***********************
if lPrintAll = 1 Then
strFilter = "(&(objectCategory=person)(objectClass=user))"
else
strFilter = "(&(objectCategory=person)(objectClass=user)(sAMAccountName=" & strUserID & "))"
end if
strAttributes = "sAMAccountName,EmployeeID,userprincipalname,displayName,title,description,manager," & _
"department,distinguishedName,userAccountControl,whenCreated,lastLogon,MemberOf"
blnFirstServer = True
blnFirstDone = False
For k = 0 To Ubound(arrstrDCs)
strBase = "<LDAP://" & arrstrDCs(k) & "/" & strDNSDomain & ">"
strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"
adoCommand.CommandText = strQuery
On Error Resume Next
Set adoRecordset = adoCommand.Execute
If (Err.Number <> 0) Then
On Error GoTo 0
msgbox "Domain Controller not available: " & arrstrDCs(k)
Else
On Error GoTo 0
Do Until adoRecordset.EOF
strKey = adoRecordset.Fields("sAMAccountName").Value
if blnFirstServer Then
blnFirstDone = True
strDN = strKey & "; " & adoRecordset.Fields("EmployeeID").Value
strDN = strDN & "; " & adoRecordset.Fields("userprincipalname").Value
strDisplayName = adoRecordset.Fields("displayName").Value
If IsNull(strDisplayName) Then
strDisplayName = strUserID
ElseIf StrDisplayName = "Internet Guest Account" Then
strDisplayName = strUserID
End IF
'**********************
' The description attribute is multi-valued, but
' there is never more than one item in the array.
'**********************
arrDesc = adoRecordset.Fields("description").Value
If IsNull(arrDesc) Then
strDesc = ""
Else
For Each strItem In arrDesc
strDesc = strItem
Next
End If
strDN = strDN & "; " & adoRecordset.Fields("displayName").Value
strDN = strDN & "; " & adoRecordset.Fields("title").Value
strDN = strDN & "; " & strDesc
strDN = strDN & "; " & adoRecordset.Fields("manager").Value
strDN = strDN & "; " & adoRecordset.Fields("department").Value
strDN = strDN & "; " & adoRecordset.Fields("distinguishedName").Value
strDN = strDN & "; " & adoRecordset.Fields("userAccountControl").Value
strDN = strDN & "; " & adoRecordset.Fields("whenCreated").Value
arrMemberOf = adoRecordset.Fields("MemberOf").Value
End If
On Error Resume Next
Set objDate = adoRecordset.Fields("lastLogon").Value
If (Err.Number <> 0) Then
On Error GoTo 0
dtmDate = #1/1/1601#
Else
On Error GoTo 0
lngHigh = objDate.HighPart
lngLow = objDate.LowPart
If (lngLow < 0) Then
lngHigh = lngHigh + 1
End If
If (lngHigh = 0) And (lngLow = 0 ) Then
dtmDate = #1/1/1601#
Else
dtmDate = #1/1/1601# + (((lngHigh * (2 ^ 32)) _
+ lngLow)/600000000 - lngBias)/1440
End If
End If
'***************************
'This is the snippet that creates the final list. If it finds a strDN, nothing is added
'***************************
If (objLogonList.Exists(strKey) = True) Then
If (dtmDate > objLogonList.Item(strKey)) Then
objLogonList.Item(strKey) = dtmDate
End If
Else
objLogonList.Add strKey, dtmDate
objIDList.Add strKey, strDN
End If
adoRecordset.MoveNext
Loop
If blnFirstDone Then
blnFirstServer = False
End If
adoRecordset.Close
End If
Next
'******************
'Only executed once to put the date at the top of the output
'******************
If lPrintOnce = 1 Then
lPrintOnce = 0
Set ws = CreateObject ("Scripting.FileSystemObject")
If lprintAll = 1 Then
Set w = ws.OpenTextFile ( "AllADUser" & ".txt", ForAppending, True)
Else
Set w = ws.OpenTextFile ( strDisplayName & ".txt", ForAppending, True)
End If
w.writeLine Now
w.writeLine ""
w.writeLine Replace(strAttributes, ",", ";")
End If
'***********************
' Output all IDs collected
'***********************
For Each strKey In objIDList.Keys
w.writeLine objIDList.Item(strKey) & " ; " & objLogonList.Item(strKey)
Next
w.writeLine ""
'***********************************
'If this is a single user listing - output group memberships
'***********************************
If lprintAll = 0 Then
'***************************
'Create a new Array to hold the names of the groups. ArrMember contains DSN
'***************************
If Not IsNull( arrMemberOf ) Then
i = 0
ReDim arrGroupName(UBound(arrMemberOf))
For Each Group in arrMemberOf
On Error Resume Next
Set objGroup = GetObject("LDAP://" & Group)
If (Err.Number <> 0) Then
arrGroupName(i) = "? " & Group
Else
intGroupType = objGroup.GroupType
If intGroupType AND ADS_GROUP_TYPE_SECURITY_ENABLED Then
arrGroupName(i) = "S " & objGroup.sAMAccountName
Else
arrGroupName(i) = "D " & objGroup.sAMAccountName
End If
End If
i = i + 1
Next
'**************************
'Sort the Array of Group Names
'**************************
For i = UBound(arrGroupName) - 1 To 0 Step -1
For k= 0 to i
If arrGroupName(k)>arrGroupName(k+1) then
temp=arrGroupName(k+1)
arrGroupName(k+1)=arrGroupName(k)
arrGroupName(k)=temp
End if
Next
Next
'*************************
'Output the sorted array of group names
'*************************
k = UBound(arrGroupName)
For i = 0 To k
If i < 9 Then
w.WriteLine i+1 & " " & arrGroupName(i)
ElseIf i < 99 Then
w.WriteLine i+1 & " " & arrGroupName(i)
Else
w.WriteLine i+1 & " " & arrGroupName(i)
End If
Next
Else
w.WriteLine "This account has no group memberships"
End If
End If
' Clean up.
adoConnection.Close
Set objRootDSE = Nothing
Set adoConnection = Nothing
Set adoCommand = Nothing
Set adoRecordset = Nothing
Set objDC = Nothing
Set objDate = Nothing
Set objIDList = Nothing
Set objLogonList = Nothing
Set objShell = Nothing
set objRootDSE = nothing
wScript.Quit
Can you give this a try please.
Const ForAppending = 8
Const ADS_SCOPE_SUBTREE = 2
Const ADS_UF_ACCOUNTDISABLE = &H02
Const ADS_GROUP_TYPE_GLOBAL_GROUP = &h2
Const ADS_GROUP_TYPE_LOCAL_GROUP = &h4
Const ADS_GROUP_TYPE_UNIVERSAL_GROUP = &h8
Const ADS_GROUP_TYPE_SECURITY_ENABLED = &h80000000
strLogFile = "manage-rougue-users-log.csv"
strSuspectAccountOU = "OU=Suspect Account"
strQueryName = InputBox("Enter the user name.","Manage Suspect Accounts","username")
if strQueryName = "" then
wscript.quit
end if
Set objFSO = CreateObject("Scripting.FileSystemObject")
If NOT objFSO.FileExists(strLogFile) Then
Set objFile = objFSO.OpenTextFile(strLogFile, 8, True, 0)
objFile.WriteLine("""DATE"",""USER"",""LAST LOGIN"",""GROUP MEMBERSHIP""")
else
Set objFile = objFSO.OpenTextFile (strLogFile, ForAppending, True)
End If
Set objRootDSE = GetObject("LDAP://RootDSE")
strDomain = objRootDSE.Get("DefaultNamingContext")
Set objConnection = CreateObject("ADODB.Connection")
Set objCommand = CreateObject("ADODB.Command")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
Set objCommand.ActiveConnection = objConnection
objCommand.Properties("Page Size") = 1000
objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE
objCommand.CommandText = "SELECT distinguishedName FROM 'LDAP://" & strDomain & "' WHERE objectCategory='user' AND sAMAccountName='" & strQueryName & "'"
Set objRecordSet = objCommand.Execute
objRecordSet.MoveFirst
Do Until objRecordSet.EOF
strUser = objRecordSet.Fields("distinguishedName").Value
strLastLogin = GetLastLogonTime(strUser)
strGroupMembership = GetGroupMembership(strUser)
DisableAccount(strUser)
MoveToNewOU strUser,strSuspectAccountOU,strDomain
ArchiveMailbox(strQueryName)
objFile.WriteLine("""" & now() & """,""" & strUser & """,""" & strLastLogin & """,""" & strGroupMembership & """")
objRecordSet.MoveNext
Loop
objFile.Close
Function GetLastLogonTime(DN)
set objUser = GetObject("LDAP://" & DN)
Set objLastLogon = objUser.Get("lastLogon")
intLastLogonTime = objLastLogon.HighPart * (2^32) + objLastLogon.LowPart
intLastLogonTime = intLastLogonTime / (60 * 10000000)
intLastLogonTime = intLastLogonTime / 1440
GetLastLogonTime = intLastLogonTime + #1/1/1601#
End Function
Function GetGroupMembership(DN)
Set objUser = GetObject("LDAP://" & DN)
objmemberOf = objUser.GetEx("memberOf")
i = 0
ReDim arrGroupName(UBound(objmemberOf))
For Each objGroup in objmemberOf
Set objGroup = GetObject("LDAP://" & objGroup)
intGroupType = objGroup.GroupType
If intGroupType AND ADS_GROUP_TYPE_SECURITY_ENABLED Then
arrGroupName(i) = "[S] " & objGroup.sAMAccountName
Else
arrGroupName(i) = "[D] " & objGroup.sAMAccountName
End If
i = i + 1
Next
For i = UBound(arrGroupName) - 1 To 0 Step -1
For k= 0 to i
If arrGroupName(k)>arrGroupName(k+1) then
temp=arrGroupName(k+1)
arrGroupName(k+1)=arrGroupName(k)
arrGroupName(k)=temp
End if
Next
Next
GetGroupMembership = join(arrGroupName,", ")
End Function
Sub DisableAccount(DN)
set objUser = GetObject("LDAP://" & DN)
intUAC = objUser.Get("userAccountControl")
if (intUAC AND ADS_UF_ACCOUNTDISABLE) = 0 Then
objUser.put "userAccountControl", intUAC XOR ADS_UF_ACCOUNTDISABLE
objUser.setinfo
end if
End Sub
Sub MoveToNewOU(DN,OU,DOMAIN)
set objUser = GetObject("LDAP://" & DN)
Set objNewOU = GetObject("LDAP://" & OU & "," & DOMAIN)
objNewOU.MoveHere objUser.ADsPath, vbNullString
End SUb
Sub ArchiveMailbox(Username)
'launch powershell and execute something like this...
'Get-Mailbox "" & Username & "" | Export-Mailbox -PSTFolderPath "\\server\share\folder$($_.SAMAccountName)"
End Sub
ASKER
This works great, i have one thing id like to see if possible and thats on the log file on the group memberships. Can we make it so that each group is in a new cell in the same column, just so its a little easier to read?
Any ideas on where I should start looking to get the export mailbox function to work?
Any ideas on where I should start looking to get the export mailbox function to work?
ASKER
Any update on this rejoinder?
Sorry for the long delay - I have been busy on another project. I will have time tomorrow to look at this.
Here is script #2
Like above, you will need to edit the logfile name and location as well as the OU where the script will find the user accounts.
To see what accounts the script is looking at there is a section commented out;
'wscript.echo "30 days ago: " & dateAdd("d",-30,dtToday) & vbCRLF &_
' "When changed: " & dtWhenChanged & vbCRLF &_
' "User name: " & strUser & vbCRLF &_
' "Home drive: " & strhomeDirectory & vbCRLF &_
' "Terminal Profile: " & strTerminalServicesProfile Path & vbCRLF &_
' "Account disabled: " & objUser.AccountDisabled
uncomment that block to see what account the script is currently working on.
I really don't know how to archive the mailboxes - we are not on Exch 2007 yet.
Here are a few links which talk about the PS script needed...
http://msexchangeteam.com/archive/2007/04/13/437745.aspx
http://blogs.technet.com/sbs/archive/2009/01/13/sbs-2008-how-to-export-and-import-mailboxes-to-and-from-pst.aspx
Like above, you will need to edit the logfile name and location as well as the OU where the script will find the user accounts.
To see what accounts the script is looking at there is a section commented out;
'wscript.echo "30 days ago: " & dateAdd("d",-30,dtToday) & vbCRLF &_
' "When changed: " & dtWhenChanged & vbCRLF &_
' "User name: " & strUser & vbCRLF &_
' "Home drive: " & strhomeDirectory & vbCRLF &_
' "Terminal Profile: " & strTerminalServicesProfile
' "Account disabled: " & objUser.AccountDisabled
uncomment that block to see what account the script is currently working on.
I really don't know how to archive the mailboxes - we are not on Exch 2007 yet.
Here are a few links which talk about the PS script needed...
http://msexchangeteam.com/archive/2007/04/13/437745.aspx
http://blogs.technet.com/sbs/archive/2009/01/13/sbs-2008-how-to-export-and-import-mailboxes-to-and-from-pst.aspx
Const ForAppending = 8
on error resume next
strLogFile = "manage-rougue-users-delete-log.csv"
strSuspectAccountOU = "OU=Suspect Account"
Set objFSO = CreateObject("Scripting.FileSystemObject")
If NOT objFSO.FileExists(strLogFile) Then
Set objFile = objFSO.OpenTextFile(strLogFile, 8, True, 0)
objFile.WriteLine("""DATE"",""USER"",""Home Directory"",""Terminal Services Profile Path""")
else
Set objFile = objFSO.OpenTextFile (strLogFile, ForAppending, True)
End If
Set objRootDSE = GetObject("LDAP://RootDSE")
strDNSDomain = objRootDSE.Get("DefaultNamingContext")
strObject = "user"
strSuspectAccountOU = "OU=Suspect Account"
strContainer = strSuspectAccountOU & "," & strDNSDomain
set objOU = GetObject("LDAP://" & strContainer)
For each objUser in objOU
strObjectDN = objUser.distinguishedName
set objEntry = GetObject("LDAP://" & strObjectDN)
objEntry.GetInfo
dtToday = now()
dtWhenChanged = formatdatetime(objEntry.whenChanged,2)
strUser = objEntry.distinguishedName
strhomeDirectory = objEntry.homeDirectory
strTerminalServicesProfilePath = objEntry.TerminalServicesProfilePath
'wscript.echo "30 days ago: " & dateAdd("d",-30,dtToday) & vbCRLF &_
' "When changed: " & dtWhenChanged & vbCRLF &_
' "User name: " & strUser & vbCRLF &_
' "Home drive: " & strhomeDirectory & vbCRLF &_
' "Terminal Profile: " & strTerminalServicesProfilePath & vbCRLF &_
' "Account disabled: " & objUser.AccountDisabled
'Test if older than 30 days and disabled
if dtWhenChanged <= dateAdd("d",-30,dtToday) AND objUser.AccountDisabled then
set FileSystemObj = CreateObject ("Scripting.FileSystemObject")
'delete home folder
if strhomeDirectory <> "" then
set Folder = FileSystemObj.GetFolder(strhomeDirectory)
Folder.Delete
end if
'delete terminal server profile
if strTerminalServicesProfilePath <> "" then
set Folder = FileSystemObj.GetFolder(strTerminalServicesProfilePath)
Folder.Delete
end if
'delete account
strCN = "CN=" & objUser.cn
objOU.delete strObject, strCN
'log entry
objFile.WriteLine("""" & now() & """,""" & strUser & """,""" & strhomeDirectory & """,""" & strTerminalServicesProfilePath & """")
end if
next
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Regarding point 1 on script 2, AD doesn't keep track of when accounts are disabled. A workaround is to either modify the description field of the user to become the date the account was disabled or to compare the last modified date of the disabled account. From my point of view, the last modified date field is a good field to compare because there may be times that you want to go back in and change something little in the AD account and you might not want to blow away off their data. Your choice, please post a comment when you are able.
For now, play around with script 1 to see that the user account is being moved accordingly and the log file is capturing what you want. Before you begin however, please edit the variables with the correct information;
strLogFile = "C:\manage-rougue-users-lo
strSuspectAccountOU = "OU=Suspect Account" <-- This is the path to the OU. If you have to traverse through a few OU's to get there the variable would look something like this;
strSuspectAccountOU = "OU=Suspect Account,OU=Test,OU=My Accounts"
During the logging you wanted to keep track of the last login date. The field is a bit of an odd ball because it isn't replicated throughout the domain as other fields are. You may find that the field can be as much as 45 days out of sync. Anyway, dont stake your career on the dates being returned by this field think of it more as a nice to have.
Open in new window