Link to home
Start Free TrialLog in
Avatar of amyassein
amyassein

asked on

How can i disable inactive user accounts and move them to specified OU?

Hello,

I need a vbscript that can disable inactive AD user accounts that hasn't logged on for more than 90 days and move them to a specified OU after disabling them.

Appreciate your fast response.

Yassein
Avatar of Mike Kline
Mike Kline
Flag of United States of America image

Yassein,
Does this have to be done using vbscript?   The reason I ask is because there is a great tool called old computer by MVP Joe Richards.
...don't worry it also works on users
http://www.joeware.net/freetools/tools/oldcmp/index.htm
To look at a quick report run
oldcmp -report -users -llts
that -llts is for lastlogontimestamp which is available in a w2k3 domain which it looks like you are.
It can disable and move too
Thanks
Mike

Because it's sometimes useful to know how... VbScript version below :)

Chris
' Inactivity period
 
Const PERIOD_TO_REMOVE = 90
 
' Create a connection to the OU which holds disabled accounts
 
Set objOU = GetObject("LDAP://OU=Disabled Users,DC=yourdomain,DC=com")
 
' Create a value to represent the date
 
Dim dblInt8 : dblInt8 = CDbl(DateDiff("s", CDate("01/01/1601 00:00:00"), Now - PERIOD_TO_REMOVE))
 
' Create a filter to return all user accounts which have been inactive for PERIOD_TO_REMOVE and
' and are still enabled.
 
Dim strLdapFilter
strLdapFilter = "(&(objectClass=user)(objectCategory=person)(lastLogonTimeStamp<=" & _
  CStr(dblInt8) & "0000000)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
 
' Search the current AD domain
 
Dim objConnection : Set objConnection = CreateObject("ADODB.Connection")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
 
Dim objCommand : Set objCommand = Createobject("ADODB.Command")
objCommand.ActiveConnection = objConnection
objCommand.Properties("Page Size") = 1000
 
Dim objRootDSE : Set objRootDSE = GetObject("LDAP://RootDSE")
 
objCommand.CommandText = "<LDAP://" & objRootDSE.Get("defaultNamingContext") & ">;" & _
  strLdapFilter & ";distinguishedName,userAccountControl;subtree"
 
Dim objRecordSet : Set objRecordSet = objCommand.Execute
 
Do Until objRecordSet.EOF
  Set objUser = GetObject("LDAP://" & objRecordSet.Fields("distinguishedName").Value)
 
  ' Debugging / Testing
  WScript.Echo "Disable: " & objRecordSet.Fields("distinguishedName").Value
 
  ' Disable this account - Note: these are commented out to allow testing prior to activation
  ' objUser.AccountDisabled = True
  ' objUser.SetInfo
 
  Set objUser = Nothing
 
  ' Debugging / Testing
  WScript.Echo "Moving: " & objRecrodSet.Fields("distinguishedName").Value  
 
  ' Move the account to a new OU - Note: this is commented out to allow testing prior to activation
 
  ' objOU.MoveHere "LDAP://" & objRecordSet.Fields("distinguishedName").Value, VbNullString
 
  objRecordSet.MoveNext
Loop

Open in new window

Avatar of amyassein
amyassein

ASKER

Chris,Mkline

Thanks for the quick response and i appreciate it.

Chris,

I have a question, Which date shall i insert in this function ? CDate("01/01/1601 00:00:00" ..... Sorry but i don't do scripting too much.

Thank You again.

The lastLogonTimeStamp attribute is the number of 100-nanosecond intervals since 01/01/1601 00:00:00. That means it has to be calculated to fit into the script.

The script does that by taking the value you set for "PERIOD_TO_REMOVE" and figuring out the difference between the date above (01/01/1601) and todays date (Now) minus the specified number of days.

In short, you only need to set a value for "PERIOD_TO_REMOVE", it's 90 days at the moment.

Chris
Chris,

I am very sorry that i didn't mention this from the beginning but instead of searching the whole AD domain , i need only to look for inactive accounts in "People" OU since all user accounts are placed inside one OU.

My bad :)

Please can you help in this?

Thanks

Sure :)

Fill in the value for SEARCH_ROOT here.

Chris
' Inactivity period
 
Const PERIOD_TO_REMOVE = 90
 
' Search base - The AD path you wish to find users in (and beneath)
 
Const SEARCH_ROOT = "OU=People,DC=yourdomain,DC=com"
 
' Create a connection to the OU which holds disabled accounts
 
Set objOU = GetObject("LDAP://OU=Disabled Users,DC=yourdomain,DC=com")
 
' Create a value to represent the date
 
Dim dblInt8 : dblInt8 = CDbl(DateDiff("s", CDate("01/01/1601 00:00:00"), Now - PERIOD_TO_REMOVE))
 
' Create a filter to return all user accounts which have been inactive for PERIOD_TO_REMOVE and
' and are still enabled.
 
Dim strLdapFilter
strLdapFilter = "(&(objectClass=user)(objectCategory=person)(lastLogonTimeStamp<=" & _
  CStr(dblInt8) & "0000000)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
 
' Search the current AD domain
 
Dim objConnection : Set objConnection = CreateObject("ADODB.Connection")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
 
Dim objCommand : Set objCommand = Createobject("ADODB.Command")
objCommand.ActiveConnection = objConnection
objCommand.Properties("Page Size") = 1000
 
objCommand.CommandText = "<LDAP://" & SEARCH_ROOT & ">;" & _
  strLdapFilter & ";distinguishedName,userAccountControl;subtree"
 
Dim objRecordSet : Set objRecordSet = objCommand.Execute
 
Do Until objRecordSet.EOF
  Set objUser = GetObject("LDAP://" & objRecordSet.Fields("distinguishedName").Value)
 
  ' Debugging / Testing
  WScript.Echo "Disable: " & objRecordSet.Fields("distinguishedName").Value
 
  ' Disable this account - Note: these are commented out to allow testing prior to activation
  ' objUser.AccountDisabled = True
  ' objUser.SetInfo
 
  Set objUser = Nothing
 
  ' Debugging / Testing
  WScript.Echo "Moving: " & objRecrodSet.Fields("distinguishedName").Value  
 
  ' Move the account to a new OU - Note: this is commented out to allow testing prior to activation
 
  ' objOU.MoveHere "LDAP://" & objRecordSet.Fields("distinguishedName").Value, VbNullString
 
  objRecordSet.MoveNext
Loop

Open in new window

Just to follow up with oldcmp, you can also change the base where it searches
...for that you use the -b switch then your OU
so
oldcmp -b "DN of your OU here" -report -users -llts
That report gives you 90 days but you can change the age by using the -age switch.  If you want to check for 120 days for example
oldcmp -b "DN of your OU here" -report -age 120 -users -llts  
Thanks
Mike
Chris,

Thank you so much for the valuable information. Tomorrow i will test it and will update you.

Mkline,

Fantastic follow up !!! i promise i will give it a try later. The reason why i love scripting because it's flexible, less CPU consuming and give you results in terms of visible outputs such as MsgBox.

Thanks again all.

We should have had a bit of PowerShell as well, just to round things off nicely :)

Chris
One thing that I also like about oldcmp is that you can send those html reports to the managers.  Can make you look good.
...even managers can understand them :)
Chris,

I tried your final code but it generated a run time error "Object required "objRecordSet".

Any Suggestions?

That confused me for a while, but there's a typo, it complains about "objRecrodSet" being required instead :)

Fixed here.

Chris
' Inactivity period
 
Const PERIOD_TO_REMOVE = 90
 
' Search base - The AD path you wish to find users in (and beneath)
 
Const SEARCH_ROOT = "OU=People,DC=yourdomain,DC=com"
 
' Create a connection to the OU which holds disabled accounts
 
Set objOU = GetObject("LDAP://OU=Disabled Users,DC=yourdomain,DC=com")
 
' Create a value to represent the date
 
Dim dblInt8 : dblInt8 = CDbl(DateDiff("s", CDate("01/01/1601 00:00:00"), Now - PERIOD_TO_REMOVE))
 
' Create a filter to return all user accounts which have been inactive for PERIOD_TO_REMOVE and
' and are still enabled.
 
Dim strLdapFilter
strLdapFilter = "(&(objectClass=user)(objectCategory=person)(lastLogonTimeStamp<=" & _
  CStr(dblInt8) & "0000000)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
 
' Search the current AD domain
 
Dim objConnection : Set objConnection = CreateObject("ADODB.Connection")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
 
Dim objCommand : Set objCommand = Createobject("ADODB.Command")
objCommand.ActiveConnection = objConnection
objCommand.Properties("Page Size") = 1000
 
objCommand.CommandText = "<LDAP://" & SEARCH_ROOT & ">;" & _
  strLdapFilter & ";distinguishedName,userAccountControl;subtree"
 
Dim objRecordSet : Set objRecordSet = objCommand.Execute
 
Do Until objRecordSet.EOF
  Set objUser = GetObject("LDAP://" & objRecordSet.Fields("distinguishedName").Value)
 
  ' Debugging / Testing
  WScript.Echo "Disable: " & objRecordSet.Fields("distinguishedName").Value
 
  ' Disable this account - Note: these are commented out to allow testing prior to activation
  ' objUser.AccountDisabled = True
  ' objUser.SetInfo
 
  Set objUser = Nothing
 
  ' Debugging / Testing
  WScript.Echo "Moving: " & objRecordSet.Fields("distinguishedName").Value  
 
  ' Move the account to a new OU - Note: this is commented out to allow testing prior to activation
 
  ' objOU.MoveHere "LDAP://" & objRecordSet.Fields("distinguishedName").Value, VbNullString
 
  objRecordSet.MoveNext
Loop

Open in new window

Chris,

Thank you so much for fixing that error. Well, when i run the script, it works fine by showing pop up messages for disabling and moving the intended accounts but the thing is, when i checked the accounts, i noticed that they still not disabled and not moved to the specified OU. I am sure that i mentioned the name of the destination OU correctly (where all disabled accounts will be placed). Actually, i got many DCs in my environment so i waited for 30 minutes which is the DC replication interval because i doubted the script ran against a specified DC not all DCs.

Anyways, how can i tell the script to run against all the DCs in my environment?

Thank you again man.

Hey :)

It's because the lines that make the changes are commented out. I wanted to give you a chance to test it before it starting actually making changes.

You should see two sections for this:

  ' Disable this account - Note: these are commented out to allow testing prior to activation
  ' objUser.AccountDisabled = True
  ' objUser.SetInfo

And:

  ' Move the account to a new OU - Note: this is commented out to allow testing prior to activation

  ' objOU.MoveHere "LDAP://" & objRecordSet.Fields("distinguishedName").Value, VbNullString

Removing the ' will activate them (don't remove it from the actual comment though :)).

Chris
Chris,

Yeah Yeah, now it's working man after removing these things  :)

But, there is still minor issue. Moving to the new OU is not performed.

It's moving to the same OU where i perform the search. For example, I perform the search in People OU and my destination folder is Decommissioned OU. After i configure these OUs in the script, the accoun is disabled properly but not moving to the destination OU. It also says "The server is unwilling to process the request"

Any ideas my friend?
Chris,

Ok let me clarify things more.

After running the script, it says:

"Disable: CN=Smith,OU=People,DC=Domain,DC=com"

I click "Ok", then the second message appears:

"Moving: CN=Smith,OU=People,DC=Domain,DC=com"

Well, it should say:

"Moving: CN=Smith,OU=Decommissioned,DC=Domain,DC=com"

The this runtime error appears:

"The server is unwilling to process the request"

Therefore, the result is that the account is disabled successfully but remains in the source OU. I checked my permissions in the destination OU and i can move to and from this OU.

Hope this was clear.

Hey sorry dude, bit distracted.

> Well, it should say:

No, it shouldn't. It's telling you which object is being moved, not what the object will be after the move. That's only there for debugging.

> "The server is unwilling to process the request"

There aren't any other accounts in the target OU with the same name are there?

Chris
Chris,

You know what! you are right! .... your script now is working like charm. I was access denied on the target OU when some administrator was doing maintenance for the OU permissions.

I know i ask too many questions but i have one last request which i beleive it's very easy to you.
I need the script to silently do this operation without poping up messages to me because it's headache to keep clicking "OK" after each message prompt. I think you need to remove those "Echo" lines. i don't want to do it as i am afraid i ruin something.

Many Thanks.
ASKER CERTIFIED SOLUTION
Avatar of Chris Dent
Chris Dent
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial