Public/ActiveDirectory/Disable-User.ps1
|
function Disable-User { <# .SYNOPSIS Disables an Active Directory user account and optionally performs cloud offboarding actions. .DESCRIPTION Executes the full user offboarding workflow for a given identity: 1. Resolves the user via Search-User (AD lookup). 2. Disables the AD account and moves it to the configured Disabled OU (see settings.offboarding.disabledOU in config.json). 3. Stamps the AD Description field with a dated offboarding note, including forwarding outcome when EXO actions are requested. 4. Optionally removes all AD group memberships (see settings.offboarding.cleanupADGroups in config.json). 5. Optionally connects to Exchange Online and: - Sets mailbox forwarding to the user's manager (or a supplied override address). - Grants the manager full access to the disabled mailbox. 6. Writes a structured offboarding summary via Write-OffboardingSummary. HYBRID ENVIRONMENTS When settings.offboarding.useHybridAutoDisable is true (or -ForceCloud is NOT specified), EXO and downstream cloud steps are skipped on the assumption that AAD Connect or another sync mechanism will propagate the disable automatically. Pass -ForceCloud to override this behaviour and always run cloud steps. SUPPORTS -WhatIf / -Confirm All destructive operations (AD disable, group removal, mailbox changes) honour ShouldProcess so you can preview with -WhatIf before committing. .PARAMETER Identity The identity of the user to disable. Accepts sAMAccountName, UPN, display name, or any value supported by Search-User. .PARAMETER IncludeEXO When specified, Exchange Online offboarding actions are performed: mailbox forwarding is configured and the user's manager is granted full mailbox access. Overrides the settings.offboarding.includeEXO config value for this invocation. .PARAMETER ForwardToSmtpAddress SMTP address to forward the disabled user's mailbox to. Overrides automatic manager-based forwarding. Provide a plain address (e.g. jdoe@company.com) without an smtp: prefix — any accidental prefix is stripped automatically. Requires -IncludeEXO (or settings.offboarding.includeEXO = true). .PARAMETER DeliverToMailboxAndForward Controls whether a copy of each message is retained in the mailbox while forwarding is active. Defaults to $true (keep a copy) when not supplied. Pass -DeliverToMailboxAndForward:$false to forward-only with no local copy. .PARAMETER ForceCloud Forces cloud (EXO) offboarding steps to run regardless of the useHybridAutoDisable setting in config.json. Use this in fully cloud-only or manual-sync environments where AAD Connect is not handling propagation. .PARAMETER Credential A PSCredential used for Active Directory operations and, where applicable, cloud service authentication. When omitted, the current session identity is used. .INPUTS None. This function does not accept pipeline input. .OUTPUTS System.Management.Automation.PSCustomObject Returns an ordered hashtable (cast to PSCustomObject) with keys for each completed step, e.g. ADDisable, ADGroups, Forwarding, ManagerAccess, and any corresponding *Error keys if a non-fatal step failed. .EXAMPLE Disable-User -Identity "jdoe" Disables jdoe's AD account and moves it to the Disabled OU. No EXO actions are performed. Behaviour for group cleanup and hybrid sync is determined by config.json. .EXAMPLE Disable-User -Identity "jdoe" -IncludeEXO Disables the AD account and performs Exchange Online offboarding: forwards mail to jdoe's manager and grants the manager full mailbox access. .EXAMPLE Disable-User -Identity "jdoe@company.com" -IncludeEXO -ForwardToSmtpAddress "helpdesk@company.com" Disables the account and forwards mail to helpdesk@company.com instead of the user's manager. .EXAMPLE Disable-User -Identity "jdoe" -IncludeEXO -DeliverToMailboxAndForward:$false Disables the account, sets forwarding to the manager, and does NOT keep a local copy in the mailbox (forward-only mode). .EXAMPLE Disable-User -Identity "jdoe" -IncludeEXO -WhatIf Previews all actions without making any changes to AD or Exchange Online. .EXAMPLE Disable-User -Identity "jdoe" -ForceCloud -IncludeEXO Forces cloud offboarding steps even when useHybridAutoDisable is enabled in config.json. .NOTES - Requires the ActiveDirectory module and appropriate AD permissions. - EXO actions require ExchangeOnlineManagement (Connect-ExchangeOnline) or the internal Connect-ExchangeOnlineAlways helper. - The Disabled OU, group-cleanup toggle, EXO default, and hybrid-sync flag are all configurable via settings.offboarding in config.json. - A dated note is always written to the user's AD Description field, regardless of whether EXO actions are included. - Non-fatal EXO failures (forwarding, manager access) are captured in the returned object (*Error keys) and logged as warnings rather than terminating the workflow. .LINK Search-User .LINK Disable-ADUserAccount .LINK Remove-ADUserGroups .LINK Write-OffboardingSummary #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] param( [Parameter(Mandatory)] [string]$Identity, [switch]$IncludeEXO, [string]$ForwardToSmtpAddress, [switch]$DeliverToMailboxAndForward, [switch]$ForceCloud, [pscredential]$Credential ) Initialize-TechToolboxRuntime $user = $null $exoConnected = $false $results = [ordered]@{} try { Write-Log -Level Info -Message ("Starting Disable-User workflow for '{0}'..." -f $Identity) # --- Config load & validation --- $cfg = $script:cfg if (-not $cfg) { throw "Effective config is null. Check config.json path and schema." } $settings = $cfg.settings if (-not $settings) { throw "Config missing 'settings' node." } $off = $settings.offboarding if (-not $off) { throw "Config missing 'settings.offboarding' node." } # --- Compute effective options (switches override config) --- $effective = [ordered]@{ IncludeEXO = if ($PSBoundParameters.ContainsKey('IncludeEXO')) { [bool]$IncludeEXO } else { [bool]$off.includeEXO } UseHybridAutoDisable = if ($PSBoundParameters.ContainsKey('ForceCloud')) { -not [bool]$ForceCloud } else { [bool]$off.useHybridAutoDisable } DisabledOU = [string]$off.disabledOU CleanupADGroups = [bool]$off.cleanupADGroups } if ($effective.DisabledOU -is [string] -and [string]::IsNullOrWhiteSpace($effective.DisabledOU)) { Write-Log -Level Warn -Message "settings.offboarding.disabledOU is empty; OU move will be skipped." } # --- Resolve user (AD-only Search-User) --- Write-Log -Level Info -Message ("Offboarding: Resolving user '{0}'..." -f $Identity) $suParams = @{ Identity = $Identity } if ($Credential) { $suParams.Credential = $Credential } $user = Search-User @suParams if (-not $user) { throw "User '$Identity' not found." } # --- Compute forwarding target/stamp once (override > manager) --- $forwardTarget = $null $forwardSource = $null if ($effective.IncludeEXO) { if ($ForwardToSmtpAddress) { $forwardTarget = $ForwardToSmtpAddress.Trim() $forwardSource = 'override' } elseif ($user.ManagerUpn) { $forwardTarget = $user.ManagerUpn.Trim() $forwardSource = 'manager' } # Strip accidental smtp: prefix if someone pastes it if ($forwardTarget -match '^(?i)smtp:') { $forwardTarget = ($forwardTarget -replace '^(?i)smtp:', '').Trim() } # Validate only when we actually have a value if ($forwardTarget -and ($forwardTarget -notmatch '^[^@\s]+@[^@\s]+\.[^@\s]+$')) { Write-Log -Level Warn -Message "EXO: forwarding target doesn't look like an SMTP address: '$forwardTarget'. Forwarding will be skipped." $forwardTarget = $null $forwardSource = $null } } $forwardStamp = if ($effective.IncludeEXO) { if ($forwardTarget) { "Mail forwarding to $forwardTarget ($forwardSource)" } else { "EXO requested; forwarding not set" } } else { $null } # --- Disable AD user (and move OU handled inside Disable-ADUserAccount) --- Write-Log -Level Info -Message ("Offboarding: Disabling AD account for '{0}'..." -f $user.SamAccountName) if ($PSCmdlet.ShouldProcess($user.SamAccountName, "Disable AD account (and move to Disabled OU if configured)")) { $disableParams = @{ SamAccountName = $user.SamAccountName DisabledOU = $effective.DisabledOU } if ($Credential) { $disableParams.Credential = $Credential } $results.ADDisable = Disable-ADUserAccount @disableParams } # --- Always append "Disabled on <date>" to AD Description --- $dateStamp = Get-Date -Format 'yyyy-MM-dd HH:mm' $note = if ($effective.IncludeEXO) { if ($forwardTarget) { "Disabled on $dateStamp > Mail forwarding to $forwardTarget ($forwardSource)" } else { "Disabled on $dateStamp > EXO requested; forwarding not set" } } else { "Disabled on $dateStamp" } Set-OffboardingAdDescription ` -SamAccountName $user.SamAccountName ` -Note $note ` -SkipIfAlreadyPresent ` -Credential $Credential | Out-Null $descriptionNote = $note # --- Optional: cleanup AD groups --- if ($effective.CleanupADGroups) { Write-Log -Level Info -Message ("Offboarding: Cleaning AD group memberships for '{0}'..." -f $user.SamAccountName) if ($PSCmdlet.ShouldProcess($user.SamAccountName, "Cleanup AD group memberships")) { $grpParams = @{ SamAccountName = $user.SamAccountName } if ($Credential) { $grpParams.Credential = $Credential } $results.ADGroups = Remove-ADUserGroups @grpParams } } # --- Hybrid auto-disable short-circuit --- if ($effective.UseHybridAutoDisable) { Write-Log -Level Info -Message "Hybrid auto-disable enabled; cloud actions deferred to AAD Connect or downstream automation." if (Get-Command -Name Write-OffboardingSummary -ErrorAction SilentlyContinue) { Write-OffboardingSummary -User $user -Results $results } Write-Log -Level Ok -Message ("Disable-User workflow completed for '{0}' (hybrid short-circuit).`n" -f ($user.UserPrincipalName ? $user.UserPrincipalName : $Identity)) return [pscustomobject]$results } # --- Exchange Online (EXO) actions --- if ($effective.IncludeEXO) { Write-Log -Level Info -Message "EXO: starting Exchange Online actions." $exoConnected = $false try { # Prefer your helper; fallback to native connect $helperCmd = Get-Command -Name Connect-ExchangeOnlineAlways -ErrorAction SilentlyContinue if ($helperCmd) { $null = Connect-ExchangeOnlineAlways -ShowProgress:$false -ErrorAction Stop $exoConnected = $true } else { $nativeConnect = Get-Command -Name Connect-ExchangeOnline -ErrorAction SilentlyContinue if (-not $nativeConnect) { throw "ExchangeOnlineManagement cmdlets not available in this session." } Connect-ExchangeOnline -ShowBanner:$false -UseRPSSession:$false -ErrorAction Stop | Out-Null $exoConnected = $true } } catch { Write-Log -Level Warn -Message ("EXO: connect failed; skipping EXO actions. Reason: {0}" -f $_.Exception.Message) } if ($exoConnected -and $user.UserPrincipalName) { $setMailboxCmd = Get-Command -Name Set-Mailbox -ErrorAction SilentlyContinue $grantCmd = Get-Command -Name Grant-ManagerMailboxAccess -ErrorAction SilentlyContinue if (-not $setMailboxCmd -and -not $grantCmd) { Write-Log -Level Warn -Message "EXO: required cmdlets not found (Set-Mailbox / Grant-ManagerMailboxAccess)." } # Default keep-copy to true unless explicitly passed $keepCopy = $true if ($PSBoundParameters.ContainsKey('DeliverToMailboxAndForward')) { $keepCopy = [bool]$DeliverToMailboxAndForward } # Forwarding if ($setMailboxCmd -and $PSCmdlet.ShouldProcess($user.UserPrincipalName, "Set mailbox forwarding")) { try { if ($forwardTarget) { Write-Log -Level Info -Message ( "EXO: setting forwarding for '{0}' to '{1}' ({2}; keep copy: {3})..." -f $user.UserPrincipalName, $forwardTarget, $forwardSource, $keepCopy ) $results.Forwarding = Set-Mailbox ` -Identity $user.UserPrincipalName ` -ForwardingSmtpAddress $forwardTarget ` -DeliverToMailboxAndForward:$keepCopy ` -ErrorAction Stop } else { Write-Log -Level Warn -Message ( "EXO: skipping forwarding — no forwarding target provided and no manager assigned for '{0}'." -f $user.UserPrincipalName ) } } catch { $results.ForwardingError = $_.Exception.Message Write-Log -Level Warn -Message ( "EXO: set forwarding failed for '{0}': {1}" -f $user.UserPrincipalName, $_.Exception.Message ) } } # Manager access (optional) if ($grantCmd -and $PSCmdlet.ShouldProcess($user.UserPrincipalName, "Grant manager mailbox access")) { try { if ($user.ManagerUpn) { Write-Log -Level Info -Message ( "EXO: granting manager mailbox access for '{0}' to '{1}'..." -f $user.UserPrincipalName, $user.ManagerUpn ) $results.ManagerAccess = Grant-ManagerMailboxAccess ` -Identity $user.UserPrincipalName ` -ManagerUPN $user.ManagerUpn ` -ErrorAction Stop } else { Write-Log -Level Warn -Message ( "EXO: skipping manager mailbox access — no manager assigned for '{0}'." -f $user.UserPrincipalName ) } } catch { $results.ManagerAccessError = $_.Exception.Message Write-Log -Level Warn -Message ( "EXO: grant manager access failed for '{0}': {1}" -f $user.UserPrincipalName, $_.Exception.Message ) } } # Append EXO results to AD Description note $forwardFailed = -not [string]::IsNullOrWhiteSpace($results['ForwardingError']) $finalNote = if ($effective.IncludeEXO) { if ($forwardTarget) { if ($forwardFailed) { "Disabled on $dateStamp > Mail forwarding FAILED to $forwardTarget" } else { "Disabled on $dateStamp > Mail forwarding to $forwardTarget" } } else { "Disabled on $dateStamp > EXO requested; forwarding not set" } } else { "Disabled on $dateStamp" } if ($finalNote -ne $descriptionNote) { Set-OffboardingAdDescription ` -SamAccountName $user.SamAccountName ` -Note $finalNote ` -SkipIfAlreadyPresent ` -Credential $Credential | Out-Null } } } elseif (-not $user.UserPrincipalName) { Write-Log -Level Warn -Message "EXO: actions skipped (UserPrincipalName is empty)." } else { Write-Log -Level Info -Message "EXO: IncludeEXO is false; skipping EXO actions." } # --- Summary & return --- Write-Log -Level Info -Message ("Offboarding: Generating summary for '{0}'..." -f ($user.UserPrincipalName ? $user.UserPrincipalName : $Identity)) if (Get-Command -Name Write-OffboardingSummary -ErrorAction SilentlyContinue) { Write-OffboardingSummary -User $user -Results $results } Write-Log -Level Ok -Message ("Disable-User workflow completed for '{0}'." -f ($user.UserPrincipalName ? $user.UserPrincipalName : $Identity)) return [pscustomobject]$results } catch { $who = ($user -and $user.UserPrincipalName) ? $user.UserPrincipalName : $Identity Write-Log -Level Error -Message ("Disable-User failed for '{0}': {1}" -f $who, $_.Exception.Message) throw } finally { if ($exoConnected) { try { if (Get-Command -Name Disconnect-ExchangeOnline -ErrorAction SilentlyContinue) { Disconnect-ExchangeOnline -Confirm:$false | Out-Null } } catch { } } } } # SIG # Begin signature block # MIIfAgYJKoZIhvcNAQcCoIIe8zCCHu8CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDwbNrWYa51lLYA # 7GFOprjOYnZrfFKAbzbxz3mEDvMASaCCGEowggUMMIIC9KADAgECAhAR+U4xG7FH # qkyqS9NIt7l5MA0GCSqGSIb3DQEBCwUAMB4xHDAaBgNVBAMME1ZBRFRFSyBDb2Rl # IFNpZ25pbmcwHhcNMjUxMjE5MTk1NDIxWhcNMjYxMjE5MjAwNDIxWjAeMRwwGgYD # VQQDDBNWQURURUsgQ29kZSBTaWduaW5nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A # MIICCgKCAgEA3pzzZIUEY92GDldMWuzvbLeivHOuMupgpwbezoG5v90KeuN03S5d # nM/eom/PcIz08+fGZF04ueuCS6b48q1qFnylwg/C/TkcVRo0WFcKoFGT8yGxdfXi # caHtapZfbSRh73r7qR7w0CioVveNBVgfMsTgE0WKcuwxemvIe/ptmkfzwAiw/IAC # Ib0E0BjiX4PySbwWy/QKy/qMXYY19xpRItVTKNBtXzADUtzPzUcFqJU83vM2gZFs # Or0MhPvM7xEVkOWZFBAWAubbMCJ3rmwyVv9keVDJChhCeLSz2XR11VGDOEA2OO90 # Y30WfY9aOI2sCfQcKMeJ9ypkHl0xORdhUwZ3Wz48d3yJDXGkduPm2vl05RvnA4T6 # 29HVZTmMdvP2475/8nLxCte9IB7TobAOGl6P1NuwplAMKM8qyZh62Br23vcx1fXZ # TJlKCxBFx1nTa6VlIJk+UbM4ZPm954peB/fIqEacm8LkZ0cPwmLE5ckW7hfK4Trs # o+RaudU1sKeA+FvpOWgsPccVRWcEYyGkwbyTB3xrIBXA+YckbANZ0XL7fv7x29hn # gXbZipGu3DnTISiFB43V4MhNDKZYfbWdxze0SwLe8KzIaKnwlwRgvXDMwXgk99Mi # EbYa3DvA/5ZWikLW9PxBFD7Vdr8ZiG/tRC9I2Y6fnb+PVoZKc/2xsW0CAwEAAaNG # MEQwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQW # BBRfYLVE8caSc990rnrIHUjoB7X/KjANBgkqhkiG9w0BAQsFAAOCAgEAiGB2Wmk3 # QBtd1LcynmxHzmu+X4Y5DIpMMNC2ahsqZtPUVcGqmb5IFbVuAdQphL6PSrDjaAR8 # 1S8uTfUnMa119LmIb7di7TlH2F5K3530h5x8JMj5EErl0xmZyJtSg7BTiBA/UrMz # 6WCf8wWIG2/4NbV6aAyFwIojfAcKoO8ng44Dal/oLGzLO3FDE5AWhcda/FbqVjSJ # 1zMfiW8odd4LgbmoyEI024KkwOkkPyJQ2Ugn6HMqlFLazAmBBpyS7wxdaAGrl18n # 6bS7QuAwCd9hitdMMitG8YyWL6tKeRSbuTP5E+ASbu0Ga8/fxRO5ZSQhO6/5ro1j # PGe1/Kr49Uyuf9VSCZdNIZAyjjeVAoxmV0IfxQLKz6VOG0kGDYkFGskvllIpQbQg # WLuPLJxoskJsoJllk7MjZJwrpr08+3FQnLkRuisjDOc3l4VxFUsUe4fnJhMUONXT # Sk7vdspgxirNbLmXU4yYWdsizz3nMUR0zebUW29A+HYme16hzrMPOeyoQjy4I5XX # 3wXAFdworfPEr/ozDFrdXKgbLwZopymKbBwv6wtT7+1zVhJXr+jGVQ1TWr6R+8ea # tIOFnY7HqGaxe5XB7HzOwJKdj+bpHAfXft1vUoiKr16VajLigcYCG8MdwC3sngO3 # JDyv2V+YMfsYBmItMGBwvizlQ6557NbK95EwggWNMIIEdaADAgECAhAOmxiO+dAt # 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV # BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa # Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD # ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC # ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E # MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy # unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF # xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1 # 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB # MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR # WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6 # nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB # YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S # UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x # q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB # NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP # TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC # AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp # Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv # bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0 # aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB # LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc # Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov # Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy # oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW # juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF # mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z # twGpn1eqXijiuZQwgga0MIIEnKADAgECAhANx6xXBf8hmS5AQyIMOkmGMA0GCSqG # SIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx # GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRy # dXN0ZWQgUm9vdCBHNDAeFw0yNTA1MDcwMDAwMDBaFw0zODAxMTQyMzU5NTlaMGkx # CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4 # RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYg # MjAyNSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC0eDHTCphB # cr48RsAcrHXbo0ZodLRRF51NrY0NlLWZloMsVO1DahGPNRcybEKq+RuwOnPhof6p # vF4uGjwjqNjfEvUi6wuim5bap+0lgloM2zX4kftn5B1IpYzTqpyFQ/4Bt0mAxAHe # HYNnQxqXmRinvuNgxVBdJkf77S2uPoCj7GH8BLuxBG5AvftBdsOECS1UkxBvMgEd # gkFiDNYiOTx4OtiFcMSkqTtF2hfQz3zQSku2Ws3IfDReb6e3mmdglTcaarps0wjU # jsZvkgFkriK9tUKJm/s80FiocSk1VYLZlDwFt+cVFBURJg6zMUjZa/zbCclF83bR # VFLeGkuAhHiGPMvSGmhgaTzVyhYn4p0+8y9oHRaQT/aofEnS5xLrfxnGpTXiUOeS # LsJygoLPp66bkDX1ZlAeSpQl92QOMeRxykvq6gbylsXQskBBBnGy3tW/AMOMCZIV # NSaz7BX8VtYGqLt9MmeOreGPRdtBx3yGOP+rx3rKWDEJlIqLXvJWnY0v5ydPpOjL # 6s36czwzsucuoKs7Yk/ehb//Wx+5kMqIMRvUBDx6z1ev+7psNOdgJMoiwOrUG2Zd # SoQbU2rMkpLiQ6bGRinZbI4OLu9BMIFm1UUl9VnePs6BaaeEWvjJSjNm2qA+sdFU # eEY0qVjPKOWug/G6X5uAiynM7Bu2ayBjUwIDAQABo4IBXTCCAVkwEgYDVR0TAQH/ # BAgwBgEB/wIBADAdBgNVHQ4EFgQU729TSunkBnx6yuKQVvYv1Ensy04wHwYDVR0j # BBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1Ud # JQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0 # cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0 # cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8E # PDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz # dGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw # DQYJKoZIhvcNAQELBQADggIBABfO+xaAHP4HPRF2cTC9vgvItTSmf83Qh8WIGjB/ # T8ObXAZz8OjuhUxjaaFdleMM0lBryPTQM2qEJPe36zwbSI/mS83afsl3YTj+IQhQ # E7jU/kXjjytJgnn0hvrV6hqWGd3rLAUt6vJy9lMDPjTLxLgXf9r5nWMQwr8Myb9r # EVKChHyfpzee5kH0F8HABBgr0UdqirZ7bowe9Vj2AIMD8liyrukZ2iA/wdG2th9y # 1IsA0QF8dTXqvcnTmpfeQh35k5zOCPmSNq1UH410ANVko43+Cdmu4y81hjajV/gx # dEkMx1NKU4uHQcKfZxAvBAKqMVuqte69M9J6A47OvgRaPs+2ykgcGV00TYr2Lr3t # y9qIijanrUR3anzEwlvzZiiyfTPjLbnFRsjsYg39OlV8cipDoq7+qNNjqFzeGxcy # tL5TTLL4ZaoBdqbhOhZ3ZRDUphPvSRmMThi0vw9vODRzW6AxnJll38F0cuJG7uEB # YTptMSbhdhGQDpOXgpIUsWTjd6xpR6oaQf/DJbg3s6KCLPAlZ66RzIg9sC+NJpud # /v4+7RWsWCiKi9EOLLHfMR2ZyJ/+xhCx9yHbxtl5TPau1j/1MIDpMPx0LckTetiS # uEtQvLsNz3Qbp7wGWqbIiOWCnb5WqxL3/BAPvIXKUjPSxyZsq8WhbaM2tszWkPZP # ubdcMIIG7TCCBNWgAwIBAgIQCoDvGEuN8QWC0cR2p5V0aDANBgkqhkiG9w0BAQsF # ADBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNV # BAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYgU0hB # MjU2IDIwMjUgQ0ExMB4XDTI1MDYwNDAwMDAwMFoXDTM2MDkwMzIzNTk1OVowYzEL # MAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJE # aWdpQ2VydCBTSEEyNTYgUlNBNDA5NiBUaW1lc3RhbXAgUmVzcG9uZGVyIDIwMjUg # MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANBGrC0Sxp7Q6q5gVrMr # V7pvUf+GcAoB38o3zBlCMGMyqJnfFNZx+wvA69HFTBdwbHwBSOeLpvPnZ8ZN+vo8 # dE2/pPvOx/Vj8TchTySA2R4QKpVD7dvNZh6wW2R6kSu9RJt/4QhguSssp3qome7M # rxVyfQO9sMx6ZAWjFDYOzDi8SOhPUWlLnh00Cll8pjrUcCV3K3E0zz09ldQ//nBZ # ZREr4h/GI6Dxb2UoyrN0ijtUDVHRXdmncOOMA3CoB/iUSROUINDT98oksouTMYFO # nHoRh6+86Ltc5zjPKHW5KqCvpSduSwhwUmotuQhcg9tw2YD3w6ySSSu+3qU8DD+n # igNJFmt6LAHvH3KSuNLoZLc1Hf2JNMVL4Q1OpbybpMe46YceNA0LfNsnqcnpJeIt # K/DhKbPxTTuGoX7wJNdoRORVbPR1VVnDuSeHVZlc4seAO+6d2sC26/PQPdP51ho1 # zBp+xUIZkpSFA8vWdoUoHLWnqWU3dCCyFG1roSrgHjSHlq8xymLnjCbSLZ49kPmk # 8iyyizNDIXj//cOgrY7rlRyTlaCCfw7aSUROwnu7zER6EaJ+AliL7ojTdS5PWPsW # eupWs7NpChUk555K096V1hE0yZIXe+giAwW00aHzrDchIc2bQhpp0IoKRR7YufAk # prxMiXAJQ1XCmnCfgPf8+3mnAgMBAAGjggGVMIIBkTAMBgNVHRMBAf8EAjAAMB0G # A1UdDgQWBBTkO/zyMe39/dfzkXFjGVBDz2GM6DAfBgNVHSMEGDAWgBTvb1NK6eQG # fHrK4pBW9i/USezLTjAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYB # BQUHAwgwgZUGCCsGAQUFBwEBBIGIMIGFMCQGCCsGAQUFBzABhhhodHRwOi8vb2Nz # cC5kaWdpY2VydC5jb20wXQYIKwYBBQUHMAKGUWh0dHA6Ly9jYWNlcnRzLmRpZ2lj # ZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFRpbWVTdGFtcGluZ1JTQTQwOTZTSEEy # NTYyMDI1Q0ExLmNydDBfBgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vY3JsMy5kaWdp # Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRUaW1lU3RhbXBpbmdSU0E0MDk2U0hB # MjU2MjAyNUNBMS5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB # MA0GCSqGSIb3DQEBCwUAA4ICAQBlKq3xHCcEua5gQezRCESeY0ByIfjk9iJP2zWL # pQq1b4URGnwWBdEZD9gBq9fNaNmFj6Eh8/YmRDfxT7C0k8FUFqNh+tshgb4O6Lgj # g8K8elC4+oWCqnU/ML9lFfim8/9yJmZSe2F8AQ/UdKFOtj7YMTmqPO9mzskgiC3Q # YIUP2S3HQvHG1FDu+WUqW4daIqToXFE/JQ/EABgfZXLWU0ziTN6R3ygQBHMUBaB5 # bdrPbF6MRYs03h4obEMnxYOX8VBRKe1uNnzQVTeLni2nHkX/QqvXnNb+YkDFkxUG # tMTaiLR9wjxUxu2hECZpqyU1d0IbX6Wq8/gVutDojBIFeRlqAcuEVT0cKsb+zJNE # suEB7O7/cuvTQasnM9AWcIQfVjnzrvwiCZ85EE8LUkqRhoS3Y50OHgaY7T/lwd6U # Arb+BOVAkg2oOvol/DJgddJ35XTxfUlQ+8Hggt8l2Yv7roancJIFcbojBcxlRcGG # 0LIhp6GvReQGgMgYxQbV1S3CrWqZzBt1R9xJgKf47CdxVRd/ndUlQ05oxYy2zRWV # FjF7mcr4C34Mj3ocCVccAvlKV9jEnstrniLvUxxVZE/rptb7IRE2lskKPIJgbaP5 # t2nGj/ULLi49xTcBZU8atufk+EMF/cWuiC7POGT75qaL6vdCvHlshtjdNXOCIUjs # arfNZzGCBg4wggYKAgEBMDIwHjEcMBoGA1UEAwwTVkFEVEVLIENvZGUgU2lnbmlu # ZwIQEflOMRuxR6pMqkvTSLe5eTANBglghkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3 # AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisG # AQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCBvluEIEgVt # QK60xQh3Ov+M1fwIVf+APL10oV8RZj4pwTANBgkqhkiG9w0BAQEFAASCAgDJ83tX # 6cvnP/GxCexkdHDA2vxs/Q83fTV5lG7Dq3slB6GvTQPT60Pno+gTejBt9pJ1b5oU # 8TnxPeW31SXQsBBresgPQK6Xse9JOxYCJ8WBkl8lANtaZVnZutnRB2vxGleDiHTY # dQRE4ExuS90NYC2BO6d5M4dzT90Bw65rxxzl85TrrK2NSxhhTVQeqwXQhUTcvGqs # Y2T+Nofmra8pivvByJXblL+3v9ipJ3Qdr6YLcR0wXHEd5nie8MCQN6A0RQl4tarL # g2+sO9yGbi1HY8GMHjkhtolHoslptIWnXIJEO/9nKhY8gyLDsU4RURUYmsEcwCtH # UyphgSaYjFCrAOu7KuMQ/mMOT0RalQnErOcPdJyFfEyYg/H8N+y+usIcpGoVaKqh # 7fY3kjH8JxbML8J/gT2RsjdJju6iw9OZrdjkuwnsXjGPZ1hV1j3uM2mQkCAYLkfe # TmjZhMm/us/YoBSNnaDrnTKtYVn0ohA3s/4UcUxfSlNpHAaWCE364CZpOX4Na8RP # bEl/JVsUIq6R/mL/5lHa3XRlUHjjvpDF7q0ZjA6+tic4jbWI2BW23UClq53V33C6 # 9q3sD1vMtHl3AuSazCprokzloQc0XLov6ythDTq+gNhxSuTV5xwZq/HkDE3Bttra # Zl2hmsKH1yri4Vse5tvi7QBe0pYIoir0TguFcaGCAyYwggMiBgkqhkiG9w0BCQYx # ggMTMIIDDwIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg # SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcg # UlNBNDA5NiBTSEEyNTYgMjAyNSBDQTECEAqA7xhLjfEFgtHEdqeVdGgwDQYJYIZI # AWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJ # BTEPFw0yNjA1MDYxNTExNTdaMC8GCSqGSIb3DQEJBDEiBCBnmeJQAHPRnfsRcQ3g # jg+VK7syWuT8EDup+2W5xzQzYTANBgkqhkiG9w0BAQEFAASCAgCYx0sLpsB+CmBw # UnXKt1GNE1Xw1/E+RonW2b56o99iux6c1jTJcRRexU40UrTbey5xPBbhT8AZzYNm # 4BjAScWHsZQF4sPOGfDVXTrHjDrkZngNKhe/Uhxvuop6Pj6G43TGVwd5tA8DBKo+ # tWZfL2pkrj/lfqGW8zaGh31nZ84HLKLjUa/KndLSF2yhPvuIRN+miQa34u9eU6B1 # Wq/47pLnDtg6MrJWTqNe1bDO60CaTw2gSylfaNuITbXZxCVtWP8eCNKlcERUfAjn # AUJQsKzBZSekWozsNOGhHciaeHmsRnvtwGjUDExV1RtnBsE1ODrUETQCiFwi2NO8 # 3xGraQ6ZToIA/JWm16BuCjvEVcWFtvF0VLh5ZX47CW90fwRhTpoxOCAgcNL/AMbP # ZNnw5gnP2+2XcekNN0iCE3gBobqd8qze799H86VnkCCanXra2mzDOpUH6wE/3hZN # rH9Qs8oesvT7qg5wsNNVHFlFeP/PoKWTi/YORE+0eXUaC9KkFMKGK5+4n/gD341D # CX/4Cqz5WBaM7CPZLag8Udfst0BEZXr/e8a+ggr6qAkDWAjJ5CmklN8B/OQ6mS/K # pMXPz0pJMr24XQ6CeIlB0JDFJMWbbmPfHDlW5wbxKY59dUdm5Bqwbx/70bOExzzC # un1MiBCzNauCj/P3qk6OqQ0spEXj3A== # SIG # End signature block |