UMN-Grouper.psm1

#region License
    # Copyright 2017 University of Minnesota, Office of Information Technology

    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.

    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    # GNU General Public License for more details.

    # You should have received a copy of the GNU General Public License
    # along with Foobar. If not, see <http://www.gnu.org/licenses/>.
#endregion

#region New-GrouperHeader
    function New-GrouperHeader
    {
        <#
            .SYNOPSIS
                Create Header to be consumed by all other functions

            .DESCRIPTION
                Create Header to be consumed by all other functions

            .PARAMETER psCreds
                PScredential composed of your username/password to Server

            .NOTES
                Author: Travis Sobeck
                LASTEDIT: 6/20/2018

            .EXAMPLE

        #>

        [CmdletBinding()]
        param
        (
            [Parameter(Mandatory)]
            [System.Management.Automation.PSCredential]$psCreds
        )
        $auth = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($psCreds.UserName+':'+$psCreds.GetNetworkCredential().Password))
        return (@{"Authorization" = "Basic $auth"})
    }
#endregion

#region Get-GrouperGroup
    function Get-GrouperGroup
    {
        <#
            .SYNOPSIS
                Get Grouper Group(s)

            .DESCRIPTION
                Get Grouper Group(s)

            .PARAMETER uri
                Full path to Server plus path to API
                Example "https://<FQDN>/grouper-ws/servicesRest/json/v2_2_100"

            .PARAMETER header
                Use New-Header to get this

            .PARAMETER contentType
                Set Content Type, currently 'text/x-json;charset=UTF-8'

            .PARAMETER groupName
                Use this if you know the exact name

            .PARAMETER stemName
                Use this to get a list of groups in a specific stem. Use Get-GrouperStem to find stem
            
            .PARAMETER subjectId
                Set this to a username to search as that user if you have access to

            .NOTES
                Author: Travis Sobeck
                LASTEDIT: 7/30/2018

            .EXAMPLE
        #>

        [CmdletBinding()]
        param
        (
            [Parameter(Mandatory)]
            [string]$uri,

            [Parameter(Mandatory)]
            [System.Collections.Hashtable]$header,

            [string]$contentType = 'text/x-json;charset=UTF-8',

            [Parameter(Mandatory,ParameterSetName='groupName')]
            [string]$groupName,

            [Parameter(Mandatory,ParameterSetName='stemName')]
            [string]$stemName,

            [string]$subjectId
        )

        Begin{}

        Process
        {
            if ($groupName)
            {
                Write-Warning "This Option has not been coded yet"
            }
            else
            {
                $uri = "$uri/groups"
                $groupName = "$stemName`:"
                $body = @{
                        WsRestFindGroupsRequest = @{
                            wsQueryFilter = @{groupName = $groupName;queryFilterType = 'FIND_BY_GROUP_NAME_APPROXIMATE'}
                        }
                } 
                if ($subjectId)
                {
                    
                    $body['WsRestFindGroupsRequest']['actAsSubjectLookup'] = @{subjectId = $subjectId};
                }
                $body = $body | ConvertTo-Json -Depth 5
                $response = Invoke-WebRequest -Uri $uri -Headers $header -Method Post -Body $body -UseBasicParsing -ContentType $contentType
                return ($response.Content | ConvertFrom-Json).WsFindGroupsResults.groupResults
            }
        }

        End{}
    }
#endregion

#region Get-GrouperGroupMembers
    function Get-GrouperGroupMembers
    {
        <#
            .SYNOPSIS
                Get List of Members in a Group

            .DESCRIPTION
                Get List of Members in a Group

            .PARAMETER uri
                Full path to Server plus path to API
                Example "https://<FQDN>/grouper-ws/servicesRest/json/v2_2_100"

            .PARAMETER header
                Use New-Header to get this

            .PARAMETER contentType
                Set Content Type, currently 'text/x-json;charset=UTF-8'

            .PARAMETER groupName
                This represents the identifier for the group, it should look like 'stemname:group'
                Example: stem1:substem:supergroup

            .NOTES
                Author: Travis Sobeck
                LASTEDIT: 7/30/2018

            .EXAMPLE
        #>

        [CmdletBinding()]
        param
        (
            [Parameter(Mandatory)]
            [string]$uri,

            [Parameter(Mandatory)]
            [System.Collections.Hashtable]$header,

            [string]$contentType = 'text/x-json;charset=UTF-8',

            [Parameter(Mandatory)]
            [string]$groupName
        )

        Begin{}

        Process
        {
            $uri = "$uri/groups"
            $body = @{
                WsRestGetMembersRequest = @{
                    subjectAttributeNames = @("description","name")
                    wsGroupLookups = @(@{groupName = $groupName})
                }
            } | ConvertTo-Json -Depth 5
            $response = Invoke-WebRequest -Uri $uri -Headers $header -Method Post -Body $body -UseBasicParsing -ContentType $contentType
            return ($response.Content | ConvertFrom-Json).WsGetMembersResults.results.wsSubjects
        }

        End{}
    }
#endregion

#region Get-GrouperPrivileges
function Get-GrouperPrivileges
{
    <#
        .SYNOPSIS
            Get Grouper Privileges

        .DESCRIPTION
            Get Grouper Privileges

        .PARAMETER uri
            Full path to Server plus path to API
            Example "https://<FQDN>/grouper-ws/servicesRest/json/v2_2_100"

        .PARAMETER header
            Use New-Header to get this

        .PARAMETER contentType
            Set Content Type, currently 'text/x-json;charset=UTF-8'

        .PARAMETER stemName
            stemName
        
        .PARAMETER subjectId
            Filter result for a specific user

        .PARAMETER actAsSubjectId
            User security context to restrict search to. ie search as this user

        .NOTES
            Author: Travis Sobeck
            LASTEDIT: 7/30/2018

        .EXAMPLE
    #>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory)]
        [string]$uri,

        [Parameter(Mandatory)]
        [System.Collections.Hashtable]$header,

        [string]$contentType = 'text/x-json;charset=UTF-8',

        [Parameter(Mandatory,ParameterSetName='stem')]
        [string]$stemName,

        [Parameter(Mandatory,ParameterSetName='group')]
        [string]$groupName,

        [string]$actAsSubjectId,

        [string]$subjectId
    )

    Begin{}
    Process
    {
        $uri = "$uri/grouperPrivileges"
        $body = @{
            WsRestGetGrouperPrivilegesLiteRequest = @{}
        } 
        if ($subjectId)
        {
            
            $body['WsRestGetGrouperPrivilegesLiteRequest']['actAsSubjectId'] = $subjectId
        }
        if ($actAsSubjectId)
        {
            
            $body['WsRestGetGrouperPrivilegesLiteRequest']['actAsSubjectId'] = $actAsSubjectId
        }
        if ($groupName)
        {
            
            $body['WsRestGetGrouperPrivilegesLiteRequest']['groupName'] = $groupName
        }
        if ($stemName)
        {
            
            $body['WsRestGetGrouperPrivilegesLiteRequest']['stemName'] = $stemName
        }
        
        $body = $body | ConvertTo-Json -Depth 5
        $response = Invoke-WebRequest -Uri $uri -Headers $header -Method Post -Body $body -UseBasicParsing -ContentType $contentType
        return ($response.Content | ConvertFrom-Json).WsGetGrouperPrivilegesLiteResult.privilegeResults
        if (($response.Content | ConvertFrom-Json).WsFindStemsResults.stemResults.count -gt 0)
        {
            ($response.Content | ConvertFrom-Json).WsFindStemsResults.stemResults
        }
        else {
            Write-Verbose "NO results found"
        }
    }
    End{}
}
#endregion

#region Get-GrouperStem
    function Get-GrouperStem
    {
        <#
            .SYNOPSIS
                Get Grouper Stem(s)

            .DESCRIPTION
                Get a Grouper Stem or use the -search switch to get all Grouper Stem(s) that match stem pattern
                From API docs -- find by approx name, pass the name in. stem name is required

            .PARAMETER uri
                Full path to Server plus path to API
                Example "https://<FQDN>/grouper-ws/servicesRest/json/v2_2_100"

            .PARAMETER header
                Use New-Header to get this

            .PARAMETER contentType
                Set Content Type, currently 'text/x-json;charset=UTF-8'

            .PARAMETER stemName
                stemName
            
            .PARAMETER search
                Switch to do a search. Use with the caution, results from grouper API are not very reliable

            .PARAMETER subjectId
                Set this to a username to search as that user if you have access to

            .NOTES
                Author: Travis Sobeck
                LASTEDIT: 7/30/2018

            .EXAMPLE
        #>

        [CmdletBinding()]
        param
        (
            [Parameter(Mandatory)]
            [string]$uri,

            [Parameter(Mandatory)]
            [System.Collections.Hashtable]$header,

            [string]$contentType = 'text/x-json;charset=UTF-8',

            [Parameter(Mandatory)]
            [string]$stemName,
            
            [switch]$search,

            [string]$subjectId
        )

        Begin{}

        Process
        {
            $uri = "$uri/stems"
            $body = @{
                    WsRestFindStemsRequest = @{
                        wsStemQueryFilter = @{stemName = $stemName}
                    }
            }

            if ($search){$body['WsRestFindStemsRequest']['wsStemQueryFilter']['stemQueryFilterType'] = 'FIND_BY_STEM_NAME_APPROXIMATE'}
            else{$body['WsRestFindStemsRequest']['wsStemQueryFilter']['stemQueryFilterType'] = 'FIND_BY_STEM_NAME'}

            if ($subjectId)
            {                
                $body['WsRestFindStemsRequest']['actAsSubjectLookup'] = @{subjectId = $subjectId};
            }
            $body = $body | ConvertTo-Json -Depth 5
            $response = Invoke-WebRequest -Uri $uri -Headers $header -Method Post -Body $body -UseBasicParsing -ContentType $contentType
            if (($response.Content | ConvertFrom-Json).WsFindStemsResults.stemResults.count -gt 0)
            {
                ($response.Content | ConvertFrom-Json).WsFindStemsResults.stemResults
            }
            else {
                Write-Verbose "NO results found"
            }
        }

        End{}
    }
#endregion

#region Get-GrouperStemByParent
    function Get-GrouperStemByParent
    {
        <#
            .SYNOPSIS
                Get Grouper child Stem(s) of a parent stem

            .DESCRIPTION
                Get Grouper child Stem(s) of a parent stem

            .PARAMETER uri
                Full path to Server plus path to API
                Example "https://<FQDN>/grouper-ws/servicesRest/json/v2_2_100"

            .PARAMETER header
                Use New-Header to get this

            .PARAMETER contentType
                Set Content Type, currently 'text/x-json;charset=UTF-8'

            .PARAMETER parentStemName
                stemName of Parent
            
            .PARAMETER noRecursion
                By default the function will recursivly search for all sub-stems, use this switch to only get stems one level below the parent stem
            
            .PARAMETER subjectId
                Set this to a username to search as that user if you have access to

            .NOTES
                Author: Travis Sobeck
                LASTEDIT: 7/30/2018

            .EXAMPLE
        #>

        [CmdletBinding()]
        param
        (
            [Parameter(Mandatory)]
            [string]$uri,

            [Parameter(Mandatory)]
            [System.Collections.Hashtable]$header,

            [string]$contentType = 'text/x-json;charset=UTF-8',

            [Parameter(Mandatory)]
            [string]$parentStemName,

            [switch]$noRecursion,

            [string]$subjectId
        )

        Begin{}

        Process
        {
            $uri = "$uri/stems"
            $body = @{
                    WsRestFindStemsRequest = @{
                        wsStemQueryFilter = @{parentStemName = $parentStemName;stemQueryFilterType = 'FIND_BY_PARENT_STEM_NAME'}
                    }
            }
            if($noRecursion){$body['WsRestFindStemsRequest']['wsStemQueryFilter']["parentStemNameScope"] = 'ONE_LEVEL'}
            else{$body['WsRestFindStemsRequest']['wsStemQueryFilter']["parentStemNameScope"] = 'ALL_IN_SUBTREE'}

            if ($subjectId)
            {
                
                $body['WsRestFindStemsRequest']['actAsSubjectLookup'] = @{subjectId = $subjectId};
            }
            $body = $body | ConvertTo-Json -Depth 5
            Write-Verbose -Message $body
            $response = Invoke-WebRequest -Uri $uri -Headers $header -Method Post -Body $body -UseBasicParsing -ContentType $contentType
            if (($response.Content | ConvertFrom-Json).WsFindStemsResults.stemResults.count -gt 0)
            {
                ($response.Content | ConvertFrom-Json).WsFindStemsResults.stemResults
            }
            else {
                Write-Verbose "NO results found"
            }
        }

        End{}
    }
#endregion

#region Get-GrouperStemByUUID
function Get-GrouperStemByUUID
{
    <#
        .SYNOPSIS
            Get a Grouper Stem by its UUID

        .DESCRIPTION
            Get a Grouper Stem by its UUID

        .PARAMETER uri
            Full path to Server plus path to API
            Example "https://<FQDN>/grouper-ws/servicesRest/json/v2_2_100"

        .PARAMETER header
            Use New-Header to get this

        .PARAMETER contentType
            Set Content Type, currently 'text/x-json;charset=UTF-8'

        .PARAMETER uuid
            UUID of the stem to retrieve

        .PARAMETER subjectId
            Set this to a username to search as that user if you have access to

        .NOTES
            Author: Travis Sobeck
            LASTEDIT: 7/30/2018

        .EXAMPLE
    #>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory)]
        [string]$uri,

        [Parameter(Mandatory)]
        [System.Collections.Hashtable]$header,

        [string]$contentType = 'text/x-json;charset=UTF-8',

        [Parameter(Mandatory)]
        [string]$uuid,

        [string]$subjectId
    )

    Begin{}

    Process
    {
        $uri = "$uri/stems"
        $body = @{
                WsRestFindStemsRequest = @{
                    wsStemQueryFilter = @{stemUuid = $uuid;stemQueryFilterType = 'FIND_BY_STEM_UUID'}
                }
        }

        if ($subjectId)
        {
            
            $body['WsRestFindStemsRequest']['actAsSubjectLookup'] = @{subjectId = $subjectId};
        }
        $body = $body | ConvertTo-Json -Depth 5
        $response = Invoke-WebRequest -Uri $uri -Headers $header -Method Post -Body $body -UseBasicParsing -ContentType $contentType
        if (($response.Content | ConvertFrom-Json).WsFindStemsResults.stemResults.count -gt 0)
        {
            ($response.Content | ConvertFrom-Json).WsFindStemsResults.stemResults
        }
        else {
            Write-Verbose "NO results found"
        }
    }

    End{}
}
#endregion

#region New-GrouperGroup
    function New-GrouperGroup
    {
        <#
            .SYNOPSIS
                Create new Group in Grouper

            .DESCRIPTION
                Create new Group in Grouper

            .PARAMETER uri
                Full path to Server plus path to API
                Example "https://<FQDN>/grouper-ws/servicesRest/json/v2_2_100"

            .PARAMETER header
                Use New-Header to get this

            .PARAMETER contentType
                Set Content Type, currently 'text/x-json;charset=UTF-8'

            .PARAMETER groupName
                This represents the identifier for the group, it should look like 'stemname:group'
                Example: stem1:substem:supergroup

            .PARAMETER description
                The description represents the the Name in the form users in the UI will see the group

            .NOTES
                Author: Travis Sobeck
                LASTEDIT: 7/30/2018

            .EXAMPLE
        #>

        [CmdletBinding()]
        param
        (
            [Parameter(Mandatory)]
            [string]$uri,

            [Parameter(Mandatory)]
            [System.Collections.Hashtable]$header,

            [string]$contentType = 'text/x-json;charset=UTF-8',

            [Parameter(Mandatory)]
            [string]$groupName,

            [Parameter(Mandatory)]
            [string]$description
        )

        Begin{}

        Process
        {
            $uri = "$uri/groups"
            $body = @{
                WsRestGroupSaveRequest = @{
                    wsGroupToSaves = @(@{wsGroup = @{description = $description;displayExtension = $description;extension = $description;name = $groupName};wsGroupLookup = @{groupName = $groupName}})
                }
            } | ConvertTo-Json -Depth 5
            ($response = Invoke-WebRequest -Uri $uri -Headers $header -Method Post -Body $body -UseBasicParsing -ContentType $contentType)
            return ($response.Content | ConvertFrom-Json).WsGroupSaveResults.results.wsGroup
        }

        End{}
    }
#endregion

#region New-GrouperGroupMember
    function New-GrouperGroupMember
    {
        <#
            .SYNOPSIS
                Add a user to a Group

            .DESCRIPTION
                Add a user to a Group

            .PARAMETER uri
                Full path to Server plus path to API
                Example "https://<FQDN>/grouper-ws/servicesRest/json/v2_2_100"

            .PARAMETER header
                Use New-Header to get this

            .PARAMETER contentType
                Set Content Type, currently 'text/x-json;charset=UTF-8'

            .PARAMETER groupName
                This represents the identifier for the group, it should look like 'stemname:group'
                Example: stem1:substem:supergroup

            .PARAMETER subjectId

            .PARAMETER subjectSourceId
                Source location of subjectId, ie ldap
            .NOTES
                Author: Travis Sobeck
                LASTEDIT: 7/30/2018

            .EXAMPLE
        #>

        [CmdletBinding()]
        param
        (
            [Parameter(Mandatory)]
            [string]$uri,

            [Parameter(Mandatory)]
                [System.Collections.Hashtable]$header,

            [string]$contentType = 'text/x-json;charset=UTF-8',

            [Parameter(Mandatory)]
            [string]$groupName,

            [Parameter(Mandatory)]
            [string]$subjectId,

            [string]$subjectSourceId
        )

        Begin{}

        Process
        {
            $uri = "$uri/groups"
            $subjectLookups = @(@{subjectId = $subjectId})
            if ($subjectSourceId){$subjectLookups[0]['subjectSourceId'] = $subjectSourceId}
                $body = @{
                    WsRestAddMemberRequest = @{
                        subjectLookups = $subjectLookups
                        wsGroupLookup = @{groupName = $groupName}
                    }
                } | ConvertTo-Json -Depth 5
                $response = Invoke-WebRequest -Uri $uri -Headers $header -Method Post -Body $body -UseBasicParsing -ContentType $contentType
                return @(($response.Content | ConvertFrom-Json).WsAddMemberResults.results.wsSubject,($response.Content | ConvertFrom-Json).WsAddMemberResults.wsGroupAssigned)
        }

        End{}
    }
#endregion

#region New-GrouperPrivileges
function New-GrouperPrivileges
{
    <#
        .SYNOPSIS
            Set Grouper Privileges

        .DESCRIPTION
            Set Grouper Privileges)

        .PARAMETER uri
            Full path to Server plus path to API
            Example "https://<FQDN>/grouper-ws/servicesRest/json/v2_2_100"

        .PARAMETER header
            Use New-Header to get this

        .PARAMETER contentType
            Set Content Type, currently 'text/x-json;charset=UTF-8'

        .PARAMETER stemName
            stemName
        
        .PARAMETER subjectId
            User to apply Privilege to

        .PARAMETER actAsSubjectId
            User security context to use to apply change

        .PARAMETER privilegeName
            Name of privilege to apply, see Get-GrouperPrivileges for examples

        .PARAMETER subjectIdIsAGroup
            Use this switch (set to true) if the subjectID is actually a GroupName. The default assumption is that the subjectID is a users ID

        .NOTES
            Author: Travis Sobeck
            LASTEDIT: 7/30/2018

        .EXAMPLE
    #>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory)]
        [string]$uri,

        [Parameter(Mandatory)]
        [System.Collections.Hashtable]$header,

        [string]$contentType = 'text/x-json;charset=UTF-8',

        [Parameter(Mandatory,ParameterSetName='stem')]
        [string]$stemName,

        [Parameter(Mandatory,ParameterSetName='group')]
        [string]$groupName,

        [string]$actAsSubjectId,

        [Parameter(Mandatory)]
        [string]$subjectId,

        [switch]$subjectIdIsAGroup = $false,

        [Parameter(Mandatory)]
        [string]$privilegeName
    )

    Begin{}
    Process
    {
        $uri = "$uri/grouperPrivileges"
        $body = @{
            WsRestAssignGrouperPrivilegesLiteRequest = @{
                allowed = 'T'
                privilegeName = $privilegeName                
            }
        }
        if ($subjectIdIsAGroup)
        {
            $body['WsRestAssignGrouperPrivilegesLiteRequest']['subjectIdentifier'] = $subjectId
            $body['WsRestAssignGrouperPrivilegesLiteRequest']['subjectSourceId'] = "g:gsa"
        }
        else {$body['WsRestAssignGrouperPrivilegesLiteRequest']['subjectId'] = $subjectId}

        if ($actAsSubjectId)
        {
            
            $body['WsRestAssignGrouperPrivilegesLiteRequest']['actAsSubjectId'] = $actAsSubjectId
        }
        if ($groupName)
        {
            
            $body['WsRestAssignGrouperPrivilegesLiteRequest']['groupName'] = $groupName
            $body['WsRestAssignGrouperPrivilegesLiteRequest']['privilegeType'] = 'access'
        }
        if ($stemName)
        {
            
            $body['WsRestAssignGrouperPrivilegesLiteRequest']['stemName'] = $stemName
            $body['WsRestAssignGrouperPrivilegesLiteRequest']['privilegeType'] = 'naming'
        }
        
        $body = $body | ConvertTo-Json -Depth 5
        #Write-Debug $body
        $response = Invoke-WebRequest -Uri $uri -Headers $header -Method Post -Body $body -UseBasicParsing -ContentType $contentType
        return ($response.Content | ConvertFrom-Json).WsGetGrouperPrivilegesLiteResult.privilegeResults
        if (($response.Content | ConvertFrom-Json).WsFindStemsResults.stemResults.count -gt 0)
        {
            ($response.Content | ConvertFrom-Json).WsFindStemsResults.stemResults
        }
        else {
            Write-Verbose "NO results found"
        }
    }
    End{}
}
#endregion

#region New-GrouperStem
    function New-GrouperStem
    {
        <#
            .SYNOPSIS
                Create new Stem in Grouper

            .DESCRIPTION
                Create new Stem in Grouper

            .PARAMETER uri
                Full path to Server plus path to API
                Example "https://<FQDN>/grouper-ws/servicesRest/json/v2_2_100"

            .PARAMETER header
                Use New-Header to get this

            .PARAMETER contentType
                Set Content Type, currently 'text/x-json;charset=UTF-8'

            .PARAMETER stemName
                This represents the identifier for the stem, it should look like 'stemParentA:stemParentB:stemname'
                Example: stem1:substem:newstem

            .PARAMETER description
                The description represents the the Name in the form users in the UI will see the group

            .NOTES
                Author: Travis Sobeck
                LASTEDIT: 7/30/2018

            .EXAMPLE
        #>

        [CmdletBinding()]
        param
        (
            [Parameter(Mandatory)]
            [string]$uri,

            [Parameter(Mandatory)]
            [System.Collections.Hashtable]$header,

            [string]$contentType = 'text/x-json;charset=UTF-8',

            [Parameter(Mandatory)]
            [string]$stemName,

            [Parameter(Mandatory)]
            [string]$description
        )

        Begin{}

        Process
        {
            $uri = "$uri/stems"
            $body = @{
                WsRestStemSaveRequest = @{
                    wsStemToSaves = @(@{wsStem = @{description = $description;displayExtension = $description;name = $stemName};wsStemLookup = @{stemName = $stemName}})
                }
            } | ConvertTo-Json -Depth 5
            ($response = Invoke-WebRequest -Uri $uri -Headers $header -Method Post -Body $body -UseBasicParsing -ContentType $contentType)
            return ($response.Content | ConvertFrom-Json).WsStemSaveResults.results.wsStem
        }

        End{}
    }
#endregion

#region Remove-GrouperGroup
    function Remove-GrouperGroup
    {
        <#
            .SYNOPSIS
                Remove a Grouper Group

            .DESCRIPTION
                Remove a Grouper Group

            .PARAMETER uri
                Full path to Server plus path to API
                Example "https://<FQDN>/grouper-ws/servicesRest/json/v2_2_100"

            .PARAMETER header
                Use New-Header to get this

            .PARAMETER contentType
                Set Content Type, currently 'text/x-json;charset=UTF-8'

            .PARAMETER groupName
                The groupName, use Get-GrouperGroup to the get the "name" field

            .NOTES
                Author: Travis Sobeck
                LASTEDIT: 7/30/2018

            .EXAMPLE
        #>

        [CmdletBinding()]
        param
        (
            [Parameter(Mandatory)]
            [string]$uri,

            [Parameter(Mandatory)]
            [System.Collections.Hashtable]$header,

            [string]$contentType = 'text/x-json;charset=UTF-8',

            [Parameter(Mandatory)]
            [string[]]$groupName
        )

        Begin{}

        Process
        {
            $uri = "$uri/groups"
            <# This didn't seem to work :()
                foreach ($gn in $groupName)
                {
                    $gnArray = $gnArray + @{groupName = $gn}
                }
                $body = @{
                    WsRestGroupDeleteRequest = @{
                        wsGroupLookups = $gnArray
                    }
                } | ConvertTo-Json -Depth 5
            #>

            foreach ($gn in $groupName)
            {
                $body = @{
                    WsRestGroupDeleteRequest = @{
                        wsGroupLookups = @(@{groupName = $gn})
                    }
                } | ConvertTo-Json -Depth 5
                $response = Invoke-WebRequest -Uri $uri -Headers $header -Method Post -Body $body -UseBasicParsing -ContentType $contentType
                $deletedGroups = ($response.Content | ConvertFrom-Json).WsGroupDeleteResults.results.wsGroup
                $deletedGroups
            }
            
            #return ($response.Content | ConvertFrom-Json).WsGroupDeleteResults.results.resultMetadata.resultCode
        }

        End{}
    }
#endregion

#region Remove-GrouperStem
    function Remove-GrouperStem
    {
        <#
            .SYNOPSIS
                Remove a Grouper Stem

            .DESCRIPTION
                Remove a Grouper Stem

            .PARAMETER uri
                Full path to Server plus path to API
                Example "https://<FQDN>/grouper-ws/servicesRest/json/v2_2_100"

            .PARAMETER header
                Use New-Header to get this

            .PARAMETER contentType
                Set Content Type, currently 'text/x-json;charset=UTF-8'

            .PARAMETER stemName
                Use Get-GrouperStem to find name

            .PARAMETER removeGroups
                Grouper will not remove a Stem with other Stems or Groups in it. Set this to remove all the groups first

            .PARAMETER recursive
                Recursively remove all child stems

            .NOTES
                Author: Travis Sobeck
                LASTEDIT: 7/30/2018

            .EXAMPLE
        #>

        [CmdletBinding()]
        param
        (
            [Parameter(Mandatory)]
            [string]$uri,

            [Parameter(Mandatory)]
            [System.Collections.Hashtable]$header,

            [string]$contentType = 'text/x-json;charset=UTF-8',

            [Parameter(Mandatory)]
            [string]$stemName,

            [switch]$removeGroups,

            [switch]$recursive
        )

        Begin{}

        Process
        {
            if ($recursive)
            {
                $stemNames = (Get-GrouperStemByParent -uri $uri -header $header -parentStemName $stemName -noRecursion).Name
                Write-Verbose "Child Stems: $stemNames"
                foreach ($stem in $stemNames)
                {
                    (Remove-GrouperStem -uri $uri -header $header -stemName $stem -removeGroups:$removeGroups -recursive:$recursive).name
                }
            }
            if ($removeGroups)
            {
                # Get all the groups
                $groupNames = (Get-GrouperGroup -uri $uri -header $header -stemName $stemName).name
                # Remove the groups
                Write-Verbose "Child groups: $groupNames"
                foreach ($groupName in $groupNames)
                {
                    $null = Remove-GrouperGroup -uri $uri -header $header -groupName $groupName                    
                }
                Start-Sleep -Seconds 1           
            }
            $uri = "$uri/stems"
            $body = @{
                WsRestStemDeleteRequest = @{
                    wsStemLookups = @(@{stemName = $stemName})
                }
            } | ConvertTo-Json -Depth 5
            $response = Invoke-WebRequest -Uri $uri -Headers $header -Method Post -Body $body -UseBasicParsing -ContentType $contentType
            $removedStems = ($response.Content | ConvertFrom-Json).WsStemDeleteResults.results.wsStem
            return $removedStems
            #($response.Content | ConvertFrom-Json).WsStemDeleteResults.results.resultMetadata.resultCode
        }

        End{}
    }
#endregion

# SIG # Begin signature block
# MIIfBwYJKoZIhvcNAQcCoIIe+DCCHvQCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUJqL5ZXOWMlfCHVZ3g4ov+7rH
# a9WgghoTMIIEhDCCA2ygAwIBAgIQQhrylAmEGR9SCkvGJCanSzANBgkqhkiG9w0B
# AQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNV
# BAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRU
# cnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTA1MDYwNzA4MDkxMFoXDTIwMDUzMDEw
# NDgzOFowgZUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2Fs
# dCBMYWtlIENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8G
# A1UECxMYaHR0cDovL3d3dy51c2VydHJ1c3QuY29tMR0wGwYDVQQDExRVVE4tVVNF
# UkZpcnN0LU9iamVjdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6q
# gT+jo2F4qjEAVZURnicPHxzfOpuCaDDASmEd8S8O+r5596Uj71VRloTN2+O5bj4x
# 2AogZ8f02b+U60cEPgLOKqJdhwQJ9jCdGIqXsqoc/EHSoTbL+z2RuufZcDX65OeQ
# w5ujm9M89RKZd7G3CeBo5hy485RjiGpq/gt2yb70IuRnuasaXnfBhQfdDWy/7gbH
# d2pBnqcP1/vulBe3/IW+pKvEHDHd17bR5PDv3xaPslKT16HUiaEHLr/hARJCHhrh
# 2JU022R5KP+6LhHC5ehbkkj7RwvCbNqtMoNB86XlQXD9ZZBt+vpRxPm9lisZBCzT
# bafc8H9vg2XiaquHhnUCAwEAAaOB9DCB8TAfBgNVHSMEGDAWgBStvZh6NLQm9/rE
# JlTvA73gJMtUGjAdBgNVHQ4EFgQU2u1kdBScFDyr3ZmpvVsoTYs8ydgwDgYDVR0P
# AQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0gBAowCDAGBgRVHSAAMEQG
# A1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9BZGRUcnVz
# dEV4dGVybmFsQ0FSb290LmNybDA1BggrBgEFBQcBAQQpMCcwJQYIKwYBBQUHMAGG
# GWh0dHA6Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEFBQADggEBAE1C
# L6bBiusHgJBYRoz4GTlmKjxaLG3P1NmHVY15CxKIe0CP1cf4S41VFmOtt1fcOyu9
# 08FPHgOHS0Sb4+JARSbzJkkraoTxVHrUQtr802q7Zn7Knurpu9wHx8OSToM8gUmf
# ktUyCepJLqERcZo20sVOaLbLDhslFq9s3l122B9ysZMmhhfbGN6vRenf+5ivFBjt
# pF72iZRF8FUESt3/J90GSkD2tLzx5A+ZArv9XQ4uKMG+O18aP5cQhLwWPtijnGMd
# ZstcX9o+8w8KCTUi29vAPwD55g1dZ9H9oB4DK9lA977Mh2ZUgKajuPUZYtXSJrGY
# Ju6ay0SnRVqBlRUa9VEwggTmMIIDzqADAgECAhBiXE2QjNVC+6supXM/8VQZMA0G
# CSqGSIb3DQEBBQUAMIGVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNV
# BAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdv
# cmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEdMBsGA1UEAxMU
# VVROLVVTRVJGaXJzdC1PYmplY3QwHhcNMTEwNDI3MDAwMDAwWhcNMjAwNTMwMTA0
# ODM4WjB6MQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVy
# MRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDEg
# MB4GA1UEAxMXQ09NT0RPIFRpbWUgU3RhbXBpbmcgQ0EwggEiMA0GCSqGSIb3DQEB
# AQUAA4IBDwAwggEKAoIBAQCqgvGEqVvYcbXSXSvt9BMgDPmb6dGPdF5u7uspSNjI
# vizrCmFgzL2SjXzddLsKnmhOqnUkcyeuN/MagqVtuMgJRkx+oYPp4gNgpCEQJ0Ca
# WeFtrz6CryFpWW1jzM6x9haaeYOXOh0Mr8l90U7Yw0ahpZiqYM5V1BIR8zsLbMaI
# upUu76BGRTl8rOnjrehXl1/++8IJjf6OmqU/WUb8xy1dhIfwb1gmw/BC/FXeZb5n
# OGOzEbGhJe2pm75I30x3wKoZC7b9So8seVWx/llaWm1VixxD9rFVcimJTUA/vn9J
# AV08m1wI+8ridRUFk50IYv+6Dduq+LW/EDLKcuoIJs0ZAgMBAAGjggFKMIIBRjAf
# BgNVHSMEGDAWgBTa7WR0FJwUPKvdmam9WyhNizzJ2DAdBgNVHQ4EFgQUZCKGtkqJ
# yQQP0ARYkiuzbj0eJ2wwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8C
# AQAwEwYDVR0lBAwwCgYIKwYBBQUHAwgwEQYDVR0gBAowCDAGBgRVHSAAMEIGA1Ud
# HwQ7MDkwN6A1oDOGMWh0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZp
# cnN0LU9iamVjdC5jcmwwdAYIKwYBBQUHAQEEaDBmMD0GCCsGAQUFBzAChjFodHRw
# Oi8vY3J0LnVzZXJ0cnVzdC5jb20vVVROQWRkVHJ1c3RPYmplY3RfQ0EuY3J0MCUG
# CCsGAQUFBzABhhlodHRwOi8vb2NzcC51c2VydHJ1c3QuY29tMA0GCSqGSIb3DQEB
# BQUAA4IBAQARyT3hBeg7ZazJdDEDt9qDOMaSuv3N+Ntjm30ekKSYyNlYaDS18Ash
# U55ZRv1jhd/+R6pw5D9eCJUoXxTx/SKucOS38bC2Vp+xZ7hog16oYNuYOfbcSV4T
# p5BnS+Nu5+vwQ8fQL33/llqnA9abVKAj06XCoI75T9GyBiH+IV0njKCv2bBS7vzI
# 7bec8ckmONalMu1Il5RePeA9NbSwyVivx1j/YnQWkmRB2sqo64sDvcFOrh+RMrjh
# JDt77RRoCYaWKMk7yWwowiVp9UphreAn+FOndRWwUTGw8UH/PlomHmB+4uNqOZrE
# 6u4/5rITP1UDBE0LkHLU6/u8h5BRsjgZMIIE/jCCA+agAwIBAgIQK3PbdGMRTFpb
# MkryMFdySTANBgkqhkiG9w0BAQUFADB6MQswCQYDVQQGEwJHQjEbMBkGA1UECBMS
# R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD
# T01PRE8gQ0EgTGltaXRlZDEgMB4GA1UEAxMXQ09NT0RPIFRpbWUgU3RhbXBpbmcg
# Q0EwHhcNMTkwNTAyMDAwMDAwWhcNMjAwNTMwMTA0ODM4WjCBgzELMAkGA1UEBhMC
# R0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9y
# ZDEYMBYGA1UECgwPU2VjdGlnbyBMaW1pdGVkMSswKQYDVQQDDCJTZWN0aWdvIFNI
# QS0xIFRpbWUgU3RhbXBpbmcgU2lnbmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
# MIIBCgKCAQEAv1I2gjrcdDcNeNV/FlAZZu26GpnRYziaDGayQNungFC/aS42Lwpn
# P0ChSopjNZvQGcx0qhcZkSu1VSAZ+8AaOm3KOZuC8rqVoRrYNMe4iXtwiHBRZmns
# d/7GlHJ6zyWB7TSCmt8IFTcxtG2uHL8Y1Q3P/rXhxPuxR3Hp+u5jkezx7M5ZBBF8
# rgtgU+oq874vAg/QTF0xEy8eaQ+Fm0WWwo0Si2euH69pqwaWgQDfkXyVHOaeGWTf
# dshgRC9J449/YGpFORNEIaW6+5H6QUDtTQK0S3/f4uA9uKrzGthBg49/M+1BBuJ9
# nj9ThI0o2t12xr33jh44zcDLYCQD3npMqwIDAQABo4IBdDCCAXAwHwYDVR0jBBgw
# FoAUZCKGtkqJyQQP0ARYkiuzbj0eJ2wwHQYDVR0OBBYEFK7u2WC6XvUsARL9jo2y
# VXI1Rm/xMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQM
# MAoGCCsGAQUFBwMIMEAGA1UdIAQ5MDcwNQYMKwYBBAGyMQECAQMIMCUwIwYIKwYB
# BQUHAgEWF2h0dHBzOi8vc2VjdGlnby5jb20vQ1BTMEIGA1UdHwQ7MDkwN6A1oDOG
# MWh0dHA6Ly9jcmwuc2VjdGlnby5jb20vQ09NT0RPVGltZVN0YW1waW5nQ0FfMi5j
# cmwwcgYIKwYBBQUHAQEEZjBkMD0GCCsGAQUFBzAChjFodHRwOi8vY3J0LnNlY3Rp
# Z28uY29tL0NPTU9ET1RpbWVTdGFtcGluZ0NBXzIuY3J0MCMGCCsGAQUFBzABhhdo
# dHRwOi8vb2NzcC5zZWN0aWdvLmNvbTANBgkqhkiG9w0BAQUFAAOCAQEAen+pStKw
# pBwdDZ0tXMauWt2PRR3wnlyQ9l6scP7T2c3kGaQKQ3VgaoOkw5mEIDG61v5MzxP4
# EPdUCX7q3NIuedcHTFS3tcmdsvDyHiQU0JzHyGeqC2K3tPEG5OfkIUsZMpk0uRlh
# dwozkGdswIhKkvWhQwHzrqJvyZW9ljj3g/etfCgf8zjfjiHIcWhTLcuuquIwF4Mi
# KRi14YyJ6274fji7kE+5Xwc0EmuX1eY7kb4AFyFu4m38UnnvgSW6zxPQ+90rzYG2
# V4lO8N3zC0o0yoX/CLmWX+sRE+DhxQOtVxzhXZIGvhvIPD+lIJ9p0GnBxcLJPufF
# cvfqG5bilK+GLjCCBawwggSUoAMCAQICEHJNXiAT1cKRQFXzfFSJVHEwDQYJKoZI
# hvcNAQELBQAwfDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAk1JMRIwEAYDVQQHEwlB
# bm4gQXJib3IxEjAQBgNVBAoTCUludGVybmV0MjERMA8GA1UECxMISW5Db21tb24x
# JTAjBgNVBAMTHEluQ29tbW9uIFJTQSBDb2RlIFNpZ25pbmcgQ0EwHhcNMTcxMjE0
# MDAwMDAwWhcNMjAxMjEzMjM1OTU5WjCByzELMAkGA1UEBhMCVVMxDjAMBgNVBBEM
# BTU1NDU1MRIwEAYDVQQIDAlNaW5uZXNvdGExFDASBgNVBAcMC01pbm5lYXBvbGlz
# MRgwFgYDVQQJDA8xMDAgVW5pb24gU3QgU0UxIDAeBgNVBAoMF1VuaXZlcnNpdHkg
# b2YgTWlubmVzb3RhMSQwIgYDVQQLDBtDb21wdXRlciBhbmQgRGV2aWNlIFN1cHBv
# cnQxIDAeBgNVBAMMF1VuaXZlcnNpdHkgb2YgTWlubmVzb3RhMIIBIjANBgkqhkiG
# 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwk6kLE9u+tWv0JUkIJSn5pWfa09g6cqFLucC
# XomNj9NYj8t+JfPna3gC6LHv3OQAUDHOoC5H+8N3ea7qVGYIiwPRHzXOGqG/tVai
# U5s5hG3vBhfRX8W1/2g4/hpgeXUzrxYn/2c5SOGGy0MU1ZJyUSFEdsjXHEV7HXK4
# qmFGV9RJxtiLZH1qUldCglxcj7zw0QnUdG6oAxpwTCeVp057/WXbnIR8a0gPse+y
# /new5+CBUGTAvrw6K2BrJQVsdIIVn/q+BbcZxh9PpeZfTtsi6lgkvy0bUWtl5sSp
# d75+hvw4Sl3HAaWZtoWN7LPmbDbbVRO2Arv4doh4Chod4wJ5xQIDAQABo4IB2DCC
# AdQwHwYDVR0jBBgwFoAUrjUjF///Bj2cUOCMJGUzHnAQiKIwHQYDVR0OBBYEFF4L
# EhElVUvT8n5txOJSNAczooSAMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAA
# MBMGA1UdJQQMMAoGCCsGAQUFBwMDMBEGCWCGSAGG+EIBAQQEAwIEEDBmBgNVHSAE
# XzBdMFsGDCsGAQQBriMBBAMCATBLMEkGCCsGAQUFBwIBFj1odHRwczovL3d3dy5p
# bmNvbW1vbi5vcmcvY2VydC9yZXBvc2l0b3J5L2Nwc19jb2RlX3NpZ25pbmcucGRm
# MEkGA1UdHwRCMEAwPqA8oDqGOGh0dHA6Ly9jcmwuaW5jb21tb24tcnNhLm9yZy9J
# bkNvbW1vblJTQUNvZGVTaWduaW5nQ0EuY3JsMH4GCCsGAQUFBwEBBHIwcDBEBggr
# BgEFBQcwAoY4aHR0cDovL2NydC5pbmNvbW1vbi1yc2Eub3JnL0luQ29tbW9uUlNB
# Q29kZVNpZ25pbmdDQS5jcnQwKAYIKwYBBQUHMAGGHGh0dHA6Ly9vY3NwLmluY29t
# bW9uLXJzYS5vcmcwGQYDVR0RBBIwEIEOb2l0bXB0QHVtbi5lZHUwDQYJKoZIhvcN
# AQELBQADggEBAENRlesMKmBaZ0g68lttYEMtaPiz+DaNpOlXBs1gH66aghB1aP6i
# iRJcFVasGLUVFncdG1xbw503LTrBUc5PECMVDVF7KKCfHA1OeFV9vOWyvdVgbe3p
# aDy1sj4CADO2D0gnxcGiZoFhEZiBkTvSsj4S3GXZEvoFHJxJLw2kvdLnzy0gH/b/
# b/yblwA1fKXw4locUpDM6qTwM7SiKgkQ5W7/280EYu8BI6c8rpiJmqM1tZLcpswu
# avB00T52Y+ZZmz3tMMVgFHn9pFFltYr3s3bEek7I6pU8unISbiyQzxqhIUKaBi8h
# y8LgoY5UnGjX5jHsIvINzms+JX5Ity02sL0wggXrMIID06ADAgECAhBl4eLj1d5Q
# RYXzJiSABeLUMA0GCSqGSIb3DQEBDQUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UE
# CBMKTmV3IEplcnNleTEUMBIGA1UEBxMLSmVyc2V5IENpdHkxHjAcBgNVBAoTFVRo
# ZSBVU0VSVFJVU1QgTmV0d29yazEuMCwGA1UEAxMlVVNFUlRydXN0IFJTQSBDZXJ0
# aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNDA5MTkwMDAwMDBaFw0yNDA5MTgyMzU5
# NTlaMHwxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJNSTESMBAGA1UEBxMJQW5uIEFy
# Ym9yMRIwEAYDVQQKEwlJbnRlcm5ldDIxETAPBgNVBAsTCEluQ29tbW9uMSUwIwYD
# VQQDExxJbkNvbW1vbiBSU0EgQ29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0B
# AQEFAAOCAQ8AMIIBCgKCAQEAwKAvix56u2p1rPg+3KO6OSLK86N25L99MCfmutOY
# MlYjXAaGlw2A6O2igTXrC/Zefqk+aHP9ndRnec6q6mi3GdscdjpZh11emcehsrip
# hHMMzKuHRhxqx+85Jb6n3dosNXA2HSIuIDvd4xwOPzSf5X3+VYBbBnyCV4RV8zj7
# 8gw2qblessWBRyN9EoGgwAEoPgP5OJejrQLyAmj91QGr9dVRTVDTFyJG5XMY4Drk
# N3dRyJ59UopPgNwmucBMyvxR+hAJEXpXKnPE4CEqbMJUvRw+g/hbqSzx+tt4z9mJ
# mm2j/w2nP35MViPWCb7hpR2LB8W/499Yqu+kr4LLBfgKCQIDAQABo4IBWjCCAVYw
# HwYDVR0jBBgwFoAUU3m/WqorSs9UgOHYm8Cd8rIDZsswHQYDVR0OBBYEFK41Ixf/
# /wY9nFDgjCRlMx5wEIiiMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/
# AgEAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMBEGA1UdIAQKMAgwBgYEVR0gADBQBgNV
# HR8ESTBHMEWgQ6BBhj9odHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVNFUlRydXN0
# UlNBQ2VydGlmaWNhdGlvbkF1dGhvcml0eS5jcmwwdgYIKwYBBQUHAQEEajBoMD8G
# CCsGAQUFBzAChjNodHRwOi8vY3J0LnVzZXJ0cnVzdC5jb20vVVNFUlRydXN0UlNB
# QWRkVHJ1c3RDQS5jcnQwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnVzZXJ0cnVz
# dC5jb20wDQYJKoZIhvcNAQENBQADggIBAEYstn9qTiVmvZxqpqrQnr0Prk41/PA4
# J8HHnQTJgjTbhuET98GWjTBEE9I17Xn3V1yTphJXbat5l8EmZN/JXMvDNqJtkyOh
# 26owAmvquMCF1pKiQWyuDDllxR9MECp6xF4wnH1Mcs4WeLOrQPy+C5kWE5gg/7K6
# c9G1VNwLkl/po9ORPljxKKeFhPg9+Ti3JzHIxW7LdyljffccWiuNFR51/BJHAZIq
# UDw3LsrdYWzgg4x06tgMvOEf0nITelpFTxqVvMtJhnOfZbpdXZQ5o1TspxfTEVOQ
# Asp05HUNCXyhznlVLr0JaNkM7edgk59zmdTbSGdMq8Ztuu6VyrivOlMSPWmay5Mj
# vwTzuNorbwBv0DL+7cyZBp7NYZou+DoGd1lFZN0jU5IsQKgm3+00pnnJ67crdFwf
# z/8bq3MhTiKOWEb04FT3OZVp+jzvaChHWLQ8gbCORgClaZq1H3aqI7JeRkWEEEp6
# Tv4WAVsr/i7LoXU72gOb8CAzPFqwI4Excdrxp0I4OXbECHlDqU4sTInqwlMwofmx
# eO4u94196qIqJQl+8Sykl06VktqMux84Iw3ZQLH08J8LaJ+WDUycc4OjY61I7FGx
# CDkbSQf3npXeRFm0IBn8GiW+TRDk6J2XJFLWEtVZmhboFlBLoUlqHUCKu0QOhU/+
# AEOqnY98j2zRMYIEXjCCBFoCAQEwgZAwfDELMAkGA1UEBhMCVVMxCzAJBgNVBAgT
# Ak1JMRIwEAYDVQQHEwlBbm4gQXJib3IxEjAQBgNVBAoTCUludGVybmV0MjERMA8G
# A1UECxMISW5Db21tb24xJTAjBgNVBAMTHEluQ29tbW9uIFJTQSBDb2RlIFNpZ25p
# bmcgQ0ECEHJNXiAT1cKRQFXzfFSJVHEwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcC
# AQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYB
# BAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFP2hhQZlJkAk
# TOI0BAbaPADJw5OlMA0GCSqGSIb3DQEBAQUABIIBAERc4TaI4/t2++Z18BRGmlcj
# CcYFd+YlwBJvjouDrdhVFF4T1w40qFh3ibz8yWKkkV++8jCTPJLQGDa6mePOwuJn
# 5rUutWXICXAyeXb3yP8ZPMnwAqZB7wD6tyRkd+9fXZBYc4Oxib8mRc4nQPJggMmI
# eap8jX13cP6c0p+iQlwvkwPZTmNbYol2fm/WQcB23aoTFo+q2CF0tde8A+hsiuX0
# nCoHMfA0rrEKkBFleH8LGRDCvK6Q8iL53Kuy5ZNzah2ZVZxnLd2wx7oycgwftPN1
# t1OLb6I9fzjOIYVAK8vp09d78sVHhG+itXaJzRocYz0gE7Pc3UbqzPuC4vJOzzOh
# ggIoMIICJAYJKoZIhvcNAQkGMYICFTCCAhECAQEwgY4wejELMAkGA1UEBhMCR0Ix
# GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEa
# MBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxIDAeBgNVBAMTF0NPTU9ETyBUaW1l
# IFN0YW1waW5nIENBAhArc9t0YxFMWlsySvIwV3JJMAkGBSsOAwIaBQCgXTAYBgkq
# hkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xOTA2MTIxNjA5
# MDRaMCMGCSqGSIb3DQEJBDEWBBSVIJl6jrbCmKMLtml2AbpBEHBiOjANBgkqhkiG
# 9w0BAQEFAASCAQBEuwH0wVbBAsc7liXKGim3hgm0EPt8wJ+jb6MZN8+SqbdGVCZA
# l4e3mwEoF8v4DbuJoRwEL+mDHVofSNY6DeSYrw0WYyAU8SH0GOn+TafbPttqCqzO
# eCCSn5TfkCwEHkKl73IBrOv45DFVeAwpz0foStUk9CbgiULvc+h7FG1mb8Af6gYL
# JdUlrnPxZGFtcpeWFwvj3t3LC5xA4UleGpWO161hE6FraG5efzhie9ApB1WK/N1d
# 9IX3hCdEgNaXwMWMcwPGvpT+HYib6uaCmclb3ipqqiKFAubP2KFGtWHoj0iSO5No
# xwmACW86SuJOawh8w3xlae5f9GA3kqA8nUcB
# SIG # End signature block