Graphimo.psm1

function Join-UriQuery {
    <#
    .SYNOPSIS
    Provides ability to join two Url paths together including advanced querying
 
    .DESCRIPTION
    Provides ability to join two Url paths together including advanced querying which is useful for RestAPI/GraphApi calls
 
    .PARAMETER BaseUri
    Primary Url to merge
 
    .PARAMETER RelativeOrAbsoluteUri
    Additional path to merge with primary url (optional)
 
    .PARAMETER QueryParameter
    Parameters and their values in form of hashtable
 
    .PARAMETER EscapeUriString
    If set, will escape the url string
 
    .EXAMPLE
    Join-UriQuery -BaseUri 'https://evotec.xyz/' -RelativeOrAbsoluteUri '/wp-json/wp/v2/posts' -QueryParameter @{
        page = 1
        per_page = 20
        search = 'SearchString'
    }
 
    .EXAMPLE
    Join-UriQuery -BaseUri 'https://evotec.xyz/wp-json/wp/v2/posts' -QueryParameter @{
        page = 1
        per_page = 20
        search = 'SearchString'
    }
 
    .EXAMPLE
    Join-UriQuery -BaseUri 'https://evotec.xyz' -RelativeOrAbsoluteUri '/wp-json/wp/v2/posts'
 
    .NOTES
    General notes
    #>

    [alias('Join-UrlQuery')]
    [CmdletBinding()]
    param (
        [parameter(Mandatory)][uri] $BaseUri,
        [parameter(Mandatory = $false)][uri] $RelativeOrAbsoluteUri,
        [Parameter()][System.Collections.IDictionary] $QueryParameter,
        [alias('EscapeUrlString')][switch] $EscapeUriString
    )
    begin {
        Add-Type -AssemblyName System.Web
    }
    process {

        if ($BaseUri -and $RelativeOrAbsoluteUri) {
            $Url = Join-Uri -BaseUri $BaseUri -RelativeOrAbsoluteUri $RelativeOrAbsoluteUri
        }
        else {
            $Url = $BaseUri
        }

        if ($QueryParameter) {
            $Collection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty)
            foreach ($key in $QueryParameter.Keys) {
                $Collection.Add($key, $QueryParameter.$key)
            }
        }

        $uriRequest = [System.UriBuilder] $Url
        if ($Collection) {
            $uriRequest.Query = $Collection.ToString()
        }
        if (-not $EscapeUriString) {
            $uriRequest.Uri.AbsoluteUri
        }
        else {
            [System.Uri]::EscapeUriString($uriRequest.Uri.AbsoluteUri)
        }
    }
}
function Remove-EmptyValue {
    <#
    .SYNOPSIS
    Removes empty values from a hashtable recursively.
 
    .DESCRIPTION
    This function removes empty values from a given hashtable. It can be used to clean up a hashtable by removing keys with null, empty string, empty array, or empty dictionary values. The function supports recursive removal of empty values.
 
    .PARAMETER Hashtable
    The hashtable from which empty values will be removed.
 
    .PARAMETER ExcludeParameter
    An array of keys to exclude from the removal process.
 
    .PARAMETER Recursive
    Indicates whether to recursively remove empty values from nested hashtables.
 
    .PARAMETER Rerun
    Specifies the number of times to rerun the removal process recursively.
 
    .PARAMETER DoNotRemoveNull
    If specified, null values will not be removed.
 
    .PARAMETER DoNotRemoveEmpty
    If specified, empty string values will not be removed.
 
    .PARAMETER DoNotRemoveEmptyArray
    If specified, empty array values will not be removed.
 
    .PARAMETER DoNotRemoveEmptyDictionary
    If specified, empty dictionary values will not be removed.
 
    .EXAMPLE
    $hashtable = @{
        'Key1' = '';
        'Key2' = $null;
        'Key3' = @();
        'Key4' = @{}
    }
    Remove-EmptyValue -Hashtable $hashtable -Recursive
 
    Description
    -----------
    This example removes empty values from the $hashtable recursively.
 
    .EXAMPLE
    $SplatDictionary = [ordered] @{
        Test = $NotExistingParameter
        Test1 = 'Existing Entry'
        Test2 = $null
        Test3 = ''
        Test5 = 0
        Test6 = 6
        Test7 = @{}
    }
 
    Remove-EmptyValue -Splat $SplatDictionary -Recursive -ExcludeParameter 'Test7'
 
    $SplatDictionary
 
    #>

    [alias('Remove-EmptyValues')]
    [CmdletBinding()]
    param(
        [alias('Splat', 'IDictionary')][Parameter(Mandatory)][System.Collections.IDictionary] $Hashtable,
        [string[]] $ExcludeParameter,
        [switch] $Recursive,
        [int] $Rerun,
        [switch] $DoNotRemoveNull,
        [switch] $DoNotRemoveEmpty,
        [switch] $DoNotRemoveEmptyArray,
        [switch] $DoNotRemoveEmptyDictionary
    )
    foreach ($Key in [string[]] $Hashtable.Keys) {
        if ($Key -notin $ExcludeParameter) {
            if ($Recursive) {
                if ($Hashtable[$Key] -is [System.Collections.IDictionary]) {
                    if ($Hashtable[$Key].Count -eq 0) {
                        if (-not $DoNotRemoveEmptyDictionary) {
                            $Hashtable.Remove($Key)
                        }
                    }
                    else {
                        Remove-EmptyValue -Hashtable $Hashtable[$Key] -Recursive:$Recursive -ExcludeParameter $ExcludeParameter -DoNotRemoveNull:$DoNotRemoveNull -DoNotRemoveEmpty:$DoNotRemoveEmpty -DoNotRemoveEmptyArray:$DoNotRemoveEmptyArray -DoNotRemoveEmptyDictionary:$DoNotRemoveEmptyDictionary
                    }
                }
                else {
                    if (-not $DoNotRemoveNull -and $null -eq $Hashtable[$Key]) {
                        $Hashtable.Remove($Key)
                    }
                    elseif (-not $DoNotRemoveEmpty -and $Hashtable[$Key] -is [string] -and $Hashtable[$Key] -eq '') {
                        $Hashtable.Remove($Key)
                    }
                    elseif (-not $DoNotRemoveEmptyArray -and $Hashtable[$Key] -is [System.Collections.IList] -and $Hashtable[$Key].Count -eq 0) {
                        $Hashtable.Remove($Key)
                    }
                }
            }
            else {
                if (-not $DoNotRemoveNull -and $null -eq $Hashtable[$Key]) {
                    $Hashtable.Remove($Key)
                }
                elseif (-not $DoNotRemoveEmpty -and $Hashtable[$Key] -is [string] -and $Hashtable[$Key] -eq '') {
                    $Hashtable.Remove($Key)
                }
                elseif (-not $DoNotRemoveEmptyArray -and $Hashtable[$Key] -is [System.Collections.IList] -and $Hashtable[$Key].Count -eq 0) {
                    $Hashtable.Remove($Key)
                }
            }
        }
    }
    if ($Rerun) {
        for ($i = 0; $i -lt $Rerun; $i++) {
            Remove-EmptyValue -Hashtable $Hashtable -Recursive:$Recursive -ExcludeParameter $ExcludeParameter -DoNotRemoveNull:$DoNotRemoveNull -DoNotRemoveEmpty:$DoNotRemoveEmpty -DoNotRemoveEmptyArray:$DoNotRemoveEmptyArray -DoNotRemoveEmptyDictionary:$DoNotRemoveEmptyDictionary
        }
    }
}
function Join-Uri {
    <#
    .SYNOPSIS
    Provides ability to join two Url paths together
 
    .DESCRIPTION
    Provides ability to join two Url paths together
 
    .PARAMETER BaseUri
    Primary Url to merge
 
    .PARAMETER RelativeOrAbsoluteUri
    Additional path to merge with primary url
 
    .EXAMPLE
    Join-Uri 'https://evotec.xyz/' '/wp-json/wp/v2/posts'
 
    .EXAMPLE
    Join-Uri 'https://evotec.xyz/' 'wp-json/wp/v2/posts'
 
    .EXAMPLE
    Join-Uri -BaseUri 'https://evotec.xyz/' -RelativeOrAbsoluteUri '/wp-json/wp/v2/posts'
 
    .EXAMPLE
    Join-Uri -BaseUri 'https://evotec.xyz/test/' -RelativeOrAbsoluteUri '/wp-json/wp/v2/posts'
 
    .NOTES
    General notes
    #>

    [alias('Join-Url')]
    [cmdletBinding()]
    param(
        [parameter(Mandatory)][uri] $BaseUri,
        [parameter(Mandatory)][uri] $RelativeOrAbsoluteUri
    )

    return ($BaseUri.OriginalString.TrimEnd('/') + "/" + $RelativeOrAbsoluteUri.OriginalString.TrimStart('/'))
}

# PowerForge merged source begin 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96
function Connect-MsalToken {
    [cmdletBinding()]
    param(
        [System.Collections.IDictionary] $MsalTokenSplat,
        [System.Collections.IDictionary] $Authorization,
        [int] $ExpiresTimeout = 30,
        [switch] $ForceRefesh
    )
    if (-not $Script:AuthorizationCache) {
        $Script:AuthorizationCache = [ordered] @{}
    }
    if ($Authorization.Splat) {
        $ApplicationID = $Authorization.Splat.ClientId
        if ($Script:AuthorizationCache[$ApplicationID] -and -not $ForceRefesh) {
            if ($Script:AuthorizationCache[$ApplicationID].MsalToken.ExpiresOn.UtcDateTime -gt ([datetime]::UtcNow).AddSeconds($ExpiresTimeout)) {
                Write-Verbose "Connect-MsalToken - Using cache for $ApplicationID"
                return $Script:AuthorizationCache[$ApplicationID]
            }
        }
        $Splat = $Authorization.Splat
        try {
            $MsalToken = Get-MsalToken @Splat -ErrorAction Stop
        }
        catch {
            Write-Warning -Message "Connect-MsalToken - Couldn't execute Get-MsalToken. Error: $($_.Exception.Message)"
            return
        }
        $Script:AuthorizationCache[$ApplicationID] = [ordered] @{
            'MsalToken' = $MsalToken
            'Splat'     = $Authorization.Splat
        }
        $Script:AuthorizationCache[$ApplicationID]
    }
    else {
        Write-Warning -Message "Connect-MsalToken - Using old authorization format without Splatting. No refresh of tokens will be available."
    }
}
# PowerForge merged source end 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96

# PowerForge merged source begin 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96
function Convert-GraphInternalUser {
    <#
    .SYNOPSIS
    Converts user returned by graph with onPremisesExtensionAttributes to new simplified object
 
    .DESCRIPTION
    Converts user returned by graph with onPremisesExtensionAttributes to new simplified object
 
    .PARAMETER InputObject
    The object to convert
 
    .EXAMPLE
    An example
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param(
        [parameter(Mandatory, ValueFromPipeline)][PSCustomObject[]] $InputObject
    )
    process {
        $InputObject | ForEach-Object {
            $NewObject = [ordered] @{}
            $Object = $_
            foreach ($Property in $Object.PSObject.Properties.Name) {
                if ($Property -eq 'onPremisesExtensionAttributes') {
                    $OnPrem = $Object.onPremisesExtensionAttributes
                    if ($null -ne $OnPrem) {
                        if ($OnPrem -is [System.Collections.IDictionary]) {
                            foreach ($Key in $OnPrem.Keys) {
                                $NewObject[$Key] = $OnPrem[$Key]
                            }
                        }
                        else {
                            foreach ($ExtensionAttribute in $OnPrem.PSObject.Properties.Name) {
                                $NewObject[$ExtensionAttribute] = $OnPrem.$ExtensionAttribute
                            }
                        }
                    }
                    # keep the raw object for debugging/inspection
                    $NewObject['onPremisesExtensionAttributes'] = $OnPrem
                }
                else {
                    $NewObject[$Property] = $Object.$Property
                }
            }
            [PSCustomObject] $NewObject
        }
    }
}
# PowerForge merged source end 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96

# PowerForge merged source begin 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96
function Format-GraphimoRestError {
    [cmdletBinding()]
    param(
        [parameter(Mandatory)][System.Management.Automation.ErrorRecord] $ErrorRecord,
        [parameter(Mandatory)][System.Collections.IDictionary] $RestSplat
    )

    $ExceptionMessage = $ErrorRecord.Exception.Message -replace "`r", ' ' -replace "`n", ' '
    $RestError = $ErrorRecord.ErrorDetails.Message
    $RequestSummary = "[$($RestSplat.Method)] $($RestSplat.Uri)"
    $StatusSummary = $null

    $Response = $ErrorRecord.Exception.Response
    if ($Response) {
        $StatusCode = $Response.StatusCode
        $ReasonPhrase = $Response.ReasonPhrase
        if (-not $ReasonPhrase) {
            $ReasonPhrase = $Response.StatusDescription
        }
        if ($StatusCode) {
            $StatusSummary = " Status: $([int] $StatusCode) $ReasonPhrase."
        }
    }

    if (-not $RestError) {
        return "Invoke-Graphimo - Error: $ExceptionMessage Request: $RequestSummary.$StatusSummary"
    }

    try {
        $ParsedError = ConvertFrom-Json -InputObject $RestError -ErrorAction Stop
        $GraphMessage = $ParsedError.error.message
        if (-not $GraphMessage) {
            $GraphMessage = $ParsedError.error_description
        }
        if (-not $GraphMessage) {
            $GraphMessage = $ParsedError.message
        }
        if ($GraphMessage) {
            $GraphMessage = $GraphMessage -replace "`r", ' ' -replace "`n", ' '
            return "Invoke-Graphimo - Error: $ExceptionMessage Request: $RequestSummary.$StatusSummary Graph error: $GraphMessage"
        }
    }
    catch {
        $ResponsePreview = $RestError -replace "`r", ' ' -replace "`n", ' '
        if ($ResponsePreview.Length -gt 500) {
            $ResponsePreview = $ResponsePreview.Substring(0, 500) + '...'
        }
        return "Invoke-Graphimo - Error: $ExceptionMessage Request: $RequestSummary.$StatusSummary Non-JSON response preview: $ResponsePreview"
    }

    $ResponsePreview = $RestError -replace "`r", ' ' -replace "`n", ' '
    if ($ResponsePreview.Length -gt 500) {
        $ResponsePreview = $ResponsePreview.Substring(0, 500) + '...'
    }
    "Invoke-Graphimo - Error: $ExceptionMessage Request: $RequestSummary.$StatusSummary Response preview: $ResponsePreview"
}
# PowerForge merged source end 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96

# PowerForge merged source begin 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96
function Invoke-InternalGraphimo {
    [CmdletBinding()]
    param(
        [Array] $OutputQuery,
        [int] $First,
        [int] $CurrentCount,
        [string] $CountVariable,
        [switch] $MgGraph
    )
    if ($OutputQuery.value) {
        $FoundUsers = $OutputQuery.value
    }
    if ($CountVariable) {
        Set-Variable -Name $CountVariable -Value $OutputQuery.'@odata.count' -Scope Global
    }
    if ($First) {
        if ($CurrentCount) {
            $First = $First - $CurrentCount
        }
        if ($FoundUsers.Count -eq $First) {
        }
        elseif ($FoundUsers.Count -gt $First) {
            $FoundUsers = $FoundUsers | Select-Object -First $First
        }
        else {
            $First = $First - $FoundUsers.Count
        }
    }
    if ($MgGraph -or $Script:MgGraphAuthenticated -eq $true) {
        foreach ($Object in $FoundUsers) {
            [PSCustomObject] $Object
        }
    }
    else {
        $FoundUsers
    }
}
# PowerForge merged source end 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96

# PowerForge merged source begin 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96
function Add-GraphGroup {
    [CmdletBinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [string] $DisplayName,
        [string] $Name,
        [string] $Description,
        [string] $MailNickname,
        [switch] $SecurityEnabled,
        [switch] $MailEnabled,
        [switch] $MgGraph
    )

    if (-not $MgGraph -and -not $Headers -and $Script:MgGraphAuthenticated -ne $true) {
        Write-Warning -Message "No headers or MgGraph switch provided. Skipping."
        return
    }

    $URI = "/groups"
    $BaseUri = 'https://graph.microsoft.com/v1.0'
    $Body = [ordered]@{
        groupTypes = @()
    }

    if ($PSBoundParameters.ContainsKey('DisplayName')) {
        $Body['displayName'] = $DisplayName
    }
    if ($PSBoundParameters.ContainsKey('Name')) {
        $Body['name'] = $Name
    }
    if ($PSBoundParameters.ContainsKey('Description')) {
        $Body['description'] = $Description
    }
    if ($PSBoundParameters.ContainsKey('MailNickname')) {
        $Body['mailNickname'] = $MailNickname
    }
    if ($PSBoundParameters.ContainsKey('SecurityEnabled')) {
        $Body['securityEnabled'] = $SecurityEnabled.IsPresent
    }
    if ($PSBoundParameters.ContainsKey('MailEnabled')) {
        $Body['mailEnabled'] = $MailEnabled.IsPresent
    }
    if ($Body.Count -gt 0) {
        Invoke-Graphimo -Uri $URI -Method POST -Headers $Headers -Body $Body -BaseUri $BaseUri -MgGraph:$MgGraph.IsPresent
    }
}
# PowerForge merged source end 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96

# PowerForge merged source begin 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96
function Add-GraphGroupMember {
    [cmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Headers')]
    param(
        [parameter()][alias('Authorization')][System.Collections.IDictionary] $Headers,
        [parameter(Mandatory)][alias('GroupID')][string] $ID,
        [parameter(Mandatory)][string] $MemberID,
        [parameter()][switch] $MgGraph
    )

    if (-not $MgGraph -and -not $Headers -and $Script:MgGraphAuthenticated -ne $true) {
        Write-Warning -Message "No headers or MgGraph switch provided. Skipping."
        return
    }

    $URI = "/groups/$ID/members/`$ref"

    $Body = @{
        "@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$MemberID"
    }

    Invoke-Graphimo -Uri $URI -Method POST -Headers $Headers -Body $Body -MgGraph:$MgGraph.IsPresent
}
# PowerForge merged source end 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96

# PowerForge merged source begin 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96
function Add-GraphUser {
    [cmdletBinding(SupportsShouldProcess)]
    param(
        [parameter()][alias('Authorization')][System.Collections.IDictionary] $Headers,
        [Parameter(Mandatory)][string] $UserPrincipalName,
        [string] $Name,
        [parameter(Mandatory)][alias('AccountEnabled')][bool] $Enabled,
        [alias('FirstName')][string] $GivenName,
        [alias('LastName')][string] $Surname,
        [alias('Title')][string] $JobTitle,
        [string] $EmployeeId,
        [string] $City,
        [Parameter(Mandatory)][string] $MailNickname,
        [alias('EmailAddress')][string] $Mail,
        [string] $Country,
        [string] $Department,
        [string] $PostalCode,
        [alias('Fax')][string] $FaxNumber,
        [string] $State,
        [string] $StreetAddress,
        [alias('OfficePhone')][string] $BusinessPhones,
        [alias('Mobile')][string] $MobilePhone,
        [string] $OfficeLocation,
        [string] $CompanyName,
        [Parameter(Mandatory)][string] $DisplayName,
        [switch] $ShowInAddressList,
        [switch] $DoNotForceChangePasswordNextSignIn,
        [string] $EmployeeType,
        [Parameter(Mandatory)][string] $Password,
        [alias('HireDate')][DateTime] $StartDate,
        [alias('CustomProperty')][System.Collections.IDictionary] $CustomProperties,
        [switch] $MgGraph
    )

    if (-not $MgGraph -and -not $Headers -and $Script:MgGraphAuthenticated -ne $true) {
        Write-Warning -Message "No headers or MgGraph switch provided. Skipping."
        return
    }

    $URI = "/users"
    $Body = [ordered]@{}
    # https://docs.microsoft.com/en-us/graph/api/resources/user?view=graph-rest-1.0
    if ($PSBoundParameters.ContainsKey('StartDate')) {
        # requires fixing
        # The date and time when the user was hired or will start work in case of a future hire.
        $Body['employeeHireDate'] = $StartDate
    }
    if ($PSBoundParameters.ContainsKey('UserPrincipalName')) {
        $Body['userPrincipalName'] = $UserPrincipalName
    }
    if ($PSBoundParameters.ContainsKey('JobTitle')) {
        $Body['jobTitle'] = $JobTitle
    }
    if ($PSBoundParameters.ContainsKey('EmployeeId')) {
        $Body['employeeId'] = $EmployeeId
    }
    if ($PSBoundParameters.ContainsKey('MailNickname')) {
        $Body['mailNickname'] = $MailNickname
    }
    if ($PSBoundParameters.ContainsKey('Mail')) {
        $Body['mail'] = $Mail
    }
    if ($PSBoundParameters.ContainsKey('FaxNumber')) {
        $Body['faxNumber'] = $FaxNumber
    }
    if ($PSBoundParameters.ContainsKey('givenName')) {
        $Body['givenName'] = $givenName
    }
    if ($PSBoundParameters.ContainsKey('Surname')) {
        $Body['surname'] = $Surname
    }
    if ($PSBoundParameters.ContainsKey('City')) {
        $Body['city'] = $City
    }
    if ($PSBoundParameters.ContainsKey('Country')) {
        $Body['country'] = $Country
    }
    if ($PSBoundParameters.ContainsKey('Department')) {
        $Body['department'] = $Department
    }
    if ($PSBoundParameters.ContainsKey('PostalCode')) {
        $Body['postalCode'] = $PostalCode
    }
    if ($PSBoundParameters.ContainsKey('State')) {
        $Body['state'] = $State
    }
    if ($PSBoundParameters.ContainsKey('StreetAddress')) {
        $Body['streetAddress'] = $StreetAddress
    }
    if ($PSBoundParameters.ContainsKey('businessPhones')) {
        $Body['businessPhones'] = @($businessPhones)
    }
    if ($PSBoundParameters.ContainsKey('mobilePhone')) {
        $Body['mobilePhone'] = $mobilePhone
    }
    if ($PSBoundParameters.ContainsKey('OfficeLocation')) {
        $Body['officeLocation'] = $OfficeLocation
    }
    if ($PSBoundParameters.ContainsKey('CompanyName')) {
        $Body['companyName'] = $CompanyName
    }
    if ($PSBoundParameters.ContainsKey('DisplayName')) {
        $Body['displayName'] = $DisplayName
    }
    if ($PSBoundParameters.ContainsKey('ShowInAddressList')) {
        $Body['showInAddressList'] = $ShowInAddressList.IsPresent
    }
    if ($PSBoundParameters.ContainsKey('Enabled')) {
        $Body['accountEnabled'] = $Enabled
    }
    if ($PSBoundParameters.ContainsKey('EmployeeType')) {
        $Body['employeeType'] = $EmployeeType
        $BaseUri = 'https://graph.microsoft.com/beta'
    }
    else {
        $BaseUri = 'https://graph.microsoft.com/v1.0'
    }
    $Body['passwordProfile'] = @{
        forceChangePasswordNextSignIn = -not $DoNotForceChangePasswordNextSignIn.IsPresent
        password                      = $Password
    }

    foreach ($Property in $CustomProperties.Keys) {
        $Body[$Property] = $CustomProperties[$Property]
    }

    #Remove-EmptyValue -Hashtable $Body
    if ($Body.Count -gt 0) {
        Invoke-Graphimo -Uri $URI -Method POST -Headers $Headers -Body $Body -BaseUri $BaseUri -MgGraph:$MgGraph.IsPresent
    }
}
# PowerForge merged source end 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96

# PowerForge merged source begin 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96
function Connect-Graphimo {
    [cmdletBinding(DefaultParameterSetName = 'ClearText')]
    param(
        [parameter(Mandatory, ParameterSetName = 'Encrypted')]
        [parameter(Mandatory, ParameterSetName = 'ClearText')][string][alias('ClientID')] $ApplicationID,
        [parameter(Mandatory, ParameterSetName = 'ClearText')][string][alias('ClientSecret')] $ApplicationKey,
        [parameter(Mandatory, ParameterSetName = 'Encrypted')][string][alias('ClientSecretEncrypted')] $ApplicationKeyEncrypted,
        [parameter(Mandatory, ParameterSetName = 'Credential')][PSCredential] $Credential,

        [parameter(Mandatory, ParameterSetName = 'Encrypted')]
        [parameter(Mandatory, ParameterSetName = 'ClearText')]
        [parameter(Mandatory, ParameterSetName = 'Credential')]
        [string] $TenantDomain,

        [parameter(ParameterSetName = 'Encrypted')]
        [parameter(ParameterSetName = 'ClearText')]
        [parameter(ParameterSetName = 'Credential')]
        [ValidateSet("https://manage.office.com", "https://graph.microsoft.com", "https://graph.microsoft.com/beta", 'https://graph.microsoft.com/.default')] $Resource = 'https://graph.microsoft.com/.default',
        [int] $ExpiresTimeout = 30,
        [switch] $ForceRefesh,

        [parameter(Mandatory, ParameterSetName = 'MsalToken')][System.Collections.IDictionary] $MsalToken,
        [parameter(Mandatory, ParameterSetName = 'MsGraphConfiguration')]
        [System.Collections.IDictionary] $MsGraphConfiguration,

        [parameter(Mandatory, ParameterSetName = 'MsGraphConfigurationSwitch')]
        [switch] $MgGraph
    )
    # Comparison V1/V2 https://nicolgit.github.io/AzureAD-Endopoint-V1-vs-V2-comparison/

    if (-not $Script:AuthorizationCache) {
        $Script:AuthorizationCache = [ordered] @{}
    }

    if ($null -ne $MsGraphConfiguration -or $MgGraph.IsPresent) {
        if ($MgGraph.IsPresent) {
            # We use Microsoft.Graph module instead of Invoke-RestMethod
            # We assume the connection was done before this function was called
            $Script:MgGraphAuthenticated = $true
            return
        }
        else {
            if (-not $MsGraphConfiguration.ErrorAction) {
                $MsGraphConfiguration.ErrorAction = 'Stop'
            }
            try {
                Connect-MgGraph @MsGraphConfiguration
                $Script:MgGraphAuthenticated = $true
                return
            }
            catch {
                if ($ErrorActionPreference -eq 'Stop') {
                    throw
                }
                $Script:MgGraphAuthenticated = $null
                $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                Write-Warning -Message "Connect-Graphimo - Error: $ErrorMessage"
                return $false
            }
        }
    }

    if ($Credential) {
        $RestSplat = @{
            ErrorAction = 'Stop'
            Method      = 'POST'
            Body        = @{
                grant_type    = "client_credentials"
                client_id     = $Credential.UserName
                client_secret = $Credential.GetNetworkCredential().Password
            }
        }
    }
    elseif ($ApplicationKey -or $ApplicationKeyEncrypted) {
        if ($ApplicationKeyEncrypted) {
            try {
                $ApplicationKeyTemp = $ApplicationKeyEncrypted | ConvertTo-SecureString -ErrorAction Stop
            }
            catch {
                $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                Write-Warning -Message "Connect-Graphimo - Error: $ErrorMessage"
                return
            }
            $ApplicationKey = [System.Net.NetworkCredential]::new([string]::Empty, $ApplicationKeyTemp).Password
        }
        $RestSplat = @{
            ErrorAction = 'Stop'
            Method      = 'POST'
            Body        = @{
                grant_type    = "client_credentials"
                client_id     = $ApplicationID
                client_secret = $ApplicationKey
            }
        }
    }
    elseif ($MsalToken) {
        $Authorization = @{
            Splat = $MsalToken
        }
        return Connect-MsalToken -Authorization $Authorization -ExpiresTimeout $ExpiresTimeout -ForceRefesh:$ForceRefesh
    }

    if ($Script:AuthorizationCache[$ApplicationID] -and -not $ForceRefesh) {
        if ($Script:AuthorizationCache[$ApplicationID].ExpiresOn -gt [datetime]::UtcNow) {
            Write-Verbose "Connect-Graphimo - Using cache for $ApplicationID"
            return $Script:AuthorizationCache[$ApplicationID]
        }
    }

    if ($Resource -in 'https://graph.microsoft.com/.default', "https://graph.microsoft.com/beta") {
        # V2 Endpoint
        $RestSplat['Body']['scope'] = $Resource
        $RestSplat['Uri'] = "https://login.microsoftonline.com/$($TenantDomain)/oauth2/v2.0/token"
    }
    else {
        # V1 Endpoint
        $RestSplat['Body']['resource'] = $Resource
        $RestSplat['Uri'] = "https://login.microsoftonline.com/$($TenantDomain)/oauth2/token"
    }
    Write-Verbose "Connect-Graphimo - EndPoint $($RestSplat['Uri'])"
    try {
        $Authorization = Invoke-RestMethod @RestSplat -Verbose:$false
        $Key = [ordered] @{
            'Authorization' = "$($Authorization.token_type) $($Authorization.access_token)"
            'Extended'      = $Authorization
            'Error'         = ''
            'ExpiresOn'     = ([datetime]::UtcNow).AddSeconds($Authorization.expires_in - $ExpiresTimeout)
            'Splat'         = [ordered] @{
                ApplicationID  = $RestSplat['Body']['client_id']
                ApplicationKey = $RestSplat['Body']['client_secret']
                TenantDomain   = $TenantDomain
                Resource       = $Resource
            }
        }
        $Script:AuthorizationCache[$ApplicationID] = $Key
    }
    catch {
        $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
        Write-Warning -Message "Connect-Graphimo - Error: $ErrorMessage"
        $Key = [ordered] @{
            'Authorization' = $Null
            'Extended'      = $Null
            'Error'         = $ErrorMessage
            'ExpiresOn'     = $null
            'Splat'         = [ordered] @{
                ApplicationID  = $RestSplat['Body']['client_id']
                ApplicationKey = $RestSplat['Body']['client_secret']
                TenantDomain   = $TenantDomain
                Resource       = $Resource
            }
        }
    }
    $Key
}
# PowerForge merged source end 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96

# PowerForge merged source begin 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96
function Get-GraphApplication {
    [cmdletBinding()]
    param(
        [parameter()][alias('Authorization')][System.Collections.IDictionary] $Headers,
        [string] $ID,
        [string] $DisplayName,
        [string[]] $Property,
        [switch] $MgGraph
    )

    if (-not $MgGraph -and -not $Headers -and $Script:MgGraphAuthenticated -ne $true) {
        Write-Warning -Message "No headers or MgGraph switch provided. Skipping."
        return
    }

    if ($ID) {
        # Query a single group
        $RelativeURI = "/applications/$ID"
        $QueryParameter = @{
            '$Select' = $Property -join ','
        }
    }
    elseif ($DisplayName) {
        $RelativeURI = '/applications'
        $QueryParameter = @{
            '$Select'  = $Property -join ','
            '$filter'  = "displayName eq '$DisplayName'"
            '$orderby' = $OrderBy
        }
    }
    else {
        # Query multiple groups
        $RelativeURI = '/applications'
        $QueryParameter = @{
            '$Select'  = $Property -join ','
            '$filter'  = $Filter
            '$orderby' = $OrderBy
        }
    }
    Remove-EmptyValue -Hashtable $QueryParameter
    Invoke-Graphimo -Uri $RelativeURI -Method GET -Headers $Headers -QueryParameter $QueryParameter -MgGraph:$MgGraph.IsPresent
}
# PowerForge merged source end 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96

# PowerForge merged source begin 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96
function Get-GraphContact {
    [cmdletBinding()]
    param(
        [parameter()][alias('Authorization')][System.Collections.IDictionary] $Headers,
        [string] $Id,
        [string[]] $Property,
        [string] $Filter,
        [string] $OrderBy,
        [switch] $MgGraph
    )

    if (-not $MgGraph -and -not $Headers -and $Script:MgGraphAuthenticated -ne $true) {
        Write-Warning -Message "No headers or MgGraph switch provided. Skipping."
        return
    }

    if ($ID) {
        # Query a single group
        $RelativeURI = "/contacts/$ID"
        $QueryParameter = @{
            '$Select' = $Property -join ','
        }
    }
    else {
        # Query multiple groups
        $RelativeURI = '/contacts'
        $QueryParameter = @{
            '$Select'  = $Property -join ','
            '$filter'  = $Filter
            '$orderby' = $OrderBy
        }
    }
    Remove-EmptyValue -Hashtable $QueryParameter
    Invoke-Graphimo -Uri $RelativeURI -Method GET -Headers $Headers -QueryParameter $QueryParameter -MgGraph:$MgGraph.IsPresent
}
# PowerForge merged source end 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96

# PowerForge merged source begin 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96
function Get-GraphGroup {
    [cmdletBinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [string] $Id,
        [string[]] $Property,
        [string] $Filter,
        [string] $OrderBy,
        [switch] $MgGraph
    )

    if (-not $MgGraph -and -not $Headers -and $Script:MgGraphAuthenticated -ne $true) {
        Write-Warning -Message "No headers or MgGraph switch provided. Skipping."
        return
    }

    if ($ID) {
        # Query a single group
        $RelativeURI = "/groups/$ID"
        $QueryParameter = @{
            '$Select' = $Property -join ','
        }
    }
    else {
        # Query multiple groups
        $RelativeURI = '/groups'
        $QueryParameter = @{
            '$Select'  = $Property -join ','
            '$filter'  = $Filter
            '$orderby' = $OrderBy
        }
    }
    Remove-EmptyValue -Hashtable $QueryParameter
    Invoke-Graphimo -Uri $RelativeURI -Method GET -Headers $Headers -QueryParameter $QueryParameter -MgGraph:$MgGraph.IsPresent
}
# PowerForge merged source end 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96

# PowerForge merged source begin 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96
function Get-GraphGroupMember {
    [cmdletBinding()]
    param(
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [parameter(Mandatory)][string] $Id,
        [string] $Search,
        [string[]] $Property,
        [switch] $MgGraph
    )

    if (-not $MgGraph -and -not $Headers -and $Script:MgGraphAuthenticated -ne $true) {
        Write-Warning -Message "No headers or MgGraph switch provided. Skipping."
        return
    }

    if ($ID) {
        # Query a single group
        $RelativeURI = "/groups/$ID/members"
        $QueryParameter = @{
            '$Select' = $Property -join ','
            '$Search' = $Search
        }
    }
    Remove-EmptyValue -Hashtable $QueryParameter
    $invokeGraphimoSplat = @{
        Uri            = $RelativeURI
        Method         = 'GET'
        Headers        = $Headers
        QueryParameter = $QueryParameter
        MgGraph        = $MgGraph.IsPresent
    }
    if ($QueryParameter.'$Search') {
        # This is required for search to work
        # https://developer.microsoft.com/en-us/identity/blogs/build-advanced-queries-with-count-filter-search-and-orderby/
        $invokeGraphimoSplat['ConsistencyLevel'] = 'eventual'
    }

    Invoke-Graphimo @invokeGraphimoSplat
}
# PowerForge merged source end 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96

# PowerForge merged source begin 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96
function Get-GraphUser {
    [alias('Get-GraphUsers')]
    [cmdletBinding(DefaultParameterSetName = 'Default')]
    param(
        [parameter(ParameterSetName = 'Default')]
        [parameter(ParameterSetName = 'EmailAddress')]
        [parameter(ParameterSetName = 'UserPrincipalName')]
        [parameter(ParameterSetName = 'Filter')]
        [parameter(ParameterSetName = 'Id')]
        [alias('Authorization')][System.Collections.IDictionary] $Headers,

        [parameter(ParameterSetName = 'Id')][string] $Id,
        [parameter(ParameterSetName = 'UserPrincipalName')][string] $UserPrincipalName,
        [alias('Mail')][parameter(ParameterSetName = 'EmailAddress')][string] $EmailAddress,

        [string[]] $Property,

        [parameter(ParameterSetName = 'Filter')][string] $Filter,
        [string] $OrderBy,
        [switch] $IncludeManager,
        [int] $First,
        [string] $CountVariable,
        [string] $ConsistencyLevel,

        [switch] $DebugRequest,
        [switch] $MgGraph
    )

    if (-not $MgGraph -and -not $Headers -and $Script:MgGraphAuthenticated -ne $true) {
        Write-Warning -Message "No headers or MgGraph switch provided. Skipping."
        return
    }

    if ($Property -contains 'EmployeeType' -or $Property -contains 'onPremisesExtensionAttributes') {
        $BaseURI = 'https://graph.microsoft.com/beta'
    }
    else {
        $BaseURI = 'https://graph.microsoft.com/v1.0'
    }
    $NewProperties = foreach ($P in $Property) {
        if ($P -like "extensionAttribute*") {
            'onPremisesExtensionAttributes'
        }
        else {
            $P
        }
    }
    $Property = $NewProperties | Select-Object -Unique

    if ($UserPrincipalName) {
        $RelativeURI = '/users'
        $QueryParameter = [ordered]@{
            '$Select' = $Property -join ','
            '$filter' = "userPrincipalName eq '$UserPrincipalName'"
        }
    }
    elseif ($EmailAddress) {
        $RelativeURI = '/users'
        $QueryParameter = [ordered]@{
            '$Select' = $Property -join ','
            '$filter' = "mail eq '$EmailAddress'"
        }
    }
    elseif ($ID) {
        # Query a single user
        # doing it standard way doesn't seem to work so lets user filter instead
        #$RelativeURI = "/users/$ID"
        $RelativeURI = "/users"
        $QueryParameter = [ordered]@{
            '$filter' = "id eq '$ID'"
            '$Select' = $Property -join ','
        }
    }
    else {
        # Query multiple groups
        $RelativeURI = '/users'
        $QueryParameter = [ordered]@{
            '$Select'  = $Property -join ','
            '$filter'  = $Filter
            '$orderby' = $OrderBy
        }
    }
    if ($IncludeManager) {
        $QueryParameter['$expand'] = 'manager'
    }
    if ($CountVariable) {
        $QueryParameter['$count'] = 'true'
    }
    # if ($ConsistencyLevel) {
    # $Headers['consistencyLevel'] = $ConsistencyLevel
    # }

    Remove-EmptyValue -Hashtable $QueryParameter

    if ($DebugRequest) {
        Write-Verbose ("Get-GraphUser request: BaseUri={0} Uri={1} Select={2} Filter={3} Expand={4} OrderBy={5} Count={6} ConsistencyLevel={7}" -f $BaseURI, $RelativeURI, $QueryParameter['$Select'], $QueryParameter['$filter'], $QueryParameter['$expand'], $QueryParameter['$orderby'], $QueryParameter['$count'], $ConsistencyLevel)
    }

    $invokeGraphimoSplat = @{
        Uri              = $RelativeURI
        Method           = 'GET'
        QueryParameter   = $QueryParameter
        BaseUri          = $BaseURI
        First            = $First
        CountVariable    = $CountVariable
        ConsistencyLevel = $ConsistencyLevel
        MgGraph          = $MgGraph.IsPresent
    }
    if ($Headers) {
        $invokeGraphimoSplat['Headers'] = $Headers
    }

    if ($Property -contains 'onPremisesExtensionAttributes') {
        $OutputData = Invoke-Graphimo @invokeGraphimoSplat
        if ($OutputData) {
            $OutputData | Convert-GraphInternalUser
        }
    }
    else {
        Invoke-Graphimo @invokeGraphimoSplat
    }
}
# PowerForge merged source end 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96

# PowerForge merged source begin 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96
function Import-GraphGuest {
    [cmdletBinding()]
    param(
        [parameter()][alias('Authorization')][System.Collections.IDictionary] $Headers,
        [string] $Name,
        [Parameter(Mandatory)][string] $EmailAddress,
        [switch] $SendInvitationMessage,
        [string] $InviteRedirectUrl = "https://portal.office.com",
        [switch] $ResetRedemption,
        [string] $InvitedUserID,
        [ValidateSet('Member', 'Guest')][string] $UserType,
        [switch] $MgGraph
    )

    if (-not $MgGraph -and -not $Headers -and $Script:MgGraphAuthenticated -ne $true) {
        Write-Warning -Message "No headers or MgGraph switch provided. Skipping."
        return
    }

    $URI = '/invitations'
    $body = [ordered]@{
        'invitedUserDisplayName'  = $Name
        'invitedUserEmailAddress' = $EmailAddress
        'inviteRedirectUrl'       = $InviteRedirectUrl
        'sendInvitationMessage'   = $SendInvitationMessage.IsPresent
        'resetRedemption'         = $ResetRedemption.IsPresent
    }
    if ($UserType) {
        $Body['invitedUserType'] = $UserType
    }
    if ($InvitedUserID) {
        $Body['invitedUser'] = @{
            'id' = $InvitedUserID
        }
    }
    Invoke-Graphimo -Uri $URI -Method POST -Headers $Headers -Body $Body -MgGraph:$MgGraph.IsPresent
}
# PowerForge merged source end 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96

# PowerForge merged source begin 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96
function Invoke-Graphimo {
    [cmdletBinding(SupportsShouldProcess)]
    param(
        [alias('PrimaryUri')][uri] $BaseUri = 'https://graph.microsoft.com/v1.0',
        [uri] $Uri,
        [parameter()][alias('Authorization')][System.Collections.IDictionary] $Headers,
        [validateset('GET', 'DELETE', 'POST', 'PATCH', 'PUT')][string] $Method = 'GET',
        [string] $ContentType = "application/json; charset=UTF-8",
        [System.Collections.IDictionary] $Body,
        [System.Collections.IDictionary] $QueryParameter,
        [switch] $FullUri,
        [string] $CountVariable,
        [int] $First,
        [string] $ConsistencyLevel,
        [switch] $MgGraph
    )
    if ($MgGraph -or $Script:MgGraphAuthenticated -eq $true) {
        # we use the Microsoft.Graph module instead of Invoke-RestMethod
    }
    elseif ($Headers.MsalToken) {
        if ($Headers.Splat) {
            $Splat = $Headers.Splat
            $Headers = Connect-MsalToken -Authorization $Headers
        }
    }
    else {
        if (-not $Headers) {
            Write-Warning "No headers provided. Skipping."
            return
        }
        # This forces a reconnect of session in case it's about to time out. If it's not timeouting a cache value is used
        if ($Headers.Splat) {
            $Splat = $Headers.Splat
            $Headers = Connect-Graphimo @Splat
        }
    }

    if ($Headers.Error) {
        Write-Warning "Invoke-Graphimo - Authorization error. Skipping."
        return
    }
    if ($Headers.MsalToken) {
        $RestSplat = @{
            Headers     = @{
                Authorization = $Headers.MsalToken.TokenType + ' ' + $Headers.MsalToken.AccessToken
            }
            Method      = $Method
            ContentType = $ContentType
        }
    }
    else {
        $RestSplat = @{
            Headers     = $Headers
            Method      = $Method
            ContentType = $ContentType
        }
    }
    if ($Body) {
        $RestSplat['Body'] = $Body | ConvertTo-Json -Depth 5
    }
    if ($FullUri) {
        $RestSplat.Uri = $Uri
    }
    else {
        $RestSplat.Uri = Join-UriQuery -BaseUri $BaseUri -RelativeOrAbsoluteUri $Uri -QueryParameter $QueryParameter
    }
    if ($RestSplat['Body']) {
        $WhatIfInformation = "Invoking [$Method] " + [System.Environment]::NewLine + $RestSplat['Body'] + [System.Environment]::NewLine
    }
    else {
        $WhatIfInformation = "Invoking [$Method] / [ConsistencyLevel: $($ConsistencyLevel)] "
    }
    if ($ConsistencyLevel) {
        if (-not $RestSplat.Headers) {
            $RestSplat.Headers = [ordered]@{}
        }
        $RestSplat.Headers['ConsistencyLevel'] = $ConsistencyLevel
    }
    Remove-EmptyValue -Hashtable $RestSplat
    try {
        if ($Method -eq 'GET') {
            Write-Verbose "Invoke-Graphimo - $($WhatIfInformation)over URI $($RestSplat.Uri)"
            if ($MgGraph -or $Script:MgGraphAuthenticated -eq $true) {
                $OutputQuery = Invoke-MgGraphRequest @RestSplat -Verbose:$false
            }
            else {
                $OutputQuery = Invoke-RestMethod @RestSplat -Verbose:$false
            }
            $Count = 1
            [Array] $FoundUsers = Invoke-InternalGraphimo -OutputQuery $OutputQuery -First $First -CountVariable $CountVariable -MgGraph:$MgGraph.IsPresent
            $CurrentCount = $FoundUsers.Count
            if ($First -gt 0 -and $CurrentCount -ge $First) {
                return $FoundUsers
            }
            else {
                $FoundUsers
            }
            if ($OutputQuery.'@odata.nextLink') {
                do {
                    $RestSplat.Uri = $OutputQuery.'@odata.nextLink'
                    Write-Verbose "Invoke-Graphimo - $($WhatIfInformation)NextLink (Page $Count/Current Count: $($CurrentCount))) over URI $($RestSplat.Uri)"
                    if ($MgGraph -or $Script:MgGraphAuthenticated -eq $true) {
                        $OutputQuery = Invoke-MgGraphRequest @RestSplat -Verbose:$false
                    }
                    else {
                        $OutputQuery = Invoke-RestMethod @RestSplat -Verbose:$false
                    }
                    [Array] $FoundUsers = Invoke-InternalGraphimo -OutputQuery $OutputQuery -First $First -CurrentCount $CurrentCount -CountVariable $CountVariable -MgGraph:$MgGraph.IsPresent
                    $FoundUsers
                    $Count++
                    $CurrentCount = $CurrentCount + $FoundUsers.Count
                } until (($First -gt 0 -and $CurrentCount -ge $First) -or $null -eq $OutputQuery.'@odata.nextLink')
            }
        }
        else {
            Write-Verbose "Invoke-Graphimo - $($WhatIfInformation)over URI $($RestSplat.Uri)"
            if ($PSCmdlet.ShouldProcess($($RestSplat.Uri), $WhatIfInformation)) {
                if ($MgGraph -or $Script:MgGraphAuthenticated -eq $true) {
                    $OutputQuery = Invoke-MgGraphRequest @RestSplat -Verbose:$false
                }
                else {
                    $OutputQuery = Invoke-RestMethod @RestSplat -Verbose:$false
                }
                if ($Method -in 'POST') {
                    $OutputQuery
                }
                else {
                    return $true
                }
            }
        }
    }
    catch {
        Write-Warning -Message (Format-GraphimoRestError -ErrorRecord $_ -RestSplat $RestSplat)
        if ($Method -notin 'GET', 'POST') {
            return $false
        }
    }
}
# PowerForge merged source end 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96

# PowerForge merged source begin 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96
function Remove-GraphGroupMember {
    [cmdletBinding(SupportsShouldProcess)]
    param(
        [parameter(ParameterSetName = 'All')]
        [parameter(ParameterSetName = 'PerID')]
        [parameter(ParameterSetName = 'BySearch')]
        [alias('Authorization')][System.Collections.IDictionary] $Headers,
        [parameter(Mandatory, ParameterSetName = 'All')]
        [parameter(Mandatory, ParameterSetName = 'PerID')]
        [parameter(Mandatory, ParameterSetName = 'BySearch')]
        [alias('GroupID')][string] $ID,

        [parameter(Mandatory, ParameterSetName = 'PerID')][string] $MemberID,

        [parameter(Mandatory, ParameterSetName = 'BySearch')][string] $Search,

        [parameter(ParameterSetName = 'All')][switch] $All,
        [switch] $MgGraph
    )

    if (-not $MgGraph -and -not $Headers -and $Script:MgGraphAuthenticated -ne $true) {
        Write-Warning -Message "No headers or MgGraph switch provided. Skipping."
        return
    }

    if ($All) {
        # Lets remove all, but to do that we need to know who to remove
        $Users = Get-GraphGroupMember -Id $ID -Headers $Headers -Verbose -Property id, displayName -MgGraph:$MgGraph.IsPresent
        foreach ($User in $Users) {
            $URI = "/groups/$ID/members/$($User.id)/`$ref"
            Invoke-Graphimo -Uri $URI -Method DELETE -Headers $Headers
        }
    }
    elseif ($Search) {
        $Users = Get-GraphGroupMember -Id $ID -Headers $Headers -Verbose -Property id, displayName -Search $Search -MgGraph:$MgGraph.IsPresent
        foreach ($User in $Users) {
            $URI = "/groups/$ID/members/$($User.id)/`$ref"
            Invoke-Graphimo -Uri $URI -Method DELETE -Headers $Headers -MgGraph:$MgGraph.IsPresent
        }
    }
    else {
        # Lets delete just one record
        $URI = "/groups/$ID/members/$MemberID/`$ref"
        Invoke-Graphimo -Uri $URI -Method DELETE -Headers $Headers -MgGraph:$MgGraph.IsPresent
    }
}
# PowerForge merged source end 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96

# PowerForge merged source begin 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96
function Remove-GraphManager {
    [cmdletBinding(SupportsShouldProcess)]
    param(
        [parameter()][alias('Authorization')][System.Collections.IDictionary] $Headers,
        [alias('UserID')][string] $ID,
        [string] $UserPrincipalName,
        [switch] $MgGraph
    )

    if (-not $MgGraph -and -not $Headers -and $Script:MgGraphAuthenticated -ne $true) {
        Write-Warning -Message "No headers or MgGraph switch provided. Skipping."
        return
    }

    if ($ID) {
        $URI = "/users/$ID/manager/`$ref"
    }
    else {
        $URI = "/users/$UserPrincipalName/manager/`$ref"
    }
    Invoke-Graphimo -Uri $URI -Method DELETE -Headers $Headers -MgGraph:$MgGraph.IsPresent
}
# PowerForge merged source end 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96

# PowerForge merged source begin 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96
function Remove-GraphUser {
    [cmdletBinding(SupportsShouldProcess)]
    param(
        [parameter()][alias('Authorization')][System.Collections.IDictionary] $Headers,
        [string] $UserPrincipalName,
        [alias('UserID')][string] $ID,
        [switch] $MgGraph
    )

    if (-not $MgGraph -and -not $Headers -and $Script:MgGraphAuthenticated -ne $true) {
        Write-Warning -Message "No headers or MgGraph switch provided. Skipping."
        return
    }

    if ($ID) {
        $URI = "/users/$ID"
    }
    else {
        $URI = "/users/$UserPrincipalName"
    }
    Invoke-Graphimo -Uri $URI -Method DELETE -Headers $Headers -MgGraph:$MgGraph.IsPresent
}
# PowerForge merged source end 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96

# PowerForge merged source begin 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96
function Set-GraphManager {
    [cmdletBinding(SupportsShouldProcess)]
    param(
        [parameter()][alias('Authorization')][System.Collections.IDictionary] $Headers,
        [alias('UserID')][string] $ID,
        [string] $UserPrincipalName,
        [string] $Name,
        [string] $ManagerID,
        [string] $ManagerDisplayName,
        [switch] $MgGraph
    )

    if (-not $MgGraph -and -not $Headers -and $Script:MgGraphAuthenticated -ne $true) {
        Write-Warning -Message "No headers or MgGraph switch provided. Skipping."
        return
    }

    if ($ID) {
        $URI = "/users/$ID/manager/`$ref"
    }
    else {
        $URI = "/users/$UserPrincipalName/manager/`$ref"
    }
    $Body = [ordered]@{
        "@odata.id" = "https://graph.microsoft.com/v1.0/users/$ManagerID"
    }
    Invoke-Graphimo -Uri $URI -Method PUT -Headers $Headers -Body $Body -MgGraph:$MgGraph.IsPresent
}
# PowerForge merged source end 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96

# PowerForge merged source begin 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96
function Set-GraphUser {
    [cmdletBinding(SupportsShouldProcess)]
    param(
        [parameter()][alias('Authorization')][System.Collections.IDictionary] $Headers,
        [alias('UserID')][string] $ID,
        [string] $SearchUserPrincipalName,
        [string] $UserPrincipalName,
        [string] $Name,
        [alias('AccountEnabled')][nullable[bool]] $Enabled,
        [alias('FirstName')][string] $GivenName,
        [alias('LastName')][string] $Surname,
        [alias('Title')][string] $JobTitle,
        [string] $EmployeeId,
        [string] $City,
        [string] $MailNickname,
        [alias('EmailAddress')][string] $Mail,
        [string] $Country,
        [string] $Department,
        [string] $PostalCode,
        [alias('Fax')][string] $FaxNumber,
        [string] $State,
        [string] $StreetAddress,
        [alias('OfficePhone')][string] $BusinessPhones,
        [alias('Mobile')][string] $MobilePhone,
        [string] $OfficeLocation,
        [string] $CompanyName,
        [string] $DisplayName,
        [string] $EmployeeType,
        [switch] $ShowInAddressList,
        [alias('HireDate')][DateTime] $StartDate,
        [alias('CustomProperty')][System.Collections.IDictionary] $CustomProperties,
        [string] $ExtensionAttribute1,
        [string] $ExtensionAttribute2,
        [string] $ExtensionAttribute3,
        [string] $ExtensionAttribute4,
        [string] $ExtensionAttribute5,
        [string] $ExtensionAttribute6,
        [string] $ExtensionAttribute7,
        [string] $ExtensionAttribute8,
        [string] $ExtensionAttribute9,
        [string] $ExtensionAttribute10,
        [string] $ExtensionAttribute11,
        [string] $ExtensionAttribute12,
        [string] $ExtensionAttribute13,
        [string] $ExtensionAttribute14,
        [string] $ExtensionAttribute15,
        [System.Collections.IDictionary] $OnPremisesExtensionAttributes,
        [string] $UserType,
        [switch] $MgGraph
    )

    if (-not $MgGraph -and -not $Headers -and $Script:MgGraphAuthenticated -ne $true) {
        Write-Warning -Message "No headers or MgGraph switch provided. Skipping."
        return
    }

    $Body = [ordered]@{}
    # https://docs.microsoft.com/en-us/graph/api/resources/user?view=graph-rest-1.0
    if ($PSBoundParameters.ContainsKey('StartDate')) {
        # requires fixing
        # The date and time when the user was hired or will start work in case of a future hire.
        $Body['employeeHireDate'] = $StartDate
    }
    if ($PSBoundParameters.ContainsKey('JobTitle')) {
        $Body['jobTitle'] = $JobTitle
    }
    if ($PSBoundParameters.ContainsKey('EmployeeId')) {
        $Body['employeeId'] = $EmployeeId
    }
    if ($PSBoundParameters.ContainsKey('UserPrincipalName')) {
        $Body['userPrincipalName'] = $UserPrincipalName
    }
    if ($PSBoundParameters.ContainsKey('MailNickname')) {
        $Body['mailNickname'] = $MailNickname
    }
    if ($PSBoundParameters.ContainsKey('Mail')) {
        $Body['mail'] = $Mail
    }
    if ($PSBoundParameters.ContainsKey('FaxNumber')) {
        $Body['faxNumber'] = $FaxNumber
    }
    if ($PSBoundParameters.ContainsKey('givenName')) {
        $Body['givenName'] = $givenName
    }
    if ($PSBoundParameters.ContainsKey('Surname')) {
        $Body['surname'] = $Surname
    }
    if ($PSBoundParameters.ContainsKey('City')) {
        $Body['city'] = $City
    }
    if ($PSBoundParameters.ContainsKey('Country')) {
        $Body['country'] = $Country
    }
    if ($PSBoundParameters.ContainsKey('Department')) {
        $Body['department'] = $Department
    }
    if ($PSBoundParameters.ContainsKey('PostalCode')) {
        $Body['postalCode'] = $PostalCode
    }
    if ($PSBoundParameters.ContainsKey('State')) {
        $Body['state'] = $State
    }
    if ($PSBoundParameters.ContainsKey('StreetAddress')) {
        $Body['streetAddress'] = $StreetAddress
    }
    if ($PSBoundParameters.ContainsKey('businessPhones')) {
        $Body['businessPhones'] = @($businessPhones)
    }
    if ($PSBoundParameters.ContainsKey('mobilePhone')) {
        $Body['mobilePhone'] = $mobilePhone
    }
    if ($PSBoundParameters.ContainsKey('OfficeLocation')) {
        $Body['officeLocation'] = $OfficeLocation
    }
    if ($PSBoundParameters.ContainsKey('CompanyName')) {
        $Body['companyName'] = $CompanyName
    }
    if ($PSBoundParameters.ContainsKey('DisplayName')) {
        $Body['displayName'] = $DisplayName
    }
    if ($PSBoundParameters.ContainsKey('ShowInAddressList')) {
        $Body['showInAddressList'] = $ShowInAddressList.IsPresent
    }
    if ($PSBoundParameters.ContainsKey('Enabled')) {
        $Body['accountEnabled'] = $Enabled
    }
    if ($PSBoundParameters.ContainsKey('EmployeeType')) {
        $Body['employeeType'] = $EmployeeType
        $BaseUri = 'https://graph.microsoft.com/beta'
    }
    else {
        $BaseUri = 'https://graph.microsoft.com/v1.0'
    }
    foreach ($Property in $CustomProperties.Keys) {
        $Body[$Property] = $CustomProperties[$Property]
    }

    $Body['onPremisesExtensionAttributes'] = [ordered] @{}
    if ($PSBoundParameters.ContainsKey('ExtensionAttribute1')) {
        $Body['onPremisesExtensionAttributes']['extensionAttribute1'] = $ExtensionAttribute1
    }
    if ($PSBoundParameters.ContainsKey('ExtensionAttribute2')) {
        $Body['onPremisesExtensionAttributes']['extensionAttribute2'] = $ExtensionAttribute2
    }
    if ($PSBoundParameters.ContainsKey('ExtensionAttribute3')) {
        $Body['onPremisesExtensionAttributes']['extensionAttribute3'] = $ExtensionAttribute3
    }
    if ($PSBoundParameters.ContainsKey('ExtensionAttribute4')) {
        $Body['onPremisesExtensionAttributes']['extensionAttribute4'] = $ExtensionAttribute4
    }
    if ($PSBoundParameters.ContainsKey('ExtensionAttribute5')) {
        $Body['onPremisesExtensionAttributes']['extensionAttribute5'] = $ExtensionAttribute5
    }
    if ($PSBoundParameters.ContainsKey('ExtensionAttribute6')) {
        $Body['onPremisesExtensionAttributes']['extensionAttribute6'] = $ExtensionAttribute6
    }
    if ($PSBoundParameters.ContainsKey('ExtensionAttribute7')) {
        $Body['onPremisesExtensionAttributes']['extensionAttribute7'] = $ExtensionAttribute7
    }
    if ($PSBoundParameters.ContainsKey('ExtensionAttribute8')) {
        $Body['onPremisesExtensionAttributes']['extensionAttribute8'] = $ExtensionAttribute8
    }
    if ($PSBoundParameters.ContainsKey('ExtensionAttribute9')) {
        $Body['onPremisesExtensionAttributes']['extensionAttribute9'] = $ExtensionAttribute9
    }
    if ($PSBoundParameters.ContainsKey('ExtensionAttribute10')) {
        $Body['onPremisesExtensionAttributes']['extensionAttribute10'] = $ExtensionAttribute10
    }
    if ($PSBoundParameters.ContainsKey('ExtensionAttribute11')) {
        $Body['onPremisesExtensionAttributes']['extensionAttribute11'] = $ExtensionAttribute11
    }
    if ($PSBoundParameters.ContainsKey('ExtensionAttribute12')) {
        $Body['onPremisesExtensionAttributes']['extensionAttribute12'] = $ExtensionAttribute12
    }
    if ($PSBoundParameters.ContainsKey('ExtensionAttribute13')) {
        $Body['onPremisesExtensionAttributes']['extensionAttribute13'] = $ExtensionAttribute13
    }
    if ($PSBoundParameters.ContainsKey('ExtensionAttribute14')) {
        $Body['onPremisesExtensionAttributes']['extensionAttribute14'] = $ExtensionAttribute14
    }
    if ($PSBoundParameters.ContainsKey('ExtensionAttribute15')) {
        $Body['onPremisesExtensionAttributes']['extensionAttribute15'] = $ExtensionAttribute15
    }
    if ($PSBoundParameters.ContainsKey("UserType")) {
        $Body['userType'] = $UserType
    }
    if ($ID) {
        $UserIdentifier = [System.Uri]::EscapeDataString($ID)
    }
    else {
        $UserIdentifier = [System.Uri]::EscapeDataString($SearchUserPrincipalName)
    }
    $URI = "/users/$UserIdentifier"
    if ($Body['onPremisesExtensionAttributes'].Count -eq 0) {
        $Body.Remove('onPremisesExtensionAttributes')
    }
    if ($Body.Count -gt 0) {
        Invoke-Graphimo -Uri $URI -Method PATCH -Headers $Headers -Body $Body -BaseUri $BaseUri -MgGraph:$MgGraph.IsPresent
    }
    else {
        Write-Warning -Message "Set-GraphUser - No changes were made to the user, as no field to change."
    }
}
# PowerForge merged source end 46f9d62272ed584a2d89b5bed3e940f8d756194e3253db7193c33a7baefaaa96

$FunctionsToExport = @('Add-GraphGroup', 'Add-GraphGroupMember', 'Add-GraphUser', 'Connect-Graphimo', 'Get-GraphApplication', 'Get-GraphContact', 'Get-GraphGroup', 'Get-GraphGroupMember', 'Get-GraphUser', 'Import-GraphGuest', 'Invoke-Graphimo', 'Remove-GraphGroupMember', 'Remove-GraphManager', 'Remove-GraphUser', 'Set-GraphManager', 'Set-GraphUser')
$CmdletsToExport = @()
$AliasesToExport = @('Get-GraphUsers')
Export-ModuleMember -Function $FunctionsToExport -Alias $AliasesToExport -Cmdlet $CmdletsToExport

# SIG # Begin signature block
# MIIt7AYJKoZIhvcNAQcCoIIt3TCCLdkCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBCVcslmfXkJSo7
# WUTT4Pauttyr3CWUf2+T5dijGDeFqKCCJukwggWNMIIEdaADAgECAhAOmxiO+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
# twGpn1eqXijiuZQwggWQMIIDeKADAgECAhAFmxtXno4hMuI5B72nd3VcMA0GCSqG
# SIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx
# GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRy
# dXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGIx
# CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
# dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBH
# NDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIw
# aTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLK
# EdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4Tm
# dDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembu
# d8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnD
# eMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1
# XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVld
# QnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTS
# YW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSm
# M9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzT
# QRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kx
# fgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD
# VR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzANBgkq
# hkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNkaA9Wz3eucPn9mkqZucl4
# XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjSPMFDQK4dUPVS/JA7u5iZ
# aWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK7VB6fWIhCoDIc2bRoAVg
# X+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eBcg3AFDLvMFkuruBx8lbk
# apdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp5aPNoiBB19GcZNnqJqGL
# FNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msgdDDS4Dk0EIUhFQEI6FUy
# 3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vriRbgjU2wGb2dVf0a1TD9u
# KFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ79ARj6e/CVABRoIoqyc54
# zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5nLGbsQAe79APT0JsyQq8
# 7kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3i0objwG2J5VT6LaJbVu8
# aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0HEEcRrYc9B9F1vM/zZn4w
# ggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0GCSqGSIb3DQEBDAUAMGIx
# CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
# dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBH
# NDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTlaMGkxCzAJBgNVBAYTAlVT
# MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1
# c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEzODQgMjAyMSBDQTEwggIi
# MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C0CiteLdd1TlZG7GIQvUz
# jOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce2vnS1WcaUk8OoVf8iJnB
# kcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0daE6ZMswEgJfMQ04uy+wjw
# iuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6TSXBCMo/7xuoc82VokaJN
# TIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoAFdE3/hoGlMJ8yOobMubK
# wvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7OhD26jq22YBoMbt2pnLdK9
# RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM1bL5OBDm6s6R9b7T+2+T
# YTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z8ujo7AqnsAMrkbI2eb73
# rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05huzUtw1T0PhH5nUwjeww
# k3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNYmtwmKwH0iU1Z23jPgUo+
# QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP/2NPTLuqDQhTQXxYPUez
# +rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0TAQH/BAgwBgEB/wIBADAd
# BgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYDVR0jBBgwFoAU7NfjgtJx
# XWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUF
# BwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGln
# aWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5j
# b20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJo
# dHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNy
# bDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATANBgkqhkiG9w0BAQwFAAOC
# AgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95RysQDKr2wwJxMSnpBEn0v9
# nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HLIvdaqpDP9ZNq4+sg0dVQ
# eYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5BtfQ/g+lP92OT2e1JnPSt0o
# 618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnhOE7abrs2nfvlIVNaw8rp
# avGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIhdXNSy0bYxDQcoqVLjc1v
# djcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV9zeKiwmhywvpMRr/Lhlc
# OXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/jwVYbKyP/j7XqiHtwa+ag
# uv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYHKi8QxAwIZDwzj64ojDzL
# j4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmCXBVmzGwOysWGw/YmMwwH
# S6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l/aCnHwZ05/LWUpD9r4VI
# IflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZWeE4wgga0MIIEnKADAgEC
# AhANx6xXBf8hmS5AQyIMOkmGMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVT
# MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
# b20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yNTA1MDcw
# MDAwMDBaFw0zODAxMTQyMzU5NTlaMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5E
# aWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1l
# U3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYgMjAyNSBDQTEwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQC0eDHTCphBcr48RsAcrHXbo0ZodLRRF51NrY0NlLWZ
# loMsVO1DahGPNRcybEKq+RuwOnPhof6pvF4uGjwjqNjfEvUi6wuim5bap+0lgloM
# 2zX4kftn5B1IpYzTqpyFQ/4Bt0mAxAHeHYNnQxqXmRinvuNgxVBdJkf77S2uPoCj
# 7GH8BLuxBG5AvftBdsOECS1UkxBvMgEdgkFiDNYiOTx4OtiFcMSkqTtF2hfQz3zQ
# Sku2Ws3IfDReb6e3mmdglTcaarps0wjUjsZvkgFkriK9tUKJm/s80FiocSk1VYLZ
# lDwFt+cVFBURJg6zMUjZa/zbCclF83bRVFLeGkuAhHiGPMvSGmhgaTzVyhYn4p0+
# 8y9oHRaQT/aofEnS5xLrfxnGpTXiUOeSLsJygoLPp66bkDX1ZlAeSpQl92QOMeRx
# ykvq6gbylsXQskBBBnGy3tW/AMOMCZIVNSaz7BX8VtYGqLt9MmeOreGPRdtBx3yG
# OP+rx3rKWDEJlIqLXvJWnY0v5ydPpOjL6s36czwzsucuoKs7Yk/ehb//Wx+5kMqI
# MRvUBDx6z1ev+7psNOdgJMoiwOrUG2ZdSoQbU2rMkpLiQ6bGRinZbI4OLu9BMIFm
# 1UUl9VnePs6BaaeEWvjJSjNm2qA+sdFUeEY0qVjPKOWug/G6X5uAiynM7Bu2ayBj
# UwIDAQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU729T
# SunkBnx6yuKQVvYv1Ensy04wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4c
# D08wDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUF
# BwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEG
# CCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
# cnVzdGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5k
# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNVHSAEGTAX
# MAgGBmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIBABfO+xaA
# HP4HPRF2cTC9vgvItTSmf83Qh8WIGjB/T8ObXAZz8OjuhUxjaaFdleMM0lBryPTQ
# M2qEJPe36zwbSI/mS83afsl3YTj+IQhQE7jU/kXjjytJgnn0hvrV6hqWGd3rLAUt
# 6vJy9lMDPjTLxLgXf9r5nWMQwr8Myb9rEVKChHyfpzee5kH0F8HABBgr0UdqirZ7
# bowe9Vj2AIMD8liyrukZ2iA/wdG2th9y1IsA0QF8dTXqvcnTmpfeQh35k5zOCPmS
# Nq1UH410ANVko43+Cdmu4y81hjajV/gxdEkMx1NKU4uHQcKfZxAvBAKqMVuqte69
# M9J6A47OvgRaPs+2ykgcGV00TYr2Lr3ty9qIijanrUR3anzEwlvzZiiyfTPjLbnF
# RsjsYg39OlV8cipDoq7+qNNjqFzeGxcytL5TTLL4ZaoBdqbhOhZ3ZRDUphPvSRmM
# Thi0vw9vODRzW6AxnJll38F0cuJG7uEBYTptMSbhdhGQDpOXgpIUsWTjd6xpR6oa
# Qf/DJbg3s6KCLPAlZ66RzIg9sC+NJpud/v4+7RWsWCiKi9EOLLHfMR2ZyJ/+xhCx
# 9yHbxtl5TPau1j/1MIDpMPx0LckTetiSuEtQvLsNz3Qbp7wGWqbIiOWCnb5WqxL3
# /BAPvIXKUjPSxyZsq8WhbaM2tszWkPZPubdcMIIG7TCCBNWgAwIBAgIQCE/cM09+
# RU7bww+P+ZIYNTANBgkqhkiG9w0BAQsFADBpMQswCQYDVQQGEwJVUzEXMBUGA1UE
# ChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQg
# VGltZVN0YW1waW5nIFJTQTQwOTYgU0hBMjU2IDIwMjUgQ0ExMB4XDTI2MDgwNTAw
# MDAwMFoXDTM3MTEwNDIzNTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRp
# Z2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBTSEEyNTYgUlNBNDA5NiBU
# aW1lc3RhbXAgUmVzcG9uZGVyIDIwMjYgMTCCAiIwDQYJKoZIhvcNAQEBBQADggIP
# ADCCAgoCggIBALZ7pvLJ/s1K+NSbTGWz/TjGMPh8CQ6RucZCLv5anHzWJjF/NWJr
# FIhy24fcpKXlgRiky4WAawDfU3YP0BMxt9l3Dm5oCG5Z69AqEN1kgHg2epx+l+lZ
# BcmJCcN0ASURML5uFIS80sZsDwO3BSkUxDjLJhBI+qiZP3aixAC/qEGLjsBNlLol
# 9VZ7pfGEXiMlneJIC5/YKuizVzNFKZZEeoy/0B8Zm+nzKBgSWG52lCO1w+nCg6Xp
# CtklTJXeIg283hw7TmmsZXR+SMbjbrEOvZ3fP2VxIgeR28Y90ZStd3F9VuA5RVyn
# b/whITPAo9b75Zr4Ta6Mj3URm26QZYMn/FnbuTegcoRcFEZ9FOqM5T6MTdtr/n74
# lIT/ug0eeOzmZ6QTFg33otX+bFRsIolvykE1jive4PuESaT8zzVeFWDAMDtozNgL
# ctkGD1ZjkEyZtJrLl5ya0m5doH/ScpaZCZVl6pNUOCybMc/kxC6EAmSJY24L0yYK
# D1Nkddsnb/ItVKi/2nXpQNMu1PT5prW83vV8d67WowuUs0HdY4H8AMLGvdL/WHEj
# 3ZnqMqAQQP9u3Ai9t+5eQ02GDwy0ODjdzi0xlp70W+ow63/0++YDEX1M0iwgUHwb
# rJvfpklkZQvw3+kv3vUPItdwroczk9icflf55W1zOEKAcJVAIXpcMCU9AgMBAAGj
# ggGVMIIBkTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQUyWOKMC7USvtulPPm40B+
# 9ezN4jAfBgNVHSMEGDAWgBTvb1NK6eQGfHrK4pBW9i/USezLTjAOBgNVHQ8BAf8E
# BAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwgZUGCCsGAQUFBwEBBIGIMIGF
# MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wXQYIKwYBBQUH
# MAKGUWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
# NFRpbWVTdGFtcGluZ1JTQTQwOTZTSEEyNTYyMDI1Q0ExLmNydDBfBgNVHR8EWDBW
# MFSgUqBQhk5odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVk
# RzRUaW1lU3RhbXBpbmdSU0E0MDk2U0hBMjU2MjAyNUNBMS5jcmwwIAYDVR0gBBkw
# FzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQCNxTph
# Hp1SCt+ZrAmAfn0oQLFr0mLywSLaDXQIENoyKqxrFbJblzCVP/pkXmwXOdrOpWyg
# LzlT12os5ipDCy35RBCg2UMeApEtrfGhz45F4Wt4WGdNdIbRWt3YTYJmpR+b7lr4
# d7Uwn+H600u4D7RnOGf8Wj4UNgAdZkfHhHv1mx9EVh71SJelcEN/oORSjXzdjfw1
# iZH9d8Nh/thn6hH23d+VsPAr6GAYyzSA02nXD1nYLI7Ijmiv+xLCiYC41DSFYL3G
# hTiy0PxpawPtGRyaBVGzq+UiTfM8pD7KVyF5aQyWP4KhVGUUTnmm/RlYJoW3TiXA
# /+t0YcT2oRVBm3JETjajHug2AL+v5jhtKVnd3D0rbHXEu27o+Q8p4sEWPMqKDB+q
# bceb6T/6WcwTwXmQ9lOCLLYcsQeSWmvKqzpAec9etE14jOQAzLKWdE3w/TCaKtLR
# aRT7LCkRYVnhA2D73FLje1O5b3HR5eHs0NzU/+xX7NbEdcofy0W3Wdwd1XOqtlpg
# /JgwtKfZM5dqO94lbUveOiJBI+xZEbGRsMNbXmMREUTgu+Oca7Y73MPWcslIx2Vh
# kSKSXjDbD6rgg39H5Mh7QfieAIjWagkJNt68Yfim6cjEzVSiLSeZfdkr5dtFPTW6
# jATlWJdYeeDRGCyatf8R1hSjzSvdN8yWQPT9gzCCB2MwggVLoAMCAQICEAhTOt+G
# wz3QWpc3Z3ZgARUwDQYJKoZIhvcNAQELBQAwaTELMAkGA1UEBhMCVVMxFzAVBgNV
# BAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0
# IENvZGUgU2lnbmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENBMTAeFw0yNTEyMjYw
# MDAwMDBaFw0yODEyMjcyMzU5NTlaMGsxCzAJBgNVBAYTAlBMMRIwEAYDVQQHDAlN
# aWtvxYLDs3cxIzAhBgNVBAoTGkV2b3RlYyBTZXJ2aWNlcyBzcC4geiBvLm8uMSMw
# IQYDVQQDExpFdm90ZWMgU2VydmljZXMgc3AuIHogby5vLjCCAiIwDQYJKoZIhvcN
# AQEBBQADggIPADCCAgoCggIBANxbP/C0CFW8Z1gxi5+MxwMPmc5G8c3fIiV9S9FY
# Lic5MEFf2hrQJ2PIx/Xf7bsfkuk0cSeUTnTL/sZYI9U9Hjah1EAxSvryILnhjoyZ
# eedLTjOJkyWjoIs8cquJ8QAJgGYW8xhYp4e52MQGv8nHFkytFLebmrNJ88czDh4F
# 5+UvvYovOkxJ68kvAXU0MMdiF3SOLii+gWQRcmRFQNghMJo4jS0jDfp7EzKN5GHY
# iNbT5/xc1H16olQHZ37kIzGQlCel9+hTnPxf94eVouHo+HOQWlC55nujZAGFkID1
# h9EuRFCpPfapQVKmKSH8kxcJ1Ml11VQOE2HvFuMnOzE0DTq4qUSY7OZa5ddPXz5e
# usNIwiIVEIXfKZD+UHRZkmdoSEGoCNVJHLRDTEKxnNJeaGKihE/K+DgeCYVipITP
# 5MOwgG5nvOxILPs7CmxqaEGH9iT/xuJ4wiiBtLmvJiyJlHUOsVj0GtTdxMP5jjg2
# tIWVRAMkLRsfqSz31c9DbENEh7n8u6qqWAW3U2pWktY1i0PZt9vwfJnboZQnP91h
# +Lee9/3YGnVyXq6J/x0TkBD9tg3CD5cTBDtA3MyDYQ4AYkYoToj6QP8fJnecOHx7
# 8UuU+f3N//gAfAu1UR3cM044N3Btm6IuyaXaRrFCro8L0nAV0WFMGz/W1FWnqTfT
# SQ/XAgMBAAGjggIDMIIB/zAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfRO
# QjAdBgNVHQ4EFgQUehhCwq/shewf/KCCM+q+nhvZG/wwPgYDVR0gBDcwNTAzBgZn
# gQwBBAEwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BT
# MA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzCBtQYDVR0fBIGt
# MIGqMFOgUaBPhk1odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz
# dGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDBToFGgT4ZN
# aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNp
# Z25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcmwwgZQGCCsGAQUFBwEBBIGHMIGE
# MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wXAYIKwYBBQUH
# MAKGUGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3J0MAkGA1UdEwQCMAAw
# DQYJKoZIhvcNAQELBQADggIBAD/YXkCSn1GzVQkE3O/LO2XlSbJYUNUFpAN4F4JZ
# qAjQN1KCIdzWBPCOA844XDWo3fZox3sO/KF0tXVF2QeD7scK5bpd2gVE0tTqsWO0
# 9wR5VvzBHWZI3vWk6JfBT8pZK3++7iqpOYtZPQFYhHCk0pKqwL8K5ajrSRNoXcKK
# VK8+ITMgVuwKqRtYpCs3VjX/kEPZtVTXAOLWB875hcuaqWhHye199ZCtuV0xgp4k
# v1Euk42sN4s5nuFgS0M3ZTAAujJLSFsKnbGv086tbBiOjj/ntPwbKd2jwZu1MDSK
# rZjvKRvUvU2sK4xVu185n5Lfz4CU6j7XRVFAYYYDxvYfMJ9ABKiZgl5o++PPx66d
# gLxDKNC30YIAnQXgExaFEp1u5OCEQNJ/VmYjwl8SRvMy4A20sb9bczlFww8tpRb6
# 2WCRdhZck7rPLsPBjQgvkreachnOQAO8hMY5R4i9t88vBVq+cxj3tg0PMgqv7MWO
# mrgzBeTow7twTnSU9w1SgVoUi39M7t8pwX1E6xJQiJL+SFlBH+AJK0iByN7YZjW7
# 65xxum54/kolZb9hgWiGQABNQMBHhEGYG8LLuLdBotIFs4sL+aP7IJv5N0mierbK
# N5ROlavUwY4YIVqQSj+c1BU+f+uljQkZ5YFFt1y8oiYvTHkUkLYjL8cHVLvxTzsT
# Nd1ZMYIGWTCCBlUCAQEwfTBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
# cnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWdu
# aW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExAhAIUzrfhsM90FqXN2d2YAEVMA0G
# CWCGSAFlAwQCAQUAoIGEMBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZI
# hvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcC
# ARUwLwYJKoZIhvcNAQkEMSIEIKqMdxVrPZNzTX+vaNRg0bebhHYIBFQ5xuu4sY3H
# QDiSMA0GCSqGSIb3DQEBAQUABIICAEGrxiw0TQIVPcz/kbR49M0R0GaHz9qk4PjG
# 5JM1C/SIVmpRaSpKeg/NYBr2SG7QC+6faM+pITV651Rc74hnNHGzVIe3sr8nuPup
# M4v4qzGlEd++obq0MwCfY5QGDtSa3BJMKfkVAIl+SLqU/TDkSqJwGsDYTaeswtE2
# UUtE0DqLTkbtVO8ez4fPxMzZWZhjL1D36iSLrJKuyDIhxbYVBnVvVhV6MmLdOtk8
# GV9fQKLTQTijMSDtrLNJexEiApkVgQZR4O14jQkldE1VI3NB4wc5vyImR50eoSSl
# iAOJ/Nywk4aS06rDJujACtQrgJYZdCtAV7ZZ68he27pctm4wjniv/wNVai35w4a3
# F+V00Q76/YWkjk911yDhXj2PgT4DwfO3k+NKQmK3AD/i4Gpai69KaMaI1GSOw1BR
# Emjgc6eCtAwdttlPWN+QbK0u+NOpihwXJy3lHJRxZ0EQ1SOk2OIMwQke1OQHV0DM
# HOaf3zUHO9ZnrJkplBoGd7AO5i3xkq9QoSCDHdTnE+SjxyUxoOiaYyGXZ1ugFpxa
# gk+KOL5YDbH6TanHZfhXNb3ufxKPv9GCClzEn+t8V+8oa23oPLoety2aj13KgqQm
# 5F+gSr4miYoYyUyQ9Y4EN9uwGcSdTeoW9FwFlBvZAp6dkTjDXTxvt/sMfvG7MiDp
# 72zHRg1IoYIDJjCCAyIGCSqGSIb3DQEJBjGCAxMwggMPAgEBMH0waTELMAkGA1UE
# BhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2Vy
# dCBUcnVzdGVkIEc0IFRpbWVTdGFtcGluZyBSU0E0MDk2IFNIQTI1NiAyMDI1IENB
# MQIQCE/cM09+RU7bww+P+ZIYNTANBglghkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJ
# AzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTI2MDkwODA3NTA0NFowLwYJ
# KoZIhvcNAQkEMSIEIC8KgV+Gu0zBT6CUUm8gh5G7Zv09Nv+fHaDFbFB8UaWRMA0G
# CSqGSIb3DQEBAQUABIICAIjH3HUhUGcoapWPnKytsxe0RRH+qOhyV60s8H0yXe3S
# h3zc0E91J6qOvHjVE26fbn8ADbwrR6hzAC3xDRwJ92SNvrPOCmCuFl2zBscQrPTo
# xaHo/FnaqrXQpP7V7yyAW+c0FJd7RPEiLDNCX5yDMTmVjHFZpRZjpItDZnNaPXtK
# yPna9wE2CWUy5YC19pvdnpqwEVU1xui+D0C3mhUCEl3LQ9ZqpkHUSBxFqEQyahQM
# SZy0eHdJDbVHOvHA0ZH2uKLSTnQicHIKzBwjiqWJM5J1J6fPOy4adGw/4FB1MTnU
# cDQXpq1Mxxk7qqcLM6OWo3q8cKfGuDpNIRytwBLexzYlAVkZklhIGoJ7fgpY7qpP
# Xhjf9Pa4aYyZh9shFZ/ZTwfpNINKbnCzDSn8TdYA42glcnIO8D+69vyNS7K4LbOL
# S8OoD2mFf6twg+9AVc0kikcAJribt0whzZYFHI3IchKJXPoZ3A7zClGCFZ8EF8ko
# Ed2Lz40nSiNfJNtIwh27db+HpsMp7OSiXfueVn8e33kfWXMXyHBkdXA4tdOptZdv
# 5ysFXf60zFLbw5YcU3oMBcRTu7j5ORYlZ/BVCF3IFsVYrIcneEq3fmay1dmCnllu
# pgV+/41fIDd1eH3YD8yoe5jXCxR7YkYSmX+96/8y/qSF6K74Yk0oo8YAWfJ/jwn9
# SIG # End signature block