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 |