ozo-ad-manage-delegations.ps1

#Requires -Modules ActiveDirectory,DFSN,DFSR,DSACL,GroupPolicy,ImportExcel,OZO,OZOAD,OZOFiles,OZOLogger

<#PSScriptInfo
    .VERSION 0.0.1
    .GUID 069ad55f-163a-4900-b35b-2a1100d64e81
    .AUTHOR Andy Lievertz <alievertz@onezeroone.dev>
    .COMPANYNAME One Zero One
    .COPYRIGHT This script is released under the terms of the GNU General Public License ("GPL") version 2.0.
    .TAGS
    .LICENSEURI https://github.com/onezeroone-dev/OZO-AD-Manage-Delegations/blob/main/LICENSE
    .PROJECTURI https://github.com/onezeroone-dev/OZO-AD-Manage-Delegations
    .ICONURI
    .EXTERNALMODULEDEPENDENCIES ActiveDirectory,DFSN,DFSR,DSACL,GroupPolicy,ImportExcel
    .REQUIREDSCRIPTS
    .EXTERNALSCRIPTDEPENDENCIES
    .RELEASENOTES https://github.com/onezeroone-dev/OZO-AD-Manage-Delegations/blob/main/CHANGELOG.md
#>


<#
    .SYNOPSIS
    See description.
    .DESCRIPTION
    Creates AD delegations based on a configuration file. This script can create OU Delegations, apply permissions to GPOs, grant access to DFSN roots, grant access to DFSN folders, and create delegations to DFSR replication groups.
    .PARAMETER Configuration
    Path to the JSON configuration file. Defaults to "ad-create-delegations.json" in the same directory as the script.
    .PARAMETER OutDir
    Path for the Excel results report. Defaults to the current directory.
    .LINK
    https://github.com/onezeroone-dev/OZO-AD-Manage-Delegations/blob/main/README.md
    .LINK
    https://github.com/SimonWahlin/DSACL/blob/master/docs
    .LINK
    https://learn.microsoft.com/en-us/powershell/module/grouppolicy/set-gppermission?view=windowsserver2022-ps
    .LINK
    https://learn.microsoft.com/en-us/powershell/module/dfsn/grant-dfsnaccess?view=windowsserver2022-ps
    .LINK
    https://learn.microsoft.com/en-us/powershell/module/dfsr/grant-dfsrdelegation?view=windowsserver2022-ps
    .NOTES
    Run this script as a user with rights to create AD delegations (e.g., a Domain Admin) from within a writable directory.
#>


# PARAMETERS
[CmdletBinding(SupportsShouldProcess = $true)] Param (
    [Parameter(Mandatory=$false,HelpMessage="Path to the JSON configuration file")][String]$Configuration = (Join-Path -Path $PSScriptRoot -ChildPath "ad-create-delegations.json"),
    [Parameter(Mandatory=$false,HelpMessage="Path for the Excel results report")][String]$OutDir = (Get-Location)
)

# CLASSES
Class Main {
    # PROPERTIES: Strings
    [String] $excelPath = $null
    # PROPERTIES: PSCustomObjects
    [PSCustomObject] $Json     = $null
    [PSCustomObject] $ozoLogger = $null
    # PROPERTIES: PSCUstomObject lists
    [System.Collections.Generic.List[PSCustomObject]] $ouDelegations         = @()
    [System.Collections.Generic.List[PSCustomObject]] $gpoPermissions        = @()
    [System.Collections.Generic.List[PSCustomObject]] $dfsnRootPermissions   = @()
    [System.Collections.Generic.List[PSCustomObject]] $dfsnFolderPermissions = @()
    [System.Collections.Generic.List[PSCustomObject]] $dfsrPermissions       = @()
    # METHODS: Constructor method
    Main($Configuration,$OutDir) {
        # Create a logger object
        $this.ozoLogger = (New-OZOLogger)
        # Declare ourselves to the world
        $this.ozoLogger.Write("Starting process.","Information")
        # And the results of ValidateConfiguration and ValidateEnvironment to set validates
        If (($this.ValidateConfiguration($Configuration) -And $this.ValidateEnvironment($OutDir)) -eq $true) {
            # Configuration and environment validate; call the permissions methods
            $this.CreateOUDelegations()
            $this.SetGPOPermissions()
            $this.GrantDFSNRootPermissions()
            $this.GrantDFSNFolderPermissions()
            $this.GrantDFSRPermissions()
        }
        # Report
        $this.Report()
        $this.ozoLogger.Write("Process complete.","Information")
    }
    # METHODS: Configuration validation method
    Hidden [Boolean] ValidateConfiguration($Configuration) {
        # control variable
        [Boolean]$Return = $true
        # Determine if the JSON path is valid
        If ([Boolean](Test-Path -Path $Configuration -ErrorAction SilentlyContinue) -eq $true) {
            # JSON path is valid; try to read the JSON
            Try {
                $this.Json = Get-Content $Configuration -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop
                # Success
            } Catch {
                # Failure
                $this.ozoLogger.Write(("Invalid JSON in " + $Configuration + "."),"Error")
                $Return = $false
            }
        } Else {
            # JSON path is not valid
            $this.ozoLogger.Write(("Could not read configuration file " + $Configuration + "."),"Error")
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: Environment validation method
    Hidden [Boolean] ValidateEnvironment($OutDir) {
        # Control variable
        [Boolean] $Return = $true
        # Determine if outDir is writable
        If ((Test-OZOPath -Path $OutDir -Writable) -eq $true) {
            # OutDir is writable
            $this.excelPath = (Join-Path -Path $OutDir -ChildPath ((Get-OZO8601Date -Time) + "-ozo-ad-manage-delegations.xlsx"))
            $this.ozoLogger.Write(("Using " + $this.excelPath + " for the Excel report."),"Information")
        } Else {
            # OutDir is not writable
            $this.ozoLogger.Write("Provided output directory is not writable.","Warning")
            # Determine if current location is writable
            If ((Test-OZOPath -Path (Get-Location) -Writable) -eq $true) {
                # Current location is writable
                $this.excelPath = (Join-Path -Path (Get-Location) -ChildPath ((Get-OZO8601Date -Time) + "-ozo-ad-manage-delegations.xlsx"))
                $this.ozoLogger.Write(("Using " + $this.excelPath + " for the Excel results report."),"Information")
            } Else {
                # Current location is not writable
                $this.ozoLogger.Write("Current directory is not writable; cannot proceed.","Error")
                $Return = $false
            }
        }
        # Return
        return $Return
    }
    # METHODS: CreateDelegations method
    Hidden [Void] CreateOUDelegations() {
        # Determine if there are OU Delegations to process
        If (($this.Json.ADOUDelegations).Count -gt 0) {
            # There are OU Delegations to process; report
            $this.ozoLogger.Write("Processing Delegations.","Information")
            # Iterate over the OU Delegations
            ForEach ($delegation in $this.Json.ADOUDelegations) {
                # Iterate over the OUs
                ForEach ($ouDN in $delegation.OUs) {
                    # Iterate over the Identities
                    ForEach ($identity in $delegation.Identities) {
                        # Iterate over the Permissions
                        ForEach ($permission in $delegation.Permissions) {
                            # Create an OUDelegation object for this Delegation's OU + Identity + Permission
                            $this.ouDelegations.Add(([OUDelegation]::new($ouDN,$identity,$permission)))
                        }
                    }
                }
            }
        } Else {
            # There are no OU Delegations to process
            $this.ozoLogger.Write("No delegations to process.","Warning")
        }
    }
    # METHODS: SetGPOPermissions method
    Hidden [Void] SetGPOPermissions() {
        # Determine if there are GPO Permissions to process
        If (($this.Json.ADGPOPermissions).Count -gt 0) {
            # There are GPO Permissiont to process; report
            $this.ozoLogger.Write("Processing GPO Permissions.","Information")
            # Iterate over the Permissions
            ForEach ($gpoPermission in $this.Json.ADGPOPermissions) {
                # Iterate over the GPO Names
                ForEach ($gpoName in $gpoPermission.GPONames) {
                    # Iterate over the Group Names
                    ForEach ($group in $gpoPermission.GroupNames) {
                        # Iterate over the Permissions
                        ForEach ($permission in $gpoPermission.Permissions) {
                            # Create a GPOPermissions object for this GPO Name + Group Name + Permission
                            $this.gpoPermissions.Add(([GPOPermissions]::new($gpoName,$group,$permission)))
                        }
                    }
                }
            }
        } Else {
            # There are no GPO Permissiosn to process
            $this.ozoLogger.Write("No GPO permissions to process.","Warning")
        }
    }
    # METHODS: GrantDFSNRootPermissions method
    Hidden [Void] GrantDFSNRootPermissions() {
        # Determine if there are DFSN Root Permissions to process
        If (($this.Json.ADDFSNRootPermissions).Count -gt 0) {
            # There are DFSN Root Permissions to process; report
            $this.ozoLogger.Write("Processing DFSN root permissions.","Information")
            # Iterate over the DFSN Root Permissions
            ForEach ($dfsnRootPermission in $this.Json.ADDFSNRootPermissions) {
                # Iterate over the Roots
                ForEach ($dfsnRoot in $dfsnRootPermission.DFSNRoots) {
                    # Iterate over the Identities
                    ForEach ($identity in $dfsnRootPermission.Identities) {
                        # Create a DFSNRootPermissions object for this Root + Identity
                        $this.dfsnRootPermissions.Add(([DFSNRootPermissions]::new($dfsnRoot,$identity)))
                    }
                }
            }
        } Else {
            # There are no DFSN Root Permissions to process
            $this.ozoLogger.Write("No DFSN root permissions to set.","Warning")
        }
    }
    # METHODS: GrantDFSNFolderPermissions method
    Hidden [Void] GrantDFSNFolderPermissions() {
        # Determine if there are DFSN Folder Permissions to process
        If (($this.Json.ADDFSNFolderPermissions).Count -gt 0) {
            # There are DFSN Folder Permissions to process
            $this.ozoLogger.Write("Processing DFSN folder permissions.","Information")
            # Iterate over the DFSN Folder Permissions
            ForEach ($dfsnFolderPermission in $this.Json.ADDFSNFolderPermissions) {
                # Iterate over the Folders
                ForEach ($dfsnFolder in $dfsnFolderPermission.DFSNFolders) {
                    # Iterate over the Identities
                    ForEach ($identity in $dfsnFolderPermission.Identities) {
                        # Create a DFSNFolderPermissions object for this Folder + Identity
                        $this.dfsnFolderPermissions.Add(([DFSNFolderPermissions]::new($dfsnFolder,$identity)))
                    }
                }
            }
        } Else {
            # There are no DFSN Folder Permissions to process
            $this.ozoLogger.Write("No DFSN folder permissions to set.","Warning")
        }
    }
    # METHODS: GrantDFSRPermissions method
    Hidden [Void] GrantDFSRPermissions() {
        # Determine if there are DFSR Permissions to process
        If (($this.Json.ADDFSRPermissions).Count -gt 0) {
            # There are DFSR Permissions to process; report
            $this.ozoLogger.Write("Processing DFSR Permissions.","Information")
            # Iterate over the DFSR Permissions
            ForEach ($dfsrPermission in $this.Json.ADDFSRPermissions) {
                # Iterate over the Groups
                ForEach ($dfsrGroup in $dfsrPermission.DFSRGroups) {
                    # Iterate over the Identities
                    ForEach ($identity in $dfsrPermission.Identities) {
                        # Create a DFSRPermissions object for this Group + Identity
                        $this.dfsrPermissions.Add(([DFSRPermissions]::new($dfsrGroup,$identity)))
                    }
                }
            }
        } Else {
            # There are no DFSR Permissiosn to process
            $this.ozoLogger.Write("No DFSR permissions to set.","Warning")
        }
    }
    # Report method
    Hidden [Void] Report() {
        # Determine that at least one object was processed
        If (($this.ouDelegations + $this.gpoPermissions + $this.dfsnRootPermissions + $this.dfsnFolderPermissions + $this.dfsrPermissions).Count -gt 0) {
            # At least one object was processed; Produce Excel output
            $this.ouDelegations | Select-Object -Property @{Name="Delegation";Expression={$_.Description}},@{Name="Success";Expression={$_.Success}},@{Name="Messages";Expression={$_.messages -Join "; "}} | Export-Excel -WorksheetName "Delegations" -Path $this.excelPath
            $this.gpoPermissions | Select-Object -Property @{Name="GPO Permission";Expression={$_.Description}},@{Name="Success";Expression={$_.Success}},@{Name="Messages";Expression={$_.messages -Join "; "}} | Export-Excel -WorksheetName "GPO Permissions" -Path $this.excelPath
            $this.dfsnRootPermissions | Select-Object -Property @{Name="DFSN Root Permission";Expression={$_.Description}},@{Name="Success";Expression={$_.Success}},@{Name="Messages";Expression={$_.messages -Join "; "}} | Export-Excel -WorksheetName "DFSN Root Permissions" -Path $this.excelPath
            $this.dfsnFolderPermissions | Select-Object -Property @{Name="DFSN Folder Permission";Expression={$_.Description}},@{Name="Success";Expression={$_.Success}},@{Name="Messages";Expression={$_.messages -Join "; "}} | Export-Excel -WorksheetName "DFSN Folder Permissions" -Path $this.excelPath
            $this.dfsrPermissions | Select-Object -Property @{Name="DFSR Permission";Expression={$_.Description}},@{Name="Success";Expression={$_.Success}},@{Name="Messages";Expression={$_.messages -Join "; "}} | Export-Excel -WorksheetName "DFSR Permissions" -Path $this.excelPath
            # Determine if session is interactive
            If ([Environment]::UserInteractive -eq $true) {
                # Session is interactive; produce output for the operator
                $this.ouDelegations | Select-Object -Property @{Name="Delegation";Expression={$_.Description}},@{Name="Success";Expression={$_.Success}} | Format-Table | Out-Host
                $this.gpoPermissions | Select-Object -Property @{Name="GPO Permission";Expression={$_.Description}},@{Name="Success";Expression={$_.Success}} | Format-Table | Out-Host
                $this.dfsnRootPermissions | Select-Object -Property @{Name="DFSN Root Permission";Expression={$_.Description}},@{Name="Success";Expression={$_.Success}} | Format-Table | Out-Host
                $this.dfsnFolderPermissions | Select-Object -Property @{Name="DFSN Folder Permission";Expression={$_.Description}},@{Name="Success";Expression={$_.Success}} | Format-Table | Out-Host
                $this.dfsrPermissions | Select-Object -Property @{Name="DFSR Permission";Expression={$_.Description}},@{Name="Success";Expression={$_.Success}} | Format-Table | Out-Host
                # Determine if Excel was created
                If ([Boolean](Test-Path -Path $this.excelPath -ErrorAction SilentlyContinue) -eq $true) {
                    # Excel was created
                    $this.ozoLogger.Write(("For additional information, please see " + $this.excelPath + "."),"Information")
                } Else {
                    $this.ozoLogger.Write("No Excel results report generated.","Warning")
                }
            }
        } Else {
            # No objects were processed
            $this.ozoLogger.Write("No objects were processed.","Warning")
        }
    }
}

Class OUDelegation {
    # PROPERTIES: Booleans, Hashtables, Strings
    [Boolean] $Success   = $false
    [Boolean] $Validates = $false
    # PROPERTIES: Hashtables
    [Hashtable] $erMap   = @{}
    [Hashtable] $guidMap = @{}
    # PROPERTIES: Strings
    [String] $dcOuDN      = $null
    [String] $duOuDN      = $null
    [String] $ouDN        = $null
    [String] $Description = $null
    [String] $Identity    = $null
    [String] $identityDN  = $null
    [String] $Permission  = $null
    # PROPERTIES: PSCustomObjects
    [PSCustomObject] $Acl         = $null
    [PSCustomObject] $Permissions = @{
        CreateChildComputers = "Delegates create child computer objects"
        CreateChildContacts = "Delegates create child contacts objects"
        CreateChildGroups = "Delegates create child groups objects"
        CreateChildUsers = "Delegates create child user objects"
        CreateOUs = "Delegates create OU"
        DeleteChildComputers = "Delegates delete child computer objects"
        DeleteChildContacts = "Delegates delete child contacts objects"
        DeleteChildGroups = "Delegates delete child groups objects"
        DeleteChildUsers = "Delegates delete child user objects"
        DeleteOUs = "Delegates delete OU"
        DomainJoinComputer = "Delegates create computer objects, Write Name, and Write name"
        DomainLeaveComputer = "Delegates delete computer objects, Write Name, and Write name"
        EnableDisableComputers = "Delegates enable and disable computer objects"
        EnableDisableUsers = "Delegates enable and disable user objects"
        FullControlComputers = "Delegates full control to computer objects"
        FullControlContacts = "Delegates full control to contacts objects"
        FullControlGroups = "Delegates full control to group objects"
        FullControlUsers = "Delegates full control to user objects"
        FullControlOUs = "Delegates full control for organizational unit objects"
        LinkGPO = "Delegates link GPO"
        ModifyGroupMembership = "Delegates modify group membership"
        ResetUserPasswords = "Delegates reset password"
        ReadBitLockerRecovery = "Delegates read to the BitLocker Recovery information"
    }
    # PROPERTIES: String lists
    [System.Collections.Generic.List[String]] $Messages = @()
    # METHODS: Constructor method
    OUDelegation($ouDN,$Identity,$Permission) {
        # Set properties
        $this.ouDN        = $ouDN
        $this.Identity    = $Identity
        $this.Permission  = $Permission
        $this.Description = ($this.Permission + " for " + $this.Identity + " on " + $this.ouDN)
        # Create a guid map and extended rights map
        $this.guidMap = (New-OZOADGuidMap)
        $this.erMap   = (New-OZOADExtendedRightsMap)
        # Determine if delegation validates
        If ($this.ValidateDelegation() -eq $true) {
            # Delegation validated
            $this.Validates = $true
            # Switch on permission
            Switch ($this.Permission) {
                "CreateChildComputers"   { $this.Success = $this.CreateChildComputers()   }
                "CreateChildContacts"    { $this.Success = $this.CreateChildContacts()    }
                "CreateChildGroups"      { $this.Success = $this.CreateChildGroups()      }
                "CreateChildUsers"       { $this.Success = $this.CreateChildUsers()       }
                "CreateOUs"              { $this.Success = $this.CreateOUs()              }
                "DeleteChildComputers"   { $this.Success = $this.DeleteChildComputers()   }
                "DeleteChildContacts"    { $this.Success = $this.DeleteChildContacts()    }
                "DeleteChildGroups"      { $this.Success = $this.DeleteChildGroups()      }
                "DeleteChildUsers"       { $this.Success = $this.DeleteChildUsers()       }
                "DeleteOUs"              { $this.Success = $this.DeleteOUs()              }
                "DomainJoinComputer"     { $this.Success = $this.DomainJoinComputer()     }
                "EnableDisableComputers" { $this.Success = $this.EnableDisableComputers() }
                "EnableDisableUsers"     { $this.Success = $this.EnableDisableUsers()     }
                "FullControlComputers"   { $this.Success = $this.FullControlComputers()   }
                "FullControlContacts"    { $this.Success = $this.FullControlContacts()    }
                "FullControlGroups"      { $this.Success = $this.FullControlGroups()      }
                "FullControlUsers"       { $this.Success = $this.FullControlUsers()       }
                "FullControlOUs"         { $this.Success = $this.FullControlOUs()         }
                "LinkGPO"                { $this.Success = $this.LinkGPO()                }
                "ModifyGroupMembership"  { $this.Success = $this.ModifyGroupMembership()  }
                "ReadBitLockerRecovery"  { $this.Success = $this.ReadBitLockerRecovery()  }
                "ResetUserPasswords"     { $this.Success = $this.ResetUserPasswords()     }
                default {
                    $this.Messages.Add(("No method found that matches " + $this.permission))
                    $this.Success = $false
                }
            }
        } Else {
            # Delegation did not validate
            $this.Messages.Add("Delegation does not validate")
            $this.Validates = $false
        }
    }
    # METHODS: ValidateDelegation method
    Hidden [Boolean] ValidateDelegation() {
        # Control variable
        [Boolean] $Return = $true
        # Determine if ouDN is empty or null
        If ([String]::IsNullOrEmpty($this.ouDN)) {
            # ouDN is empty or null
            $this.Messages.Add("OU cannot be empty or null")
            $Return = $false
        }
        # Determine if Identity is empty or null
        If ([String]::IsNullOrEmpty($this.Identity)) {
            # Identity is empty or null
            $this.Messages.Add("Identity cannot be empty or null")
            $Return = $false
        } Else {
            # Try to get the distinguished name of the Identity
            Try {
                $this.identityDN = (Get-ADObject -Filter {SamAccountName -eq $this.Identity} -ErrorAction Stop).DistinguishedName
                # Success; determine if IdentityDN is empty or null
                If ([String]::IsNullOrEmpty($this.identityDN)) {
                    # IdentityDN is empty or null
                    $this.Messages.Add("Failed to retrieve distinguished name for Identity")
                    $Return = $false
                }
            } Catch {
                # Failure
                $this.Messages.Add("Identity does not exist in AD")
                $Return = $false
            }
        }
        # Determine if Permission is null or empty
        If ([String]::IsNullOrEmpty($this.Permission)) {
            # Permission is null or empty
            $this.Messages.Add("Permission cannot be empty or null")
            $Return = $false
        } Else {
            # Permission is not null or empty; determine if it does not match the permissions hashtable
            If (($this.Permissions.PSObject.Properties -Match $this.Permission) -eq $false) {
                # Permission does not appear in the hash table
                $this.Messages.Add("Permission does not exist")
                $Return = $false
            }
        }
        # Try to get the ACL for the OU
        Try {
            $this.Acl = Get-ACL ("AD:" + $this.ouDN) -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Unable to obtain ACL for " + $this.ouDN))
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: CreateChildUsers method
    Hidden [Boolean] CreateChildComputers() {
        # Control variable
        [Boolean] $Return = $true
        # Try to delegate
        Try {
            Add-DSACLCreateChild -TargetDN $this.ouDN -DelegateDN $this.identityDN -ObjectTypeName Computer -AccessType Allow -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error adding create child computers delegation. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: CreateChildContacts method
    Hidden [Boolean] CreateChildContacts() {
        # Control variable
        [Boolean] $Return = $true
        # Try to delegate
        Try {
            Add-DSACLCreateChild -TargetDN $this.ouDN -DelegateDN $this.identityDN -ObjectTypeName Contact -AccessType Allow -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error adding create child contacts delegation. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: CreateChildGroups method
    Hidden [Boolean] CreateChildGroups() {
        # Control variable
        [Boolean] $Return = $true
        # Try to delegate
        Try {
            Add-DSACLCreateChild -TargetDN $this.ouDN -DelegateDN $this.identityDN -ObjectTypeName Group -AccessType Allow -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error adding create child groups delegation. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: CreateChildUsers method
    Hidden [Boolean] CreateChildUsers() {
        # Control variable
        [Boolean] $Return = $true
        # Try to delegate
        Try {
            Add-DSACLCreateChild -TargetDN $this.ouDN -DelegateDN $this.identityDN -ObjectTypeName User -AccessType Allow -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error adding create child users delegation. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: CreateDeleteOUs method
    Hidden [Boolean] CreateOUs() {
        # Control variable
        [Boolean] $Return = $true
        # Try to delegate create OU
        Try {
            Add-DSACLCreateChild -TargetDN $this.ouDN -DelegateDN $this.identityDN -ObjectTypeGuid ($this.guidMap['OrganizationalUnit']) -AccessType Allow -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error adding create OU delegation. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: DeleteChildUsers method
    Hidden [Boolean] DeleteChildComputers() {
        # Control variable
        [Boolean] $Return = $true
        # Try to delegate
        Try {
            Add-DSACLDeleteChild -TargetDN $this.ouDN -DelegateDN $this.identityDN -ObjectTypeName Computer -AccessType Allow -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error adding delete child computers delegation. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: DeleteChildContacts method
    Hidden [Boolean] DeleteChildContacts() {
        # Control variable
        [Boolean] $Return = $true
        # Try to delegate
        Try {
            Add-DSACLDeleteChild -TargetDN $this.ouDN -DelegateDN $this.identityDN -ObjectTypeName Contact -AccessType Allow -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error adding delete child contacts delegation. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: DeleteChildGroups method
    Hidden [Boolean] DeleteChildGroups() {
        # Control variable
        [Boolean] $Return = $true
        # Try to delegate
        Try {
            Add-DSACLDeleteChild -TargetDN $this.ouDN -DelegateDN $this.identityDN -ObjectTypeName Group -AccessType Allow -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error adding delete child groups delegation. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: DeleteChildUsers method
    Hidden [Boolean] DeleteChildUsers() {
        # Control variable
        [Boolean] $Return = $true
        # Try to delegate
        Try {
            Add-DSACLDeleteChild -TargetDN $this.ouDN -DelegateDN $this.identityDN -ObjectTypeName User -AccessType Allow -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error adding delete child users delegation. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: CreateDeleteOUs method
    Hidden [Boolean] DeleteOUs() {
        # Control variable
        [Boolean] $Return = $true
        # Try to delegate delete OU
        Try {
            Add-DSACLDeleteChild -TargetDN $this.ouDN -DelegateDN $this.identityDN -ObjectTypeGuid ($this.guidMap['OrganizationalUnit']) -AccessType Allow -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error adding delete OU delegation. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: DomainJoinComputer method
    Hidden [Boolean] DomainJoinComputer() {
        # Control variable
        [Boolean] $Return = $true
        # Try to delegate
        Try {
            Add-DSACLJoinDomain -TargetDN $this.ouDN -DelegateDN $this.identityDN -AllowCreate -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error adding join domain delegation. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: EnableDisableComputers method
    Hidden [Boolean] EnableDisableComputers() {
        # Control variable
        [Boolean] $Return = $true
        # Try to delegate
        Try {
            # Add-DSACLWriteAccountRestrictions -TargetDN <String> -DelegateDN <String> -ObjectTypeName <String> -AccessType <AccessControlType> [-NoInheritance] [<CommonParameters>]
            Add-DSACLWriteAccountRestrictions -TargetDN $this.ouDN -DelegateDN $this.identityDN -ObjectTypeName Computer -AccessType Allow -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error adding enable disable users delegation. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: EnableDisableUsers method
    Hidden [Boolean] EnableDisableUsers() {
        # Control variable
        [Boolean] $Return = $true
        # Try to delegate
        Try {
            # Add-DSACLWriteAccountRestrictions -TargetDN <String> -DelegateDN <String> -ObjectTypeName <String> -AccessType <AccessControlType> [-NoInheritance] [<CommonParameters>]
            Add-DSACLWriteAccountRestrictions -TargetDN $this.ouDN -DelegateDN $this.identityDN -ObjectTypeName User -AccessType Allow -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error adding enable disable users delegation. Error message is " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: FullControlComputers method
    Hidden [Boolean] FullControlComputers() {
        # Control variable
        [Boolean] $Return = $true
        # Try to delegate
        Try {
            Add-DSACLFullControl -TargetDN $this.ouDN -DelegateDN $this.identityDN -ObjectTypeName Computer -AccessType Allow -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error adding full control computers delegation. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: FullControlContacts method
    Hidden [Boolean] FullControlContacts() {
        # Control variable
        [Boolean] $Return = $true
        # Try to delegate
        Try {
            Add-DSACLFullControl -TargetDN $this.ouDN -DelegateDN $this.identityDN -ObjectTypeName Contact -AccessType Allow -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error adding full control contacts delegation. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: FullControlGroup method
    Hidden [Boolean] FullControlGroups() {
        # Control variable
        [Boolean] $Return = $true
        # Try to delegate
        Try {
            Add-DSACLFullControl -TargetDN $this.ouDN -DelegateDN $this.identityDN -ObjectTypeName Group -AccessType Allow -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error adding full control groups delegation. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: FullControlUsers method
    Hidden [Boolean] FullControlUsers() {
        # Control variable
        [Boolean] $Return = $true
        # Try to delegate
        Try {
            Add-DSACLFullControl -TargetDN $this.ouDN -DelegateDN $this.identityDN -ObjectTypeName User -AccessType Allow -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error adding full control users delegation. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: FullControlUsers method
    Hidden [Boolean] FullControlOUs() {
        # Control variable
        [Boolean] $Return = $true
        # Try to delegate
        Try {
            Add-DSACLFullControl -TargetDN $this.ouDN -DelegateDN $this.identityDN -ObjectTypeGuid ($this.guidMap['OrganizationalUnit']) -AccessType Allow -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error adding full control organizational units delegation. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: LinkGPO method
    Hidden [Boolean] LinkGPO() {
        # Control variable
        [Boolean] $Return = $true
        # Try to delegate
        Try {
            Add-DSACLLinkGPO -TargetDN $this.ouDN -DelegateDN $this.identityDN -AccessType Allow -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error adding link GPO delegation. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: ModifyGroupMembership method
    Hidden [Boolean] ModifyGroupMembership() {
        # Control variable
        [Boolean] $Return = $true
        # Try to delegate
        Try {
            Add-DSACLManageGroupMember -TargetDN $this.ouDN -DelegateDN $this.identityDN -AccessType Allow -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error adding modify group membership delegation. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: ReadBitLockerRecovery method
    Hidden [Boolean] ReadBitLockerRecovery() {
        # Control variable
        [Boolean] $Return = $true
        # Set parameters
        [Hashtable] $Parameters = @{
            TargetDN              = $this.ouDN
            DelegatDN             = $this.identityDN
            ActiveDirectoryRights = [System.DirectoryServices.ActiveDirectoryRights]"ReadProperty,ExtendedRight"
            AccessControlType     = [System.Security.AccessControl.AccessControlType]"Allow"
            ObjectType            = [Guid]"00000000-0000-0000-0000-000000000000"
            InheritanceType       = [System.DirectoryServices.ActiveDirectorySecurityInheritance]"All"
            InheritedObjectType   = [Guid]$this.guidMap["msFVE-RecoveryInformation"]
        }
        # Try to delegate
        Try {
            Add-DSACLCustom @Parameters -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error adding read bitlocker recovery information delegation. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
    # METHODS: Reset password method
    Hidden [Boolean] ResetUserPasswords() {
        # Control variable
        [Boolean] $Return = $true
        # Try to delegate
        Try {
            Add-DSACLResetPassword -TargetDN $this.ouDN -DelegateDN $this.identityDN -ObjectTypeName User -AccessType Allow -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error adding reset password delegation. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
}

Class GPOPermissions {
    # PROPERTIES: Booleans
    [Boolean] $Success   = $false
    [Boolean] $Validates = $false
    # PROPERTIES: Strings
    [String] $Description = $null
    [String] $gpoName     = $null
    [String] $Group       = $null
    [String] $Permission  = $null
    # PROPERTIES: PSCustomObjects
    [PSCustomObject] $Permissions = [PSCustomObject]@{
        GpoRead  = "Allows reading a named GPO"
        GpoApply = "placeholder"
        GpoEdit  = "Allows editing a named GPO"
        GpoEditDeleteModifySecurity = "placeholder"
    }
    # PROPERTIES: String lists
    [System.Collections.Generic.List[String]] $Messages = @()
    # METHODS: Constructor method
    GPOPermissions($gpoName,$group,$permission) {
        # Set properties
        $this.gpoName     = $gpoName
        $this.Group       = $group
        $this.Permission  = $permission
        $this.Description = ($this.Permission + " for " + $Group + " on " + $gpoName)
        # Determine if the GPO permissions validate
        If ($this.ValidateGPOPermission() -eq $true) {
            # GPO permissions validate
            $this.Validates = $true
            $this.Success   = $this.SetGPOPermission()
        } Else {
            # GPO permissions do not validate
            $this.Messages.Add("GPO permission does not validate")
            $this.Validates = $false
        }
    }
    # METHODS: ValidateGPOPermission method
    Hidden [Boolean] ValidateGPOPermission() {
        # Control variable
        [Boolean] $Return = $true
        # Determine if gpoName is null or empty
        If ([String]::IsNullOrEmpty($this.gpoName)) {
            # gpoName is null or empty
            $this.Messages.Add("GpoName cannot be empty or null")
            $Return = $false
        } Else {
            # gpoName is not null or empty; try to get the GPO
            Try {
                Get-GPO -Name $this.gpoName -ErrorAction Stop
                # Success
            } Catch {
                # Failure
                $this.Messages.Add("GPO does not exist in AD")
                $Return = $false
            }
        }
        # Determine if Group is null or empty
        If ([String]::IsNullOrEmpty($this.Group)) {
            # Group is null or empty
            $this.Messages.Add("Group cannot be empty or null")
            $Return = $false
        } Else {
            # Group is not null or empty; try to get the group
            Try {
                Get-ADObject -Filter {SamAccountName -eq $this.Group} -ErrorAction Stop
                # Success
            } Catch {
                # Failure
                $this.Messages.Add("Group does not exist in AD")
                $Return = $false
            }
        }
        # Determine if Permission is null or empty
        If ([String]::IsNullOrEmpty($this.Permission)) {
            # Permission is null or empty
            $this.Messages.Add("Permission cannot be empty or null")
            $Return = $false
        } Else {
            # Permission is not null or empty; detemine if Permissions exists in the Permissions object
            If (($this.Permissions.PSObject.Properties -Match $this.Permission) -eq $false) {
                # Permission does not exist in Permisssions object
                $this.Messages.Add("Permission is not handled")
                $Return = $false
            }
        }
        # Return
        return $Return
    }
    # METHODS: GpoEdit method
    Hidden [Boolean] SetGPOPermission() {
        # Control variable
        [Boolean] $Return = $true
        # Try to set GPO permission
        Try {
            Set-GPPermission -Name $this.gpoName -TargetName $this.Group -TargetType Group -PermissionLevel $this.Permission -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add("Error setting GPO permission")
            $Return = $false
        }
        # Return
        return $Return
    }
}

Class DFSNRootPermissions {
    # PROPERTIES: Booleans
    [Boolean] $Success   = $false
    [Boolean] $Validates = $false
    # PROPERTIES: Strings
    [String] $Description = $null
    [String] $dfsnRoot    = $null
    [String] $Identity    = $null
    # PROPERTIES: String lists
    [System.Collections.Generic.List[String]] $Messages = @()
    # METHODS: Constructor method
    DFSNRootPermissions($dfsnRoot,$identity) {
        # Set properties
        $this.dfsnRoot = $dfsnRoot
        $this.Identity = $identity
        $this.Description = ("Permission for " + $this.Identity + " on " + $this.dfsnRoot)
        # Determine if the DFSN root permission validates
        If ($this.ValidateDFSNRootPermission() -eq $true) {
            # DFSN root permission validates
            $this.Validates = $true
            # Call SetDFSNRootPermission to set Success
            $this.Success = $this.SetDFSNRootPermission()
        } Else {
            # DFSN root permission does not validate
            $this.Messages.Add("DFSN root permission does not validate.")
            $this.Validates = $false
        }
    }
    # METHODS: ValidateDFSNPermission method
    Hidden [Boolean] ValidateDFSNRootPermission() {
        # Control variable
        [Boolean] $Return = $true
        # Determine if dfsnRoot is null or empty
        If ([String]::IsNullOrEmpty($this.dfsnRoot)) {
            # dfsnRoot is null or empty
            $this.Messages.Add("DFSNRoot cannot be empty or null")
            $Return = $false
        } Else {
            # dfsnRoot is not null or empty; try to get the DFSN root
            Try {
                Get-DfsnRoot -Path $this.dfsnRoot -ErrorAction Stop
                # Success
            } Catch {
                # Failure
                $this.Messages.Add(("DFSN root does not exist in DFS. Error message is: " + $_))
                $Return = $false
            }
        }
        # Determine if Identity is null or empty
        If ([String]::IsNullOrEmpty($this.Identity)) {
            # Identity is null or empty
            $this.Messages.Add("Identity cannot be empty or null")
            $Return = $false
        } Else {
            # Identity is not null or empty; try to get the object
            Try {
                Get-ADObject -Filter {SamAccountName -eq $this.Identity} -ErrorAction Stop
                # Success
            } Catch {
                # Failure
                $this.Messages.Add("Identity does not exist in AD")
                $Return = $false
            }
        }
        # Return
        return $Return
    }
    # METHODS: SetDFSNPermission
    Hidden [Boolean] SetDFSNRootPermission() {
        # Control variable
        [Boolean] $Return = $true
        # Try to set the DFSN root permission
        Try {
            Set-DfsnRoot -GrantAdminAccounts $this.Identity -Path $this.dfsnRoot -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Error setting DFSN root permission. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }   
}

Class DFSNFolderPermissions {
    # PROPERTIES: Booleans
    [Boolean] $Success   = $false
    [Boolean] $Validates = $false
    # PROPERTIES: Strings
    [String] $Description = $null
    [String] $dfsnFolder  = $null
    [String] $Identity    = $null
    # PROPERTIES: String lists
    [System.Collections.Generic.List[String]] $Messages = @()
    # METHODS: Constructor method
    DFSNFolderPermissions($dfsnFolder,$identity) {
        # Set properties
        $this.dfsnFolder  = $dfsnFolder
        $this.Identity    = $identity
        $this.Description = ("Permission for " + $this.Identity + " on " + $this.dfsnFolder)
        # Determine if the DFSN folder permission validates
        If ($this.ValidateDFSNFolderPermission() -eq $true) {
            # DFSN folder permission validates
            $this.Validates = $true
            # Call SetDFSNFolderPermissions to set Success
            $this.Success = $this.SetDFSNFolderPermission()
        } Else {
            # DFSN folder permission does not validate
            $this.Messages.Add(("DFSN folder permission " + $this.Description + " does not validate"))
            $this.Validates = $false
        }
    }
    # METHODS: ValidateDFSNPermission method
    Hidden [Boolean] ValidateDFSNFolderPermission() {
        # Control variable
        [Boolean] $Return = $true
        # Determine if dfsnFolder is null or empty
        If ([String]::IsNullOrEmpty($this.dfsnFolder)) {
            # dfsnFolder is null or ermpty
            $this.Messages.Add("DFSNFolder cannot be empty or null")
            $Return = $false
        } Else {
            # dfsnFolder is not null or empty; attempt to get the folder
            Try {
                Get-DfsnFolder -Path $this.dfsnFolder -ErrorAction Stop
                # Success
            } Catch {
                # Failure
                $this.Messages.Add(("DFSN folder does not exist in DFS. Error message is: " + $_))
                $Return = $false
            }
        }
        # Determine if Identity is null or empty
        If ([String]::IsNullOrEmpty($this.Identity)) {
            # Identity is null or empty
            $this.Messages.Add("Identity cannot be empty or null")
            $Return = $false
        } Else {
            # Identity is not null or empty
            Try {
                Get-ADObject -Filter {SamAccountName -eq $this.identity} -ErrorAction Stop
                # Success
            } Catch {
                # Failure
                $this.Messages.Add("Identity does not exist in AD")
                $Return = $false
            }
        }
        # Return
        return $Return
    }
    # METHODS: SetDFSNPermission
    Hidden [Boolean] SetDFSNFolderPermission() {
        # Control variable
        $Return = $true
        # Try to grant the DFSN folder permission
        Try {
            Grant-DfsnAccess -Path $this.dfsnFolder -AccountName $this.Identity -ErrorAction Stop
            # Success
        } Catch {
            $this.Messages.Add(("Error setting DFSN folder permission. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }   
}

Class DFSRPermissions {
    # PROPERTIES: Booleans
    [Boolean] $Success   = $false
    [Boolean] $Validates = $false
    # PROPERTIES: Strings
    [String] $Description = $null
    [String] $dfsrGroup   = $null
    [String] $Identity    = $null
    # PROPERTIES: String lists
    [System.Collections.Generic.List[String]] $Messages = @()
    # METHODS: Constructor method
    DFSRPermissions($dfsrGroup,$identity) {
        $this.dfsrGroup   = $dfsrGroup
        $this.identity    = $identity
        $this.Description = ("Permission for " + $this.Identity + " to " + $this.dfsrGroup)
        # Determine if the DFSR permission validates
        If ($this.ValidateDFSRPermission() -eq $true) {
            # DFSR permission validates
            $this.Validates = $true
            # Call SetDFSRPermission to set Success
            $this.Success = $this.SetDFSRPermission()
        } Else {
            # DFSR permission does not validate
            $this.Messages.Add("Permission does not validate")
            $this.Validates = $false
        }
    }
    # METHODS: ValidateDFSRPermission method
    Hidden [Boolean] ValidateDFSRPermission() {
        # Control variable
        [Boolean] $Return = $true
        # Determine if dfsrGroup is null or empty
        If ([String]::IsNullOrEmpty($this.dfsrGroup)) {
            # dfsrGroup is null or empty
            $this.Messages.Add("DFSRGroup cannot be empty or null")
            $Return = $false
        } Else {
            # dfsrGroup is not null or empty; try to get replication grpu
            Try {
                Get-DfsReplicationGroup -GroupName $this.dfsrGroup -ErrorAction Stop
                # Success
            } Catch {
                # Failure
                $this.Messages.Add("DFSR group does not exist in DFS")
                $Return = $false
            }
        }
        # Determine if Identity is null or empty
        If ([String]::IsNullOrEmpty($this.Identity)) {
            # Identity is null or empty
            $this.Messages.Add("Identity cannot be empty or null")
            $Return = $false
        } Else {
            # Identity is not null or empty; attempt to get the object
            Try {
                Get-ADObject -Filter {SamAccountName -eq $this.Identity} -ErrorAction Stop
                # Success
            } Catch {
                # Failure
                $this.Messages.Add("Identity does not exist in AD")
                $Return = $false
            }
        }
        return $Return
    }
    # METHODS: SetDFSRPermission method
    Hidden [Boolean] SetDFSRPermission() {
        # Control variable
        [Boolean] $Return = $true
        # Try to grant permission
        Try {
            Grant-DfsrDelegation -GroupName $this.dfsrGroup -AccountName $this.identity -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add("Error setting DFSR permission")
            $Return = $false
        }
        # Return
        return $Return
    }   
}

# MAIN
[Main]::new($Configuration,$OutDir) | Out-Null

# SIG # Begin signature block
# MIIvsAYJKoZIhvcNAQcCoIIvoTCCL50CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA7ZODJnk+7WSDZ
# 3vNSlBXbUJ9xM/jT4oUd5exAcZCgfqCCEgUwggVvMIIEV6ADAgECAhBI/JO0YFWU
# jTanyYqJ1pQWMA0GCSqGSIb3DQEBDAUAMHsxCzAJBgNVBAYTAkdCMRswGQYDVQQI
# DBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoM
# EUNvbW9kbyBDQSBMaW1pdGVkMSEwHwYDVQQDDBhBQUEgQ2VydGlmaWNhdGUgU2Vy
# dmljZXMwHhcNMjEwNTI1MDAwMDAwWhcNMjgxMjMxMjM1OTU5WjBWMQswCQYDVQQG
# EwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMS0wKwYDVQQDEyRTZWN0aWdv
# IFB1YmxpYyBDb2RlIFNpZ25pbmcgUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEBAQUA
# A4ICDwAwggIKAoICAQCN55QSIgQkdC7/FiMCkoq2rjaFrEfUI5ErPtx94jGgUW+s
# hJHjUoq14pbe0IdjJImK/+8Skzt9u7aKvb0Ffyeba2XTpQxpsbxJOZrxbW6q5KCD
# J9qaDStQ6Utbs7hkNqR+Sj2pcaths3OzPAsM79szV+W+NDfjlxtd/R8SPYIDdub7
# P2bSlDFp+m2zNKzBenjcklDyZMeqLQSrw2rq4C+np9xu1+j/2iGrQL+57g2extme
# me/G3h+pDHazJyCh1rr9gOcB0u/rgimVcI3/uxXP/tEPNqIuTzKQdEZrRzUTdwUz
# T2MuuC3hv2WnBGsY2HH6zAjybYmZELGt2z4s5KoYsMYHAXVn3m3pY2MeNn9pib6q
# RT5uWl+PoVvLnTCGMOgDs0DGDQ84zWeoU4j6uDBl+m/H5x2xg3RpPqzEaDux5mcz
# mrYI4IAFSEDu9oJkRqj1c7AGlfJsZZ+/VVscnFcax3hGfHCqlBuCF6yH6bbJDoEc
# QNYWFyn8XJwYK+pF9e+91WdPKF4F7pBMeufG9ND8+s0+MkYTIDaKBOq3qgdGnA2T
# OglmmVhcKaO5DKYwODzQRjY1fJy67sPV+Qp2+n4FG0DKkjXp1XrRtX8ArqmQqsV/
# AZwQsRb8zG4Y3G9i/qZQp7h7uJ0VP/4gDHXIIloTlRmQAOka1cKG8eOO7F/05QID
# AQABo4IBEjCCAQ4wHwYDVR0jBBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQwHQYD
# VR0OBBYEFDLrkpr/NZZILyhAQnAgNpFcF4XmMA4GA1UdDwEB/wQEAwIBhjAPBgNV
# HRMBAf8EBTADAQH/MBMGA1UdJQQMMAoGCCsGAQUFBwMDMBsGA1UdIAQUMBIwBgYE
# VR0gADAIBgZngQwBBAEwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21v
# ZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEE
# KDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZI
# hvcNAQEMBQADggEBABK/oe+LdJqYRLhpRrWrJAoMpIpnuDqBv0WKfVIHqI0fTiGF
# OaNrXi0ghr8QuK55O1PNtPvYRL4G2VxjZ9RAFodEhnIq1jIV9RKDwvnhXRFAZ/ZC
# J3LFI+ICOBpMIOLbAffNRk8monxmwFE2tokCVMf8WPtsAO7+mKYulaEMUykfb9gZ
# pk+e96wJ6l2CxouvgKe9gUhShDHaMuwV5KZMPWw5c9QLhTkg4IUaaOGnSDip0TYl
# d8GNGRbFiExmfS9jzpjoad+sPKhdnckcW67Y8y90z7h+9teDnRGWYpquRRPaf9xH
# +9/DUp/mBlXpnYzyOmJRvOwkDynUWICE5EV7WtgwggYaMIIEAqADAgECAhBiHW0M
# UgGeO5B5FSCJIRwKMA0GCSqGSIb3DQEBDAUAMFYxCzAJBgNVBAYTAkdCMRgwFgYD
# VQQKEw9TZWN0aWdvIExpbWl0ZWQxLTArBgNVBAMTJFNlY3RpZ28gUHVibGljIENv
# ZGUgU2lnbmluZyBSb290IFI0NjAeFw0yMTAzMjIwMDAwMDBaFw0zNjAzMjEyMzU5
# NTlaMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzAp
# BgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYwggGiMA0G
# CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCbK51T+jU/jmAGQ2rAz/V/9shTUxjI
# ztNsfvxYB5UXeWUzCxEeAEZGbEN4QMgCsJLZUKhWThj/yPqy0iSZhXkZ6Pg2A2NV
# DgFigOMYzB2OKhdqfWGVoYW3haT29PSTahYkwmMv0b/83nbeECbiMXhSOtbam+/3
# 6F09fy1tsB8je/RV0mIk8XL/tfCK6cPuYHE215wzrK0h1SWHTxPbPuYkRdkP05Zw
# mRmTnAO5/arnY83jeNzhP06ShdnRqtZlV59+8yv+KIhE5ILMqgOZYAENHNX9SJDm
# +qxp4VqpB3MV/h53yl41aHU5pledi9lCBbH9JeIkNFICiVHNkRmq4TpxtwfvjsUe
# dyz8rNyfQJy/aOs5b4s+ac7IH60B+Ja7TVM+EKv1WuTGwcLmoU3FpOFMbmPj8pz4
# 4MPZ1f9+YEQIQty/NQd/2yGgW+ufflcZ/ZE9o1M7a5Jnqf2i2/uMSWymR8r2oQBM
# dlyh2n5HirY4jKnFH/9gRvd+QOfdRrJZb1sCAwEAAaOCAWQwggFgMB8GA1UdIwQY
# MBaAFDLrkpr/NZZILyhAQnAgNpFcF4XmMB0GA1UdDgQWBBQPKssghyi47G9IritU
# pimqF6TNDDAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADATBgNV
# HSUEDDAKBggrBgEFBQcDAzAbBgNVHSAEFDASMAYGBFUdIAAwCAYGZ4EMAQQBMEsG
# A1UdHwREMEIwQKA+oDyGOmh0dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1
# YmxpY0NvZGVTaWduaW5nUm9vdFI0Ni5jcmwwewYIKwYBBQUHAQEEbzBtMEYGCCsG
# AQUFBzAChjpodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2Rl
# U2lnbmluZ1Jvb3RSNDYucDdjMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0
# aWdvLmNvbTANBgkqhkiG9w0BAQwFAAOCAgEABv+C4XdjNm57oRUgmxP/BP6YdURh
# w1aVcdGRP4Wh60BAscjW4HL9hcpkOTz5jUug2oeunbYAowbFC2AKK+cMcXIBD0Zd
# OaWTsyNyBBsMLHqafvIhrCymlaS98+QpoBCyKppP0OcxYEdU0hpsaqBBIZOtBajj
# cw5+w/KeFvPYfLF/ldYpmlG+vd0xqlqd099iChnyIMvY5HexjO2AmtsbpVn0OhNc
# WbWDRF/3sBp6fWXhz7DcML4iTAWS+MVXeNLj1lJziVKEoroGs9Mlizg0bUMbOalO
# hOfCipnx8CaLZeVme5yELg09Jlo8BMe80jO37PU8ejfkP9/uPak7VLwELKxAMcJs
# zkyeiaerlphwoKx1uHRzNyE6bxuSKcutisqmKL5OTunAvtONEoteSiabkPVSZ2z7
# 6mKnzAfZxCl/3dq3dUNw4rg3sTCggkHSRqTqlLMS7gjrhTqBmzu1L90Y1KWN/Y5J
# KdGvspbOrTfOXyXvmPL6E52z1NZJ6ctuMFBQZH3pwWvqURR8AgQdULUvrxjUYbHH
# j95Ejza63zdrEcxWLDX6xWls/GDnVNueKjWUH3fTv1Y8Wdho698YADR7TNx8X8z2
# Bev6SivBBOHY+uqiirZtg0y9ShQoPzmCcn63Syatatvx157YK9hlcPmVoa1oDE5/
# L9Uo2bC5a4CH2RwwggZwMIIE2KADAgECAhAbV4ef7ZvUwZ6AKRT0hWSAMA0GCSqG
# SIb3DQEBDAUAMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0
# ZWQxKzApBgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYw
# HhcNMjYwOTE0MDAwMDAwWhcNMjcwOTE0MjM1OTU5WjBiMQswCQYDVQQGEwJVUzER
# MA8GA1UECAwIQ29sb3JhZG8xHzAdBgNVBAoMFkxpZXZlcnR6IEFuZHJldyBIdWJl
# cnQxHzAdBgNVBAMMFkxpZXZlcnR6IEFuZHJldyBIdWJlcnQwggIiMA0GCSqGSIb3
# DQEBAQUAA4ICDwAwggIKAoICAQCZl/Kqmb1yMMbn0868VlBqxo7LGu/SDy7bO3wz
# D6jMoxzh2UT4iFxNnIEpHJq9SzxkhNgy60Lhzr0kGxBXypLJVGKP3JpS9inmF3zn
# e/DWJcOFkQPkwYAsqEoJ6xt/klQlJqnwaN7AJUwUIgZ/72j37//UcWxltCMxnwT2
# wVUQ357c3GOafCKfBMUN9dEgUhwxt4x5GZ6Re/WSGKlh+eFtP1wNBsBXoT6Hme5E
# u2L5vTYl0fmKVAsTGJ9k7eHvLE7IUWXOD6NI548uxFQ2FeISOdCRt0WmYmpWgi6S
# j1Aoh1zLhsMaLgUPP+BXcOy0cO3zDJ+SZky+S4AB37Y0UBqy9hNvX0WbfQh3BF+2
# uWYGxHYc3FtefKKAbhQGNo9EO4pB0SBh/19a/EbbUpv67CRlO8PmGf8hEk6ic8O6
# 28OjkFi5ByIB7y/XKPKCZmeS/TWDrkyMpu8h9NGqHyWONE5oxpqCYMm3a0L92kfd
# zY5e6vSLDuZZjlANRUm3VLnbxZcD8znUvaDbR+ALdJeSZryev8NVMnfOzwZM6vKz
# fTPbYpFRBo0owFluKK85CKeZ8z+NyKQfvWh5gYXZw0JFZwdE4SE46fnNLJ3z9loF
# Qly+7Dn2zUd/+CxwlRgkuEO1ng6EJ1OAAeA7PixyZob0gjg7ob7YzSK2v4AV/QdQ
# oXa04QIDAQABo4IBrjCCAaowHwYDVR0jBBgwFoAUDyrLIIcouOxvSK4rVKYpqhek
# zQwwHQYDVR0OBBYEFMiE5GpeP6V7hsDSdHBbSjojZscBMA4GA1UdDwEB/wQEAwIH
# gDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMEoGA1UdIARDMEEw
# NQYMKwYBBAGyMQECAQMCMCUwIwYIKwYBBQUHAgEWF2h0dHBzOi8vc2VjdGlnby5j
# b20vQ1BTMAgGBmeBDAEEATBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLnNl
# Y3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2RlU2lnbmluZ0NBUjM2LmNybDB5Bggr
# BgEFBQcBAQRtMGswRAYIKwYBBQUHMAKGOGh0dHA6Ly9jcnQuc2VjdGlnby5jb20v
# U2VjdGlnb1B1YmxpY0NvZGVTaWduaW5nQ0FSMzYuY3J0MCMGCCsGAQUFBzABhhdo
# dHRwOi8vb2NzcC5zZWN0aWdvLmNvbTAjBgNVHREEHDAagRhhbGlldmVydHpAb25l
# emVyb29uZS5kZXYwDQYJKoZIhvcNAQEMBQADggGBACvVVz+HNOH8p+CBcwmrYBVx
# mlWNkTz84gmJZ/57FF5XK4kHeo6+QFalQkBSkiDheZa13+FdwUYf1zcJu5hfymkZ
# 9zmBhLlIkdluT1llv9GKr/tQaJFd2NXoLiJ7ysqRDbu47U1RR7XGU42HZHLUHa00
# Ha8KDHKYeRcCBMhltjpBK4Hw5daTzT+5fLhMHaOy3ZuWn4Fgx8zEcH1/cdEqnWNn
# NxxCWbKcp4sRI0v9uStoUYGpvX8LkYtMUUuWPr7tZAAZiPws6ctOrdROmdEwfyah
# IMt2drMfTXZVPpL/5okl37qOYhzV+u6vEvbc3xeAicAgsgOYkvvx5L2iUyFfGdLb
# JN4ioPTCKcvN7oXFPwHfyGqwBxARyJy3so2OAFvY4AGY/9DwQVE8KZuplrkktC7Y
# MzjB9SjEtvqnyW+h0fHd3H85B8GJiL8slRmIb5f+jJDvmu5RgoaoaMAYeX/V4HXY
# gpSD92Hq5AwUjjUvj1WPk+AagPBupA+J4OM2S6eO0DGCHQEwghz9AgEBMGgwVDEL
# MAkGA1UEBhMCR0IxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDErMCkGA1UEAxMi
# U2VjdGlnbyBQdWJsaWMgQ29kZSBTaWduaW5nIENBIFIzNgIQG1eHn+2b1MGegCkU
# 9IVkgDANBglghkgBZQMEAgEFAKB8MBAGCisGAQQBgjcCAQwxAjAAMBkGCSqGSIb3
# DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEV
# MC8GCSqGSIb3DQEJBDEiBCCw468cKCOLy6mEp6Oy8Qg7kGhbhj/L2LsLq31z2YEe
# YDANBgkqhkiG9w0BAQEFAASCAgBzV7aeiOhQw4RRc2rCfUTt552xUMJxruyMVTdD
# U5h0ik+0zq57G3KUTRZfdxiKr7qPoZfqkVVilGWQoo1CtzzzPSykxBB1Vhk7QaiG
# pYa4VjJgXiYTwg5b8b0hYbVNItNaY8emU7gc0laGUN14urnJdJs4H8vK2ptJnzjX
# cxXtvmAP2dTesQoFAzmWuJg8GkuzFL/F7WA8P5shIrrMteBqUyqLuIf04P9AnbtI
# iEqEVJgWoMOM9ZCxc+t/LZ1IO7yHqrkQNtvDU5VHwjBdG9Ygsm60TmGyq3ZTl3jJ
# syL39AJfr6c7IZzrh00Vsk8TKpffib9FEoGsPIvIumvS3Pbtp1OlTLoMgOH6COYs
# fu03irTUtNB1Id67qutgWfSKYPjQ7/BYjIY+JGlUIVHquJaRNnsQqGHBcUjnMLh5
# yUJEOtj0FWnVmWmgGw6oR6fOYjBsWe+S82F3Pbda5xYkXkMsexdrTmicJRU8h9/W
# U1YfkjmQqOCIY24LSHg0P8o1X9b1bEZHM4EPra8sJDsl2r0Cuy6rnn+OvELydX9c
# 7ORG/qYuwIznYsLCj1/Y6G6R182buGa7/KWy70BxJPMee9kj/C+w8X17xerkIrXt
# aPzNxLP4fEQX4IFqIYw/I0qbYg6uOOzQPsMgDb3EaRlY7bW8coL6q7ehB5jP7jRG
# 8bTvmKGCGewwghnoBgorBgEEAYI3AwMBMYIZ2DCCGdQGCSqGSIb3DQEHAqCCGcUw
# ghnBAgEDMQ8wDQYJYIZIAWUDBAICBQAwgfgGCyqGSIb3DQEJEAEEoIHoBIHlMIHi
# AgEBBgorBgEEAbIxAgEBMDEwDQYJYIZIAWUDBAIBBQAEIDKA8DJs3BpUwOpV4Gun
# LoArmrQWqx/88zOMHph8xxkPAhUAsL56E6t9pJwwayQT1dzXF85Dlo0YDzIwMjYw
# OTI4MDc0NzM2WqB2pHQwcjELMAkGA1UEBhMCR0IxFzAVBgNVBAgTDkdyZWF0ZXIg
# TG9uZG9uMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxMDAuBgNVBAMTJ1NlY3Rp
# Z28gUHVibGljIFRpbWUgU3RhbXBpbmcgU2lnbmVyIFIzN6CCFBcwggbiMIIEyqAD
# AgECAhEA507yVbBQT/rbpt/3/IujFTANBgkqhkiG9w0BAQwFADBVMQswCQYDVQQG
# EwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSwwKgYDVQQDEyNTZWN0aWdv
# IFB1YmxpYyBUaW1lIFN0YW1waW5nIENBIFI0MTAeFw0yNjAzMjUwMDAwMDBaFw0z
# NzA2MjQyMzU5NTlaMHIxCzAJBgNVBAYTAkdCMRcwFQYDVQQIEw5HcmVhdGVyIExv
# bmRvbjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTAwLgYDVQQDEydTZWN0aWdv
# IFB1YmxpYyBUaW1lIFN0YW1waW5nIFNpZ25lciBSMzcwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQCy/8NtS9xQ2UUtBRF32bj7VK3n4m50Uqjk/zTciSzi
# YV40H1LKah0/oEklYG42E4VCP3DvsBUB6DmpCkDZ0jCnZBPIEevaH15ZJOQwFWP2
# ZXr5YjlJpb68Nlbs+ElNvKx32/1YHde3qqUSLybjulxPLz6T85+HOIqK7M1Bep8L
# spyhEP/q6nw5kGxTSrGvufmeH+JF8CnVBcVMFA40FlIYh0cDJVFhhfTfdWgLy/vW
# uLMQoKkf3s/FvByf16r0rtbyHm/iemwxSioJL9zyZDDKUNAbHXl0dhXo2VxUV2Nc
# PXWXuoKsjL+6cfk6Vm2DHnxAlFdFsaBDIF1JOkSnC6PeLlBznZn2buF3vIIYJcq6
# N/zeFRCk4/HXDz7zgRsRRMdUB+rhyk5FoZaBjw0nLq3GZ3fClLUx5es5pUAxzNOD
# MBn7JkFYip2BAGBPER5eV0ROhk6tGTG+fUiMiV+vgjg1YnP5FvnYWyEtWeQD/B2h
# p3vz0RvtdkM0p3igyadzrfpOBq5ppVk/YsuhTQkP99ivneHAGfi5e7lmxJ+meoBP
# rRLuzMmb81rzzbESjJHMsn5RVtc6Ucs7rcMqQC13PUIO7BbGBETV2ufCmV6lPTp3
# P7XJOvmnUCRTPbVvMTpxP/z+SOHg4/OCBhiqs4FA9+4oQvlkk9w32NGASli9GWrm
# 5wIDAQABo4IBjjCCAYowHwYDVR0jBBgwFoAUOnSlDGfGQlDC/bX8x7spNIL0erkw
# HQYDVR0OBBYEFGEQ6XoSr1HEhdTyz6R0D1DNIK/4MA4GA1UdDwEB/wQEAwIGwDAM
# BgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMEoGA1UdIARDMEEw
# CAYGZ4EMAQQCMDUGDCsGAQQBsjEBAgEDCDAlMCMGCCsGAQUFBwIBFhdodHRwczov
# L3NlY3RpZ28uY29tL0NQUzBKBgNVHR8EQzBBMD+gPaA7hjlodHRwOi8vY3JsLnNl
# Y3RpZ28uY29tL1NlY3RpZ29QdWJsaWNUaW1lU3RhbXBpbmdDQVI0MS5jcmwwegYI
# KwYBBQUHAQEEbjBsMEUGCCsGAQUFBzAChjlodHRwOi8vY3J0LnNlY3RpZ28uY29t
# L1NlY3RpZ29QdWJsaWNUaW1lU3RhbXBpbmdDQVI0MS5jcnQwIwYIKwYBBQUHMAGG
# F2h0dHA6Ly9vY3NwLnNlY3RpZ28uY29tMA0GCSqGSIb3DQEBDAUAA4ICAQAD6j2N
# 0azN+hl6k6bKB5/U6VuSOs93ZBb3Pczy9VtBIKu4947Z5GwL0aFngIxl+GSuLFrJ
# gPruBCRvKJEJsm7kv+LQ1COVCEG9tZ+IRtr4ocUoa53lgdFaENlS0N4wgkZkbQEP
# v+x+1lSjYh+T4JeL9mUznT7Erc6Sp5dWLka5sMP/m3GZi6oJPdPcsCKWagH7m2H2
# xDGIyHJC5PdH9phvi/KmhkktiSVTNNqVeV5bWdX2zhRE6UTfz0IcMoCL996lFIyd
# XxOCE4MNDHDM0as4lnTiT/KHMccO6l8c9TnUVgmpci9ar1IABZ2U1XUkYjGGSn9M
# C3EHDP9V39VuBVvZ33/BEV/EWSRrf07T7jFplKX+gQr/UOqPGMlE7ZJ72UaUkNJy
# 7bVl3bcLKzdpjIHzLkf/4MVa1V7w8wqCv5W4gOnRGTlud5UMARbRM8BPxR/CXYXo
# MmIOD8pmTk2axgRL4LG8XtuchISdCHRmtacAmLGq5XSYSVTHTXADlO48iDKh3HM2
# r98LSF6f0sG12d8V9Jn7C3wDUieOxuKj4MdWrW+hiJU2kF87v6eH00HgCFFc2V0+
# CvfOCMn7juzS41jLaINcBlKWQ/fKb/uDLfWOW73z1I2lFY7Xj8tQ1XYtK5eREjWI
# tM8jpl1cbQOc88btR+0XS2TmboE/141+va2PWzCCBqcwggSPoAMCAQICEQCQrAhy
# IP3Fp8RrXMcN9z0GMA0GCSqGSIb3DQEBDAUAMFcxCzAJBgNVBAYTAkdCMRgwFgYD
# VQQKEw9TZWN0aWdvIExpbWl0ZWQxLjAsBgNVBAMTJVNlY3RpZ28gUHVibGljIFRp
# bWUgU3RhbXBpbmcgUm9vdCBSNDYwHhcNMjYwMzI1MDAwMDAwWhcNNDEwMzI0MjM1
# OTU5WjBVMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSww
# KgYDVQQDEyNTZWN0aWdvIFB1YmxpYyBUaW1lIFN0YW1waW5nIENBIFI0MTCCAiIw
# DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK7kSqIBrYIcYvlmLVuaA8zw1RfB
# hkn4G1CoemzjcYtML6yNUvKmwGH7y6/5MuSC1UYP/+9KYDSqvMQt/1hEKHYxMAD9
# oZpBkoaDQFEKbOJHelsKe+BaO0ZcENTKfePcraVkA7wrGAW2XHA5gQCQv4IKori/
# 3PNOXxnDMOk8yIMgVrlMeTxqfWJ4XkjT1xc2s9DD7URHWWJOFobTPoWs6mrDFlaY
# 9FlAHDYTfbzvxQHVsvRmn3W+5ZmCwyk02I8KgGPT/UX4sTz41GiR+ppwUjQXa1+2
# tEHZbsdAKUtH3OPEVtZvlt7atx4h83IdRR8oYi8wjY3OjFKXFecWpQbzzsPxbUKP
# wMWiTrzwkrFa8dH/1pDKRJt371W62PfqKPayCr/XbnBOlRn8CALSmHnRtGzuAWtT
# JpcT3BKw6oy8IIL6wSbu938F6ZIbRNIc1dKbIJtr4ULN6R5ZfTdNEhwXctqp3RHD
# bg4fuOl6LjNoaFwjud92EEDhzxFJzE1jqN4csceZIwxOT1aqfsfh0uFQE/lgTBuB
# s3i6/WL2W1OceWLy3XEdXRK1f0EWCuea6dNfX2RRdjUfk5EltFnJkN2+bWhnK14O
# PRKcyjOv5hKZ0iV4NRNd1+hjtva1rPyzb5Bs7EvFxqEQhgZbOq7qH3nm0rBwA0dx
# niBOYCFPdu246JCxAgMBAAGjggFuMIIBajAfBgNVHSMEGDAWgBT2d2rdP/0BE/8W
# oWyCAi/QCj0UJTAdBgNVHQ4EFgQUOnSlDGfGQlDC/bX8x7spNIL0erkwDgYDVR0P
# AQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwEwYDVR0lBAwwCgYIKwYBBQUH
# AwgwIwYDVR0gBBwwGjAIBgZngQwBBAIwDgYMKwYBBAGyMQECAQMIMEwGA1UdHwRF
# MEMwQaA/oD2GO2h0dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1YmxpY1Rp
# bWVTdGFtcGluZ1Jvb3RSNDYuY3JsMHwGCCsGAQUFBwEBBHAwbjBHBggrBgEFBQcw
# AoY7aHR0cDovL2NydC5zZWN0aWdvLmNvbS9TZWN0aWdvUHVibGljVGltZVN0YW1w
# aW5nUm9vdFI0Ni5wN2MwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNlY3RpZ28u
# Y29tMA0GCSqGSIb3DQEBDAUAA4ICAQAy3lJHZvGeA2b43yhzoarvobHVzbfl+Rfu
# PDwej0wCQkYAN6scTt2GwFe22qbOCv/tllqFlLKQZE+E9jVyuPTbyQHwrM7R0oLa
# pAEDC1+CowsqSRf/ptira5Pfd4PoHICnb9coPQtyZmHSQp5y9IGvqWf1qNfq7V2f
# HZ8DvEQrLUzeoGF9BJRYu2OzacW3QQtUum3NOVf0gPRwv6I4991uhncJ6VP4lcpU
# pHZKB7R3hiIUC09mR9KjzPVnXHvL9n2bAwiUECfK5Zezhiw27F2tgi39DETfU8M4
# n0N6xLgFzsf05M5GURX8C9+IX9V6kpmmKtrUzMti4LD66gtmf+mSm934K81NL6YQ
# eMEk1rpYrWPypcW76Mir6wb1AgseLIHqn/GkeuQm7zOTDf3f5WoX14qVNjZWNHF3
# JxkutV6ZnhinfCLfdv5bnwKWUfceqOajCVntI6uCbHxjBg6SCsexc5AfIGno7gVF
# vwifT4XONPsSUaJ71XsJ+EvciVUVnjOO4qxm0fWJTd8a7jP8mc4ZPqwJvQFtOp7+
# 6G+kUJAF0fnE8YgD8uttBReNTa1YmAeFMiqc38e8fI4eLm0zjM/eeGCHasnoqqrb
# GwcF41iz9HXzFDwN4iD5z3QShp6HRiU3UpTwDJiiXcr0z6pjl7PyzJ3/tmWtGehV
# 7CAfc/WlyzCCBoIwggRqoAMCAQICEDbCsL18Gzrno7PdNsvJdWgwDQYJKoZIhvcN
# AQEMBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5MRQwEgYD
# VQQHEwtKZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3Jr
# MS4wLAYDVQQDEyVVU0VSVHJ1c3QgUlNBIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
# MB4XDTIxMDMyMjAwMDAwMFoXDTM4MDExODIzNTk1OVowVzELMAkGA1UEBhMCR0Ix
# GDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDEuMCwGA1UEAxMlU2VjdGlnbyBQdWJs
# aWMgVGltZSBTdGFtcGluZyBSb290IFI0NjCCAiIwDQYJKoZIhvcNAQEBBQADggIP
# ADCCAgoCggIBAIid2LlFZ50d3ei5JoGaVFTAfEkFm8xaFQ/ZlBBEtEFAgXcUmanU
# 5HYsyAhTXiDQkiUvpVdYqZ1uYoZEMgtHES1l1Cc6HaqZzEbOOp6YiTx63ywTon43
# 4aXVydmhx7Dx4IBrAou7hNGsKioIBPy5GMN7KmgYmuu4f92sKKjbxqohUSfjk1mJ
# lAjthgF7Hjx4vvyVDQGsd5KarLW5d73E3ThobSkob2SL48LpUR/O627pDchxll+b
# TSv1gASn/hp6IuHJorEu6EopoB1CNFp/+HpTXeNARXUmdRMKbnXWflq+/g36NJXB
# 35ZvxQw6zid61qmrlD/IbKJA6COw/8lFSPQwBP1ityZdwuCysCKZ9ZjczMqbUcLF
# yq6KdOpuzVDR3ZUwxDKL1wCAxgL2Mpz7eZbrb/JWXiOcNzDpQsmwGQ6Stw8tTCqP
# umhLRPb7YkzM8/6NnWH3T9ClmcGSF22LEyJYNWCHrQqYubNeKolzqUbCqhSqmr/U
# dUeb49zYHr7ALL8bAJyPDmubNqMtuaobKASBqP84uhqcRY/pjnYd+V5/dcu9ieER
# jiRKKsxCG1t6tG9oj7liwPddXEcYGOUiWLm742st50jGwTzxbMpepmOP1mLnJskv
# ZaN5e45NuzAHteORlsSuDt5t4BBRCJL+5EZnnw0ezntk9R8QJyAkL6/bAgMBAAGj
# ggEWMIIBEjAfBgNVHSMEGDAWgBRTeb9aqitKz1SA4dibwJ3ysgNmyzAdBgNVHQ4E
# FgQU9ndq3T/9ARP/FqFsggIv0Ao9FCUwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB
# /wQFMAMBAf8wEwYDVR0lBAwwCgYIKwYBBQUHAwgwEQYDVR0gBAowCDAGBgRVHSAA
# MFAGA1UdHwRJMEcwRaBDoEGGP2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VU0VS
# VHJ1c3RSU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDA1BggrBgEFBQcBAQQp
# MCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZI
# hvcNAQEMBQADggIBAA6+ZUHtaES45aHF1BGH5Lc7JYzrftrIF5Ht2PFDxKKFOct/
# awAEWgHQMVHol9ZLSyd/pYMbaC0IZ+XBW9xhdkkmUV/KbUOiL7g98M/yzRyqUOZ1
# /IY7Ay0YbMniIibJrPcgFp73WDnRDKtVutShPSZQZAdtFwXnuiWl8eFARK3PmLqE
# m9UsVX+55DbVIz33Mbhba0HUTEYv3yJ1fwKGxPBsP/MgTECimh7eXomvMm0/GPxX
# 2uhwCcs/YLxDnBdVVlxvDjHjO1cuwbOpkiJGHmLXXVNbsdXUC2xBrq9fLrfe8IBs
# A4hopwsCj8hTuwKXJlSTrZcPRVSccP5i9U28gZ7OMzoJGlxZ5384OKm0r568Mo9T
# YrqzKeKZgFo0fj2/0iHbj55hc20jfxvK3mQi+H7xpbzxZOFGm/yVQkpo+ffv5gdh
# p+hv1GDsvJOtJinJmgGbBFZIThbqI+MHvAmMmkfb3fTxmSkop2mSJL1Y2x/955S2
# 9Gu0gSJIkc3z30vU/iXrMpWx2tS7UVfVP+5tKuzGtgkP7d/doqDrLF1u6Ci3TpjA
# ZdeLLlRQZm867eVeXED58LXd1Dk6UvaAhvmWYXoiLz4JA5gPBcz7J311uahxCweN
# xE+xxxR3kT0WKzASo5G/PyDez6NHdIUKBeE3jDPs2ACc6CkJ1Sji4PKWVT0/MYIE
# kzCCBI8CAQEwajBVMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1p
# dGVkMSwwKgYDVQQDEyNTZWN0aWdvIFB1YmxpYyBUaW1lIFN0YW1waW5nIENBIFI0
# MQIRAOdO8lWwUE/626bf9/yLoxUwDQYJYIZIAWUDBAICBQCgggH6MBoGCSqGSIb3
# DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcNMjYwOTI4MDc0NzM2
# WjA/BgkqhkiG9w0BCQQxMgQwvaGd++A4wwQdlwfICZCa87vMXrYaFtiO0yxWvbn8
# nudYJTXzKtF6D2elEwjkkBB6MIIBewYLKoZIhvcNAQkQAgwxggFqMIIBZjCCAWIw
# FgQU6XgYqSjaFQqf4b+czHqruaAO7qwwgYgEFGXDKGlvfU5QLP0Dx8IGlxjK+/dP
# MHAwW6RZMFcxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQx
# LjAsBgNVBAMTJVNlY3RpZ28gUHVibGljIFRpbWUgU3RhbXBpbmcgUm9vdCBSNDYC
# EQCQrAhyIP3Fp8RrXMcN9z0GMIG8BBSFPWMtk4KCYXzQkDXEkd6SwULaxzCBozCB
# jqSBizCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNV
# BAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsx
# LjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkC
# EDbCsL18Gzrno7PdNsvJdWgwDQYJKoZIhvcNAQEBBQAEggIAQErwtb7lqEDE2RV3
# FVyXA/yRr79Myi0JJsXUC9T52xX7Tjp3J/aJKOUYFQjePZQusgxURLJ1ITY9SfW1
# 4CATstLozEzGQT+vdB1qOL2TQWLWcRlAUoiBgSV8hIH1xnYv2mn5cFeTiiIK21P0
# zJXVU0kZbTm5qzM2heCTtAGXx1Q39SvI2HMMvwGbHOt4R4LicAplp8wd26pkNXif
# 66xwzlrW2a6ReOd1i4AeC1e9sVErG6pByCiLdP6ilPpHGHcI2x1e9n84QzkIsb60
# gKgdtZni2/wwfiV0WMDv/t3q2Cc35/QO5TnNK3YcNALHlxEden8tqseppBO7sl/z
# IN7WEyQcTBQzHXP1MsYpAFyh0pK3eN1IjoI+Ydm4+QkKXOlqLFbRDTjctqBsLQWC
# oGKn4Szg7ppQQn4O3+rg+wxX0v9VHg9aQ7CJIapwCuGW8D5qClWemFWqi6KonWeF
# 9k4xA6Oqr2tlNVT1JO4IGHKd+hWasJAXHNXVC1VSLKFAdzhbcNO4sVnZUz5LXeQx
# cq3TjJfv4YPUAYj0FU9D9B2X0AkaJ9vvPr9ik8PDedYSqRlIA+f9/e4U3r8xcXZj
# 43sVGRqTOIDYxJcLzF3Buy3YVDLt8KD+rBGmdX87xeeLCr19bBBuFUEJVNgntG8R
# TaxFg3j8tUcGkpTR2Mcjw2lZYbI=
# SIG # End signature block