Public/Start-ChangeEmailAgentActiveDirectoryListener.ps1

<#
.DESCRIPTION
    Starts the ChangeEmailAgent listener. The listener continuously checks for password reset requests and processes them.

.SYNOPSIS
    Starts the ChangeEmailAgent listener.

.EXAMPLE
    Start-ChangeEmailAgentActiveDirectoryListener -Sleep 5
#>

function Start-ChangeEmailAgentActiveDirectoryListener {
    [CmdletBinding()]
    Param(
        # Sleep interval in seconds between checks for new requests
        [Parameter(Mandatory = $false)]
        [ValidateRange(1, 10)]
        [int] $Sleep = 60,

        [Parameter(Mandatory = $false)]
        [scriptblock] $RunBlockAfterChange = $null
    )

    process {
        if (!(get-command Set-ADUser* | ? Name -eq Set-ADUser)) {
            Write-Error -Message "Set-ADUser cmdlet is not available. Please ensure the Active Directory module is installed and imported."
            Write-EventLog -LogName "Application" -Source "ChangeEmailAgent" -EventId 1210 -EntryType Error -Message "Set-ADUser cmdlet is not available. Please ensure the Active Directory module is installed and imported." -ErrorAction Continue
            return
        }

        Write-Verbose "Starting ChangeEmailAgent listener with a sleep interval of $Sleep seconds"
        Write-EventLog -LogName "Application" -Source "ChangeEmailAgent" -EventId 1006 -EntryType Information -Message "Starting ChangeEmailAgent listener with a sleep interval of $Sleep seconds" -ErrorAction Continue

        while ($true) {
            $requests = Receive-ChangeEmailAgentRequests

            if ($requests) {
                foreach ($request in $requests) {
                    Write-Verbose "Processing request: $($request | ConvertTo-Json -Depth 5)"

                    if(!$request.onPremisesImmutableId) {
                        Write-Warning "Request does not contain onPremisesImmutableId: $($request | ConvertTo-Json -Depth 5)"
                        Write-EventLog -LogName "Application" -Source "ChangeEmailAgent" -EventId 1008 -EntryType Warning -Message "Request does not contain onPremisesImmutableId: $($request | ConvertTo-Json -Depth 5)" -ErrorAction Continue
                        $request | Complete-ChangeEmailAgentRequest -Status "Failed" -ErrorMessage "Request does not contain onPremisesImmutableId"
                        continue
                    }

                    $immutableid = [System.Convert]::FromBase64String($request.onPremisesImmutableId)
                    $matchingusers = Get-ADUser -Filter { ms-Ds-ConsistencyGuid -eq $immutableid } -Properties mail,proxyAddresses,userPrincipalName

                    if (!$matchingusers) {
                        Write-Warning "No matching user found in Active Directory for request $($request | ConvertTo-Json -Depth 5)"
                        Write-EventLog -LogName "Application" -Source "ChangeEmailAgent" -EventId 1009 -EntryType Warning -Message "No matching user found in Active Directory for request $($request | ConvertTo-Json -Depth 5)" -ErrorAction Continue
                        $request | Complete-ChangeEmailAgentRequest -Status "Failed" -ErrorMessage "No matching user found in Active Directory"
                        continue
                    }

                    if ($matchingusers.Count -gt 1) {
                        Write-Warning "Multiple matching users found in Active Directory for request $($request | ConvertTo-Json -Depth 5)"
                        Write-EventLog -LogName "Application" -Source "ChangeEmailAgent" -EventId 1010 -EntryType Warning -Message "Multiple matching users found in Active Directory for request $($request | ConvertTo-Json -Depth 5)" -ErrorAction Continue
                        $request | Complete-ChangeEmailAgentRequest -Status "Failed" -ErrorMessage "Multiple matching users found in Active Directory"
                        continue
                    }

                    $user = $matchingusers | Select-Object -First 1

                    $proxyaddresses = @(
                        "SMTP:$($request.emailAddress)"
                    )

                    if ($user.proxyAddresses) {
                        $user.proxyAddresses | 
                        Where-Object { $_ -like "smtp:*" } | 
                        Where-Object { $_ -notin $proxyaddresses } | 
                        ForEach-Object {
                            $proxyaddresses += $_ -replace "^SMTP", "smtp"
                        }

                        $user.proxyAddresses | 
                        Where-Object { $_ -notlike "smtp:*" } | 
                        Where-Object { $_ -notin $proxyaddresses } | 
                        ForEach-Object {
                            $proxyaddresses += $_
                        }
                    }

                    Write-Verbose "Updating user $($user.SamAccountName)"
                    Write-Verbose " - userPrincipalName: $($user.UserPrincipalName) -> $($request.emailAddress)"
                    Write-Verbose " - mail: $($user.EmailAddress) -> $($request.emailAddress)"
                    Write-Verbose " - proxyAddresses:"
                    $user.proxyAddresses | Where-Object {$_ -cnotin $proxyaddresses} | ForEach-Object { Write-Verbose " - $_ (removed)" }
                    $proxyAddresses | Where-Object {$_ -cnotin $user.proxyaddresses} | ForEach-Object { Write-Verbose " + $_ (added)" }

                    $user | Set-ADUser -UserPrincipalName $request.emailAddress -EmailAddress $request.emailAddress -Replace @{
                        proxyAddresses = $proxyaddresses
                    }
                    
                    $request | Complete-ChangeEmailAgentRequest
                }

                if($RunBlockAfterChange) {
                    Write-Verbose "Running custom script block after processing changes"
                    & $RunBlockAfterChange
                }
            }
            else {
                Write-Verbose "No requests found, sleeping for $Sleep seconds"
                Write-EventLog -LogName "Application" -Source "ChangeEmailAgent" -EventId 1007 -EntryType Information -Message "No requests found, sleeping for $Sleep seconds" -ErrorAction Continue
            }

            Start-Sleep -Seconds $Sleep
        }
        
    }
}