Script Repository

Set account expiration date via meeting in Exchange

February 24, 2021

The script sets user account expiration date via meetings in Exchange. To use the script, you need to appoint meetings for a certain mailbox. The account you want to set expiration date for must be specified in the meeting subject. The desired expiration date must be the start date of the meeting. If an error occurs, the script sends an email to the meeting appointer with error details.

To be able to use the script, download and install Microsoft Exchange Web Services Managed API on the computer where Adaxes service runs.

The meetings must be appointed for the mailbox specified in the Run As section of the Run a program or PowerShell script action used to run the script.

To use the script in your environment, create a scheduled task configured for the Domain-DNS object type. Include any of the managed AD domains into the Activity Scope of the task.


  • $exchangeWebServiceDllPath - Specifies the full path to the Microsoft Exchange Web Services dll module.
  • $exchangeServer - Specifies the fully qualified domain name (FQDN) of your Exchange Server. If you specify $NULL for this parameter, the connection will be made to Exchnage Online.
  • $subjectSearchPhrase - Specifies the search phrase in the appointment notification message subject. The script will search meetings by this phrase. For example, if you specify User leaving: for $subjectSearchPhrase and want to expire the account of user John Doe, the meeting subject can be User leaving: John Doe.
  • $mailboxMailAddress - Specifies the email address of the mailbox where meetings will be appointed.
  • $errorEmailSubject - Specifies the subject of emails with error messages.
  • $errorEmailHeader - Specifies the header of emails with error messages.
  • $errorEmailFooter - Specifies the footer of emails with error messages.
See Also: Appoint meeting on user account expiration day.
Edit Remove
# Exchange settings
$exchangeWebServiceDllPath = "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll" # TODO: modify me
$exchangeServer = "" # TODO: modify me. If $NULL, connect to Exchange Online
$subjectSearchPhrase = "User leaving:" # TODO: modify me
$mailboxMailAddress = "" # TODO: modify me

# Email settings
$errorEmailSubject = "Failed Account Expiration Tasks" # TODO: modify me
$errorEmailHeader = "<h2><b>Errors that occurred when processing your requests to expire user accounts</b></h2>" # TODO: modify me
$errorEmailFooter = "<hr /><p><i>Please do not reply to this e-mail, it has been sent to you for notification purposes only.</i></p>" # TODO: modify me

Import-Module $exchangeWebServiceDllPath

function SearchObjects($filter, $propertiesToLoad)
    $searcher = $Context.BindToObject("Adaxes://rootDSE")
    $searcher.SearchFilter = $filter
    $searcher.SearchScope = "ADS_SCOPE_SUBTREE"
    $searcher.PageSize = 500
    $searcher.ReferralChasing = "ADS_CHASE_REFERRALS_NEVER"
    $searcher.VirtualRoot = $True
        $searchResultIterator = $searcher.ExecuteSearch()
        $searchResults = $searchResultIterator.FetchAll()
        return ,$searchResults
        # Release resources
        if ($searchResultIterator){ $searchResultIterator.Dispose() }

function AddErrorToReport ($message, $initiatorMail, $startDate, $errorReports)
    if (-not($errorReports.ContainsKey($initiatorMail)))
        $errorReports.Add($initiatorMail, (New-Object "System.Text.StringBuilder"))
    $report = $errorReports[$initiatorMail]

if ($exchangeServer -eq $NULL)
    # Connect to Exchange Online via the Exchange Web Services API
    $exchangeWebService = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService
    $exchangeWebService.Url = ""
    $token = $Context.CloudServices.GetAzureAuthAccessToken("")
    $OAuthCredentials = New-Object Microsoft.Exchange.WebServices.Data.OAuthCredentials($token)
    $exchangeWebService.Credentials = $OAuthCredentials
    $exchangeWebService.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $mailboxMailAddress)
    # Connect to Exchange on-premises via the Exchange Web Services API
    $exchangeWebService = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010)
    $exchangeWebService.Url = "https://$exchangeServer/ews/exchange.asmx"

# Build filter to find the necessary mail message
$searchFilter = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubstring([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::Subject, $subjectSearchPhrase)
$searchFilterCollection = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::And)

# Bind to the Inbox folder
$folderID = New-Object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Calendar, $mailboxMailAddress)
$inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($exchangeWebService, $folderID)

# Set order
$itemView = New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)
$itemView.OrderBy.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived, [Microsoft.Exchange.WebServices.Data.SortDirection]::Descending)

$mailSearchResults = $inbox.FindItems($searchFilterCollection, $itemView)

if ($mailSearchResults.Items.Count -eq 0)
    return # No messages to process

# Process meetings
$users = @{}
$errorReports = @{}
foreach ($item in $mailSearchResults.Items)
    $userFullName = $item.Subject.Replace($subjectSearchPhrase, "").Trim()
    # Find user
    $searchResults = SearchObjects "(&(sAMAccountType=805306368)(cn=$userFullName))" @()

    if ($searchResults.Length -eq 0)
        $message = "User '$userFullName' not found"
        $Context.LogMessage($message, "Warning")
        AddErrorToReport $message $item.Organizer.Address $item.Start $errorReports
    elseif ($searchResults.Length -gt 1)
        $message = "Found more than one user with name '$userFullName'"
        $Context.LogMessage($message, "Warning")
        AddErrorToReport $message $item.Organizer.Address $item.Start $errorReports
    if (-not($users.ContainsKey($userFullName)))
        $users.Add($userFullName, @{
            "SearchResult" = $searchResults[0]
            "ExpirationDate" = @($item.Start)
    elseif ($users[$userFullName].ExpirationDate -lt $item.Start)
        $users[$userFullName].ExpirationDate = $item.Start

# Update users
foreach ($userFullName in $users.Keys)
    # Bind to user
    $userInfo = $users[$userFullName]
    $user = $Context.BindToObject($userInfo.SearchResult.AdsPath)
    # Set account expiration date
    $user.Put("accountExpires", $userInfo.ExpirationDate)

# Send error reports to failed meeting request owners
foreach ($initiatorMail in $errorReports.Keys)
    # Build html
    $html = $errorEmailHeader + "<ul>" + $errorReports[$initiatorMail].ToString() + "</ul>" + $errorEmailFooter
    # Send mail
    $Context.SendMail($initiatorMail, $errorEmailSubject, $NULL, $html)

Comments ( 0 )
No results found.
Leave a comment