PowerShell: Clean up AD Computer Accounts

Update (2009-11-03): I have posted a newer version of this script. Please visit this link for information.

—————

I recently wrote a script to clean up workstation accounts in our Active Directory domain. It’s not perfect, but it was a good learning experience, as I found out there are some gritty details when working with Integer8 values. I plan on adding more features in the future, including logging results of the script to an Excel document, for easy readability. If you have any suggestions, please send them my way; I may or may not have time to implement them, but am always open to constructive

  • http://www.systemcentercentral.com Pete Zerger

    Trevor, looks like a good start, but prompts some comments . We use OLDCMP in a scheduled capacity (http://www.joeware.net/freetools/tools/oldcmp/index.htm) for it’s reporting and safety features. How does your script compare from a reporting perspective in advance of disabling?

    OLDCMP provides ability to report, then disable, then later delete. As a consultant, I see many one-off scenarios (like with SANs that require a windows acct that appears stale) where an unintentional delete would be disastrous.

    Would be interested to hear how the two compare.

    • pcgeek86

      Pete,

      I have heard of oldcmp, but haven’t personally used it before, so I can’t speak to the comparison. I wrote the PowerShell script so that I have full control over what’s going on, and can implement reporting in the way that I want it (e-mail, Excel, etc.).

      Regarding the issue of accidental deletion, I have specified an LDAP filter that specifically targets accounts with Windows operating systems. Similar to the SANs you mentioned, we have a handful of Mac OS X devices that authenticate to Active Directory, and do not conform to the same workstation password requirements that Windows clients do. Because I used the LDAP search filter, anything that isn’t a Windows client will not be targeted. Could you do me a favor and check out these SAN accounts and tell me what value the operatingSystem attribute has in AD?

      Thanks so much for your feedback! I really appreciate it.

      -Trevor

  • http://www.systemcentercentral.com Pete Zerger

    I’ll get that and post for you…do check out what OLDCMP can do…it sets the bar pretty high and if you could match that in PoSh, would be way cool.

    • pcgeek86

      Pete,

      I will definitely check out oldcmp and see how I can improve my PowerShell script to meet similar functionality. Do you think that logging to Excel would be appropriate? I know other formats like CSV, HTML, and so on, are a little more platform agnostic, but what do you think?

      Thanks for getting me the attribute info!

      -Trevor

  • Jeroen Budding

    I made some small changes to the script. Only want to Disabled pc’s from my OU.
    So i changed the script as below. Now it only doesn’t disable and move any pc.
    What did i do wrong?

    Jeroen

    ${TargetDN} = ‘OU=Baarn,OU=IPS,OU=Invensys Business Units,DC=corp,DC=com’
    ${DisabledDn} =

    • pcgeek86

      Hello,

      The Main function near the bottom of the script is what’s handling the logic for disabling or deleting accounts. In your modified copy, all that’s happening is functions are getting defined, but never actually called.

      1. Copy the Main function back into your script (including the call to it at the very bottom)
      2. Delete the call to “DeleteDisableAccounts” inside Main
      3. Change this line : DisableOldAccounts ([adsi]

  • Jeroen Budding

    Hi Trevor,

    Thanks for looking at my problem only still the script doesn’t work.

    ${TargetDN} = ‘OU=Baarn,OU=IPS,OU=Invensys Business Units,DC=corp,DC=com’
    ${DisabledDn} =

    • pcgeek86

      It looks like you are still missing the call to the Main() function. It’s being defined at the bottom now, but you still have to call it, by saying simply:

      Main
      • Jeroen Budding

        Runs now only i get the following error messages.

        11/6/2009 12:36:55 PM INFO: Beginning workstation account cleanup script
        11/6/2009 12:36:55 PM INFO: Found computer accounts to evaluate for disablement
        Cannot index into a null array.
        At line:1 char:24
        + ${Computer}.Properties[ <<<< 'pwdlastset']
        + CategoryInfo : InvalidOperation: (pwdlastset:String) [], RuntimeException
        + FullyQualifiedErrorId : NullArray

        Cannot index into a null array.
        At line:1 char:22
        + $Computer.Properties[ <<<< 'cn']
        + CategoryInfo : InvalidOperation: (cn:String) [], RuntimeException
        + FullyQualifiedErrorId : NullArray

        11/6/2009 12:36:55 PM WARNING: age is 149328. Account will be disabled
        Cannot index into a null array.
        At D:\INVSDATA\Powershell\Scripts\Disable Computer Account.ps1:18 char:37
        + DisableAccount $Computer.Properties[ <<<< 'distinguishedname']
        + CategoryInfo : InvalidOperation: (distinguishedname:String) [], RuntimeException
        + FullyQualifiedErrorId : NullArray

        11/6/2009 12:36:56 PM INFO: Completed workstation account cleanup script

        ${TargetDN} = 'OU=Baarn,OU=IPS,OU=Invensys Business Units,DC=corp,DC=com'
        ${DisabledDn} =

  • Jeroen Budding

    Hi Sorry was already in the script only forgot to copy into the script.

    Still get the following errors.

    11/6/2009 3:31:57 PM INFO: Beginning workstation account cleanup script
    11/6/2009 3:31:58 PM INFO: Found computer accounts to evaluate for disablement
    Cannot index into a null array.
    At line:1 char:24
    + ${Computer}.Properties[ <<<< 'pwdlastset']
    + CategoryInfo : InvalidOperation: (pwdlastset:String) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

    Cannot index into a null array.
    At line:1 char:22
    + $Computer.Properties[ <<<< 'cn']
    + CategoryInfo : InvalidOperation: (cn:String) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

    11/6/2009 3:31:58 PM WARNING: age is 149328. Account will be disabled
    Cannot index into a null array.
    At D:\INVSDATA\Powershell\Scripts\Disable Computer Account.ps1:18 char:37
    + DisableAccount $Computer.Properties[ <<<< 'distinguishedname']
    + CategoryInfo : InvalidOperation: (distinguishedname:String) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

    11/6/2009 3:31:59 PM INFO: Completed workstation account cleanup script

    ${TargetDN} = 'OU=Baarn,OU=IPS,OU=Invensys Business Units,DC=corp,DC=com'
    ${DisabledDn} =

    • pcgeek86

      Hello,

      Can you tell me:

        Client OS / service pack
        PowerShell version
        AD forest & domain functional level
        Domain controller OS?

      I’ve only tested the script with Windows 7 Release Candidate / PowerShell v2.0 against a Windows 2003 functional level forest & domain. I would imagine that the ADSI interfaces are all the same, but it sounds like it isn’t able to retrieve the pwdlastset or cn attributes.

      Cheers,
      -Trevor

  • Jeroen Budding

    Hi Trevor,

    This is how everything is setup. Hopefully you can find anything otherwish is need to do it another why

    Windows XP SP3
    Powershell V2
    Native mode
    Server 2008 AD

    Thanks for the help you already gave me,

    Jeroen

    • pcgeek86

      Jeroen,

      Sorry for the late reply … it keeps coming back to me, then slipping my mind, to get back to you.

      Well as far as I know, it sounds like your setup is OK. You’re running the release version of PowerShell 2.0 I hope (not a CTP or RC version)? For the next troubleshooting steps, I think it would be best to get ADSI Edit (are you familiar with this tool already?) and ensure that you are able to see the Active Directory attributes on the objects that the script is attempting to read. Let me know if you need help or directions on how to do this. The other thing that concerns me is that the one line says “Found computer accounts to evaluate for disablement.” It -should- read, for example, something like “Found 502 computer accounts to evaluate for disablement.” Because that number is missing, it leads me to believe that something is going wrong in the DirectorySearcher that looks for computer accounts under the “$TargetDn”.

      Would you be willing to set up a Live Meeting session sometime so we can step through and troubleshoot what’s going on with this code? I’m curious myself to know what I may have overlooked during its development.

      Cheers,
      -Trevor