Schedule user account closure with PowerShell

One of my many tasks as a system administrator is to close user accounts after they have left their jobs. Not working in HR, I don't need to concern myself with why they are no longer employed. I simply need to process their account closures in accordance with our own policies.

Whether the user had a company mobile device that needs to be wiped, passwords reset, documents moved, or email redirected, there will be actions you'll need to take in your organization, and more importantly, a time at which to carry out the actions.

For a small team of administrators, this can pose a potential problem. If you work for an external organization providing IT services, you may not have the ability to get in and follow all the steps in your account closure procedure because of other commitments. I am not here to judge; I am just here to explain how I do things.

My procedure uses two parts. One is built into a larger tool as a function, which I call ScheduleLeaver. The other is a standalone script called ProcessLeaver.

For the purpose of this article, I am splitting ScheduleLeaver into its own script.

First off, we need to gather some information about the user who is leaving and store that information for use at our scheduled time.

This section of code allows us to specify a filename to save our scheduled leavers in and if the file does not exist, create it along with the correct column headers.

Next, we use a series of "do" actions to collect some basic information about the user account.

Import-Module ActiveDirectory

Here we have imported the ActiveDirectory and Exchange PowerShell tools and prompted for the username of the account we wish to close, and then checked for that username in Active Directory. If it is found, we move on; if not, we prompt to enter the username again.

Our next section is similar, except it concerns the account closure date.

In this section, we prompt for a date to close the account and loop around if the date is not accepted.

The next code block is identical, except we prompt for the time to close the account.

The next block is slightly more complex, as we ask if we need to forward the user's email, and confirm we have entered a valid recipient address.

Next, we add on a small summary of what we have entered.

We then confirm this information and enter it into our CSV File.

You should end up with a file that looks like this.

ScheduleLeaver CSV

ScheduleLeaver CSV

In the next section, we build the second script, ProcessLeaver. This will actually take the required actions.

Our first task is to import our CSV File. We're then going to empty the contents of the file. You will see why in a moment.

At this point, the script stores everything in our CSV file in $scheduleLeaver and empties the file itself.

Why do we do that?

The next code block checks whether the current date is before or after the user’s scheduled leave date. If it is after, we can process the account closure. If it is still before the scheduled leave date, we can simply add the user back to the schedule file.

The above section builds a $leaverDate string and compares it to a Get-Date string to determine whether we should process the leaver. If we should not, the script adds the user's information back to the CSV File.

Next, we can move onto the fun part.

What do you want to accomplish with your account closure?

In my example I am keeping things simple, with the following:

  • Reset password
  • Disable account
  • Disable ActiveSync
  • Hide from address list
  • Forward email

You can of course get more creative if you wish. For example, you could add in an email notification to HR to say the account has been closed and include new login information, instructions on how to access the account's old email or documents, and so on. The possibilities are endless.

All we need to do now is save both of these files with a ps1 extension and set up a scheduled task to execute the ProcessLeaver.ps1 file at a given time of the day. For me, this is at 6pm from Monday to Friday.

If you are using Office 365, you can still use this method. You just need to add in the code to connect up to Office 365 PowerShell. After that, your Get-Mailbox commands will run against your Exchange Online instance instead of a local on-premises Exchange organization.

Take a look at the comments below for some suggestions on adding logging and error recovery!

The full script is below.

Join the 4sysops PowerShell group!

Your question was not answered? Ask in the forum!

  1. Andrew Hilborne 4 years ago

    This script is a good idea, but needs work! It is the opposite of robust in the face of failures. For example: you empty the csv file, do the work, then recreate it. If the jobs fails midway, you lose the file for good. You should create a backup of the file first and atomically rename the final version when the job is successful, possibly leaving entries behind when closure hasn't completely succeeded. You should also consider making each step in account closure idempotent: so partial failures can be re-tried at a later time.

    You also really need some logging in there. What happens when part of an account closure doesn't happen, or, worse, some accounts are closed when they shouldn't have been? You need evidence.


  2. Author

    I think you're absolutely right, I don't consider this to be a finished article. More of a good starting point. Does it accomplish the task? Yes. Is it perfect? by no means.

    My aim with PowerShell articles is to show people who may dismiss it as a means to run a command once in a while, to transition into using it every day. I am certainly no PowerShell expert but i think some people can be put off of using it by making things too complex too early. I know it did when i started to use it a few years back.

    I want people to think of new ways of doing things, and take steps to start learning how to implement their own improvements.

    Rename-Item on TechNet


  3. BI 4 years ago

    This is really great, and is something I'm working on myself. One of those projects that I've been meaning to perfect for awhile. *cough* six years *cough

    Hitting some problems with cleaning up group membership, mainly because I don't understand pipelines/filters...

    Need to basically do this -- empty out all group membership except one group.

    Got PS>> Get-ADPrincipalGroupMembership -Identity Some.Person | select Name | Where-Object {$_.Name -ne 'Some Group'}

    but either that is overkill ,or I'm not understanding how to pipe those results to Remove-ADGroup (or Remove-ADPrincipleGroupMembership)


  4. Author

    I must admit i have not used that cmdlet before.

    I just had a quick play and piping the output of the 'get' command doesn't seem that straight forward to me.

    So i would be inclined to do this,



  5. Thank you Robert Pearman, this is helpful even in my small shop.

    get-mailbox -ResultSize 2000 | Measure-Object | Select-Object count



Leave a reply

Your email address will not be published. Required fields are marked *


© 4sysops 2006 - 2020


Please ask IT administration questions in the forums. Any other messages are welcome.


Log in with your credentials


Forgot your details?

Create Account