EnterpriseUser.ps1
|
# Enterprise User Management Functions function Add-KeeperEnterpriseUser { <# .SYNOPSIS Invites Enterprise Users .PARAMETER Node Node Name or ID .PARAMETER Email Email address to invite .PARAMETER Emails Extra email addresses to invite #> [CmdletBinding()] Param ( [Parameter()][string] $FullName, [Parameter()][string] $Node, [Parameter(Position = 0, Mandatory = $true)] $Email, [Parameter(ValueFromRemainingArguments = $true)] $Emails ) [Enterprise]$enterprise = getEnterprise [Int64] $nodeId = 0 if ($Node) { $n = resolveSingleNode $Node if ($n) { $nodeId = $n.Id } } else { $nodeId = $enterprise.enterpriseData.RootNode.Id } $inviteOptions = New-Object KeeperSecurity.Enterprise.InviteUserOptions if ($nodeId -gt 0) { $inviteOptions.NodeId = $nodeId } if ($FullName) { $inviteOptions.FullName = $FullName } $user = $enterprise.enterpriseData.InviteUser($Email, $inviteOptions).GetAwaiter().GetResult() if ($user) { Write-Output "User `"$Email`" is invited" } $inviteOptions.FullName = $null foreach ($e in $Emails) { $user = $enterprise.enterpriseData.InviteUser($e, $inviteOptions).GetAwaiter().GetResult() if ($user) { Write-Output "User `"$e`" is invited" } } } New-Alias -Name invite-user -Value Add-KeeperEnterpriseUser function Lock-KeeperEnterpriseUser { <# .Synopsis Locks Enterprise User .Parameter User User email, enterprise Id, or instance. #> [CmdletBinding()] Param ( [Parameter(Position = 0, Mandatory = $true)]$User ) [Enterprise]$enterprise = getEnterprise $userObject = resolveUser $enterprise.enterpriseData $User $saved = $enterprise.enterpriseData.SetUserLocked($userObject, $true).GetAwaiter().GetResult() if ($saved) { Write-Output "User `"$($saved.Email)`" was locked" } } Register-ArgumentCompleter -CommandName Lock-KeeperEnterpriseUser -ParameterName User -ScriptBlock $Keeper_ActiveUserCompleter New-Alias -Name lock-user -Value Lock-KeeperEnterpriseUser function Unlock-KeeperEnterpriseUser { <# .Synopsis Unlocks Enterprise User .Parameter User User email, enterprise Id, or instance. #> [CmdletBinding()] Param ( [Parameter(Position = 0, Mandatory = $true)]$User ) [Enterprise]$enterprise = getEnterprise $userObject = resolveUser $enterprise.enterpriseData $User $saved = $enterprise.enterpriseData.SetUserLocked($userObject, $false).GetAwaiter().GetResult() if ($saved) { Write-Output "User `"$($saved.Email)`" was unlocked" } } Register-ArgumentCompleter -CommandName Unlock-KeeperEnterpriseUser -ParameterName User -ScriptBlock $Keeper_LockedUserCompleter New-Alias -Name unlock-user -Value Unlock-KeeperEnterpriseUser function Move-KeeperEnterpriseUser { <# .Synopsis Transfers an enterprise user's vault (records, shared folders, teams) to another user .Parameter FromUser Email or enterprise user ID of the source user whose vault will be transferred .Parameter TargetUser Email or enterprise user ID of the destination user who will receive the vault .Parameter Force Skip confirmation prompt in non-interactive sessions .Description Transfers all records, shared folders, and team memberships from one enterprise user to another. The source user's vault contents are merged into the target user's vault. Use -Force to skip confirmation in automated scripts. .Example Move-KeeperEnterpriseUser -FromUser "departing@company.com" -TargetUser "replacement@company.com" Transfers the departing user's vault to the replacement user (prompts for confirmation) .Example transfer-user "departing@company.com" "replacement@company.com" -Force Transfers vault without confirmation prompt #> [CmdletBinding()] Param ( [Parameter(Position = 0, Mandatory = $true)]$FromUser, [Parameter(Position = 1, Mandatory = $true)]$TargetUser, [Switch] $Force ) [Enterprise]$enterprise = getEnterprise $fromUserObject = resolveUser $enterprise.enterpriseData $FromUser if (-not $fromUserObject) { return } $targetUserObject = resolveUser $enterprise.enterpriseData $TargetUser if (-not $targetUserObject) { return } if (-not $Force.IsPresent) { if (Test-InteractiveSession) { Write-Output "This action cannot be undone.`n" $answer = Read-Host -Prompt "Do you want to proceed with transferring $($fromUserObject.Email) account (Yes/No)? > " } else { Write-Output('Non-interactive session. Use -Force parameter') $answer = 'no' } if ($answer -ne 'yes' -and $answer -ne 'y') { return } } $transferResult = $enterprise.enterpriseData.TransferUserAccount($enterprise.roleData, $fromUserObject, $targetUserObject).GetAwaiter().GetResult() if ($transferResult) { Write-Information "Successfully Transferred:" Write-Information " Records: $($transferResult.RecordsTransfered)" Write-Information " Shared Folders: $($transferResult.SharedFoldersTransfered)" Write-Information " Team: $($transferResult.TeamsTransfered)" if ($transferResult.RecordsCorrupted -gt 0 -or $transferResult.SharedFoldersCorrupted -gt 0 -or $transferResult.TeamsCorrupted -gt 0) { Write-Information "Failed to Transfer:" if ($transferResult.RecordsCorrupted -gt 0) { Write-Information " Records: $($transferResult.RecordsCorrupted)" } if ($transferResult.SharedFoldersCorrupted -gt 0) { Write-Information " Shared Folders: $($transferResult.SharedFoldersCorrupted)" } if ($transferResult.TeamsCorrupted -gt 0) { Write-Information " Team: $($transferResult.TeamsCorrupted)" } } } } Register-ArgumentCompleter -CommandName Move-KeeperEnterpriseUser -ParameterName FromUser -ScriptBlock $Keeper_LockedUserCompleter Register-ArgumentCompleter -CommandName Move-KeeperEnterpriseUser -ParameterName TargetUser -ScriptBlock $Keeper_ActiveUserCompleter New-Alias -Name transfer-user -Value Move-KeeperEnterpriseUser function Remove-KeeperEnterpriseUser { <# .Synopsis Removes Enterprise User .Parameter User User email, enterprise Id, or instance. #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] Param ( [Parameter(Position = 0, Mandatory = $true)]$User, [Switch] $Force ) [Enterprise]$enterprise = getEnterprise $userObject = resolveUser $enterprise.enterpriseData $User if (-not $Force.IsPresent) { Write-Output "`nDeleting a user will also delete any records owned and shared by this user." "Before you delete this user, we strongly recommend you lock their account" "and transfer any important records to other user.`n" "This action cannot be undone." if ($PSCmdlet.ShouldProcess($userObject.Email, "Removing Enterprise User")) { $enterprise.enterpriseData.DeleteUser($userObject).GetAwaiter().GetResult() | Out-Null Write-Output "User $($userObject.Email) has been deleted" } } } Register-ArgumentCompleter -CommandName Remove-KeeperEnterpriseUser -ParameterName User -ScriptBlock $Keeper_EnterpriseUserCompleter New-Alias -Name delete-user -Value Remove-KeeperEnterpriseUser function Invoke-ResendKeeperEnterpriseInvite { <# .Synopsis Resends enterprise invitation email to a user .Parameter User User email address .Description Resends the enterprise invitation email to a user who has not yet accepted their invitation. The user must be in Inactive status (not yet accepted invitation). .Example Invoke-ResendKeeperEnterpriseInvite -User "user@example.com" Resends invitation email to user@example.com #> [CmdletBinding()] Param ( [Parameter(Position = 0, Mandatory = $true)][string] $User ) [Enterprise]$enterprise = getEnterprise [KeeperSecurity.Enterprise.EnterpriseUser]$userObject = $null if ($enterprise.enterpriseData.TryGetUserByEmail($User, [ref]$userObject)) { # here user is found by email Write-Debug "User `"$User`" found by email" } elseif ($User -match '^\d+$') { $userId = [long]$User if (-not $enterprise.enterpriseData.TryGetUserById($userId, [ref]$userObject)) { Write-Error "User `"$User`" not found" -ErrorAction Stop return } # here user is found by ID Write-Debug "User `"$User`" found by ID" } else { # here user is not found by email or ID Write-Error "User `"$User`" not found" -ErrorAction Stop return } if ($userObject.UserStatus -ne [KeeperSecurity.Enterprise.UserStatus]::Inactive) { Write-Error "User has already accepted invitation. Only inactive users can have invitations resent." -ErrorAction Stop return } try { $enterprise.enterpriseData.ResendEnterpriseInvite($userObject).GetAwaiter().GetResult() | Out-Null Write-Output "Invite for $User resent." } catch { Write-Error "Failed to resend invite: $($_.Exception.Message)" -ErrorAction Stop } } Register-ArgumentCompleter -CommandName Invoke-ResendKeeperEnterpriseInvite -ParameterName User -ScriptBlock $Keeper_EnterpriseUserCompleter function Set-KeeperEnterpriseUserMasterPasswordExpire { <# .Synopsis Sets master password expiration for an enterprise user .Parameter User User email address .Description Sets the master password expiration for an active enterprise user, requiring them to change their password on next login. .Example Set-KeeperEnterpriseUserMasterPasswordExpire -User "user@example.com" Sets master password expiration for user@example.com #> [CmdletBinding()] Param ( [Parameter(Position = 0, Mandatory = $true)][string] $User ) [Enterprise]$enterprise = getEnterprise $userObject = resolveUser $enterprise.enterpriseData $User if (-not $userObject) { Write-Error "User `"$User`" not found" -ErrorAction Stop return } if ($userObject.UserStatus -ne [KeeperSecurity.Enterprise.UserStatus]::Active) { Write-Error "User $User is not active" -ErrorAction Stop return } try { $enterprise.enterpriseData.SetMasterPasswordExpire($userObject.Email).GetAwaiter().GetResult() | Out-Null Write-Output "Master password expiration set for $User" } catch { Write-Error "Failed to set master password expiration: $($_.Exception.Message)" -ErrorAction Stop } } Register-ArgumentCompleter -CommandName Set-KeeperEnterpriseUserMasterPasswordExpire -ParameterName User -ScriptBlock $Keeper_ActiveUserCompleter class EnterpriseUserAliasState { [string]$NormalizedAlias [string]$ExistingSecondaryAlias [bool]$IsAlreadyPrimary [System.Collections.Generic.List[string]]$Aliases EnterpriseUserAliasState([string]$normalizedAlias) { $this.NormalizedAlias = $normalizedAlias $this.Aliases = [System.Collections.Generic.List[string]]::new() $this.IsAlreadyPrimary = $false } } function Get-MatchingEnterpriseUserAlias { param( [Parameter(Mandatory)][System.Collections.Generic.List[string]]$Aliases, [Parameter(Mandatory)][string]$Alias ) $normalizedAlias = $Alias.Trim().ToLowerInvariant() foreach ($existingAlias in $Aliases) { if ($existingAlias.ToLowerInvariant() -eq $normalizedAlias) { return $existingAlias } } return $null } function Get-EnterpriseUserAliasState { param( [Parameter(Mandatory)][Enterprise]$Enterprise, [Parameter(Mandatory)][KeeperSecurity.Enterprise.EnterpriseUser]$User, [Parameter(Mandatory)][string]$AliasEmail ) $state = [EnterpriseUserAliasState]::new($AliasEmail.Trim().ToLowerInvariant()) foreach ($alias in $Enterprise.userAliasData.GetAliasesForUser($User.Id)) { [void]$state.Aliases.Add($alias) } $state.ExistingSecondaryAlias = Get-MatchingEnterpriseUserAlias -Aliases $state.Aliases -Alias $state.NormalizedAlias $state.IsAlreadyPrimary = ($User.Email.ToLowerInvariant() -eq $state.NormalizedAlias) return $state } function Update-EnterpriseUserAliasCache { param( [Parameter(Mandatory)][Enterprise]$Enterprise ) $Enterprise.loader.Load().GetAwaiter().GetResult() | Out-Null } function Set-KeeperEnterpriseUserPrimaryAlias { param( [Parameter(Mandatory)][Enterprise]$Enterprise, [Parameter(Mandatory)][long]$EnterpriseUserId, [Parameter(Mandatory)][string]$Alias ) $rq = New-Object Authentication.EnterpriseUserAliasRequest $rq.EnterpriseUserId = $EnterpriseUserId $rq.Alias = $Alias $Enterprise.loader.Auth.ExecuteAuthRest("enterprise/enterprise_user_set_primary_alias", $rq).GetAwaiter().GetResult() | Out-Null } function Test-KeeperEnterpriseUserAliasConflict { param([string]$Status) return ($Status -and $Status.ToLowerInvariant().Contains('conflict')) } function Add-KeeperEnterpriseUserAlias { <# .SYNOPSIS Adds an alias email address to an enterprise user .DESCRIPTION Adds an alternate email alias for an enterprise user. If the alias already exists for the user, it is set as the primary alias. .PARAMETER User User email address, alias email address, or enterprise user ID .PARAMETER Alias Alias email address to add .EXAMPLE Add-KeeperEnterpriseUserAlias -User "user@example.com" -Alias "alias@example.com" Adds alias@example.com as an alias for user@example.com .EXAMPLE kuser-alias-add "user@example.com" "alias@example.com" Adds an alias using the kuser-alias-add alias #> [CmdletBinding()] Param ( [Parameter(Position = 0, Mandatory = $true)][string] $User, [Parameter(Position = 1, Mandatory = $true)][string] $Alias ) if ([string]::IsNullOrWhiteSpace($Alias)) { Write-Error "Alias parameter is mandatory." -ErrorAction Stop return } $aliasEmail = $Alias.Trim().ToLowerInvariant() [Enterprise]$enterprise = getEnterprise try { Update-EnterpriseUserAliasCache -Enterprise $enterprise } catch { Write-Warning "Enterprise data reload failed before alias lookup: $($_.Exception.Message)" } $userObject = resolveUser $enterprise.enterpriseData $User $enterprise.userAliasData [EnterpriseUserAliasState]$aliasState = Get-EnterpriseUserAliasState -Enterprise $enterprise -User $userObject -AliasEmail $aliasEmail if ($aliasState.IsAlreadyPrimary) { Write-Output "Alias `"$($userObject.Email)`" already exists for this user." return } try { if ($aliasState.ExistingSecondaryAlias) { Set-KeeperEnterpriseUserPrimaryAlias -Enterprise $enterprise -EnterpriseUserId $userObject.Id -Alias $aliasState.ExistingSecondaryAlias Write-Output "Alias `"$($aliasState.ExistingSecondaryAlias)`" set as primary for user `"$($userObject.Email)`"." } else { $addRq = New-Object Authentication.EnterpriseUserAddAliasRequest $addRq.Primary = $true $addRq.EnterpriseUserId = $userObject.Id $addRq.Alias = $aliasState.NormalizedAlias $rq = New-Object Authentication.EnterpriseUserAddAliasRequestV2 [void]$rq.EnterpriseUserAddAliasRequest.Add($addRq) $rs = $enterprise.loader.Auth.ExecuteAuthRest( "enterprise/enterprise_user_add_alias", $rq, [Authentication.EnterpriseUserAddAliasResponse], 1 ).GetAwaiter().GetResult() $statusList = @($rs.Status) if ($statusList.Count -eq 0) { Write-Error "Failed to add alias: no status returned from server." -ErrorAction Stop } $setPrimaryOnConflict = $false foreach ($st in $statusList) { if ($st.Status -ne 'success') { if (Test-KeeperEnterpriseUserAliasConflict -Status $st.Status) { $setPrimaryOnConflict = $true } else { Write-Error "Failed to add alias for user $($st.EnterpriseUserId): $($st.Status)" -ErrorAction Stop } } } if ($setPrimaryOnConflict) { $canonicalAlias = $aliasState.ExistingSecondaryAlias if (-not $canonicalAlias) { $canonicalAlias = Get-MatchingEnterpriseUserAlias -Aliases $aliasState.Aliases -Alias $aliasState.NormalizedAlias } if (-not $canonicalAlias) { $canonicalAlias = $aliasState.NormalizedAlias } Set-KeeperEnterpriseUserPrimaryAlias -Enterprise $enterprise -EnterpriseUserId $userObject.Id -Alias $canonicalAlias Write-Output "Alias `"$canonicalAlias`" set as primary for user `"$($userObject.Email)`"." } else { Write-Output "Alias `"$($aliasState.NormalizedAlias)`" added for user `"$($userObject.Email)`"." } } } catch { if (Test-KeeperEnterpriseUserAliasConflict -Status $_.Exception.Message) { Set-KeeperEnterpriseUserPrimaryAlias -Enterprise $enterprise -EnterpriseUserId $userObject.Id -Alias $aliasState.NormalizedAlias Write-Output "Alias `"$($aliasState.NormalizedAlias)`" set as primary for user `"$($userObject.Email)`"." } else { Write-Error "Failed to add alias: $($_.Exception.Message)" -ErrorAction Stop } } try { Update-EnterpriseUserAliasCache -Enterprise $enterprise } catch { Write-Warning "Alias operation succeeded but enterprise data reload failed: $($_.Exception.Message)" } } Register-ArgumentCompleter -CommandName Add-KeeperEnterpriseUserAlias -ParameterName User -ScriptBlock $Keeper_EnterpriseUserCompleter New-Alias -Name kuser-alias-add -Value Add-KeeperEnterpriseUserAlias function Remove-KeeperEnterpriseUserAlias { <# .SYNOPSIS Removes an alias email address from an enterprise user .DESCRIPTION Removes an alternate email alias from an enterprise user. .PARAMETER User User email address, alias email address, or enterprise user ID .PARAMETER Alias Alias email address to remove. Cannot be the user's current primary email. .PARAMETER Force Skip confirmation prompt and remove the alias .EXAMPLE Remove-KeeperEnterpriseUserAlias -User "user@example.com" -Alias "alias@example.com" Removes alias@example.com from user@example.com .EXAMPLE Remove-KeeperEnterpriseUserAlias -User "user@example.com" -Alias "alias@example.com" -Force Removes the alias without a confirmation prompt .EXAMPLE kuser-alias-remove "user@example.com" "alias@example.com" Removes an alias using the kuser-alias-remove alias #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] Param ( [Parameter(Position = 0, Mandatory = $true)][string] $User, [Parameter(Position = 1, Mandatory = $true)][string] $Alias, [Parameter()][switch] $Force ) if ([string]::IsNullOrWhiteSpace($Alias)) { Write-Error "Alias parameter is mandatory." -ErrorAction Stop return } $aliasEmail = $Alias.Trim().ToLowerInvariant() [Enterprise]$enterprise = getEnterprise $userObject = resolveUser $enterprise.enterpriseData $User $enterprise.userAliasData if ($userObject.Email.ToLowerInvariant() -eq $aliasEmail) { Write-Error "Cannot remove the user's primary email address `"$($userObject.Email)`". Set another alias as primary first." -ErrorAction Stop return } if ($Force -or $PSCmdlet.ShouldProcess("$($userObject.Email) -> $aliasEmail", "Remove Enterprise User Alias")) { [EnterpriseUserAliasState]$aliasState = Get-EnterpriseUserAliasState -Enterprise $enterprise -User $userObject -AliasEmail $aliasEmail $canonicalAlias = $aliasState.ExistingSecondaryAlias if (-not $canonicalAlias) { $canonicalAlias = $aliasState.NormalizedAlias } try { $rq = New-Object Authentication.EnterpriseUserAliasRequest $rq.EnterpriseUserId = $userObject.Id $rq.Alias = $canonicalAlias $enterprise.loader.Auth.ExecuteAuthRest("enterprise/enterprise_user_delete_alias", $rq).GetAwaiter().GetResult() | Out-Null Write-Output "Alias `"$canonicalAlias`" removed from user `"$($userObject.Email)`"." } catch { Write-Error "Failed to remove alias: $($_.Exception.Message)" -ErrorAction Stop } try { Update-EnterpriseUserAliasCache -Enterprise $enterprise } catch { Write-Warning "Alias removal succeeded but enterprise data reload failed: $($_.Exception.Message)" } } else { Write-Output "Alias removal cancelled." } } Register-ArgumentCompleter -CommandName Remove-KeeperEnterpriseUserAlias -ParameterName User -ScriptBlock $Keeper_EnterpriseUserCompleter New-Alias -Name kuser-alias-remove -Value Remove-KeeperEnterpriseUserAlias function Get-KeeperEnterpriseUserTeam { <# .SYNOPSIS Gets the list of enterprise teams for a user. .DESCRIPTION Returns teams the specified enterprise user belongs to. .PARAMETER User User email, enterprise user ID, or EnterpriseUser object. .EXAMPLE Get-KeeperEnterpriseUserTeam -User "user@example.com" .EXAMPLE Get-KeeperEnterpriseUserTeam -User 123456789 #> [CmdletBinding()] Param ( [Parameter(Position = 0, Mandatory = $true)]$User ) [Enterprise]$enterprise = getEnterprise $enterpriseData = $enterprise.enterpriseData $userObject = resolveUser $enterpriseData $User if (-not $userObject) { return @() } $teamUids = @($enterpriseData.GetTeamsForUser($userObject.Id) | Select-Object -Unique) if ($teamUids.Count -eq 0) { return @() } $teams = foreach ($teamUid in $teamUids) { $team = $null if ($enterpriseData.TryGetTeam($teamUid, [ref]$team)) { $team } } return @($teams | Sort-Object { $_.Name }) } Register-ArgumentCompleter -CommandName Get-KeeperEnterpriseUserTeam -ParameterName User -ScriptBlock $Keeper_EnterpriseUserCompleter New-Alias -Name keut -Value Get-KeeperEnterpriseUserTeam function Update-KeeperEnterpriseTeamUser { <# .Synopsis Updates a user's type in an enterprise team .Parameter Team Team name or UID .Parameter User User email address .Parameter UserType User type: 0, 1, or 2. 0 = User (normal member), 1 = Administrator (can manage team, sees shared folders), 2 = Administrator Only (can manage team but does not see shared folders) .Description Updates the user type for a user in a specific enterprise team. UserType must be 0, 1, or 2. .Example Update-KeeperEnterpriseTeamUser -Team "Engineering" -User "user@example.com" -UserType 1 Makes user@example.com an Administrator in the Engineering team .Example Update-KeeperEnterpriseTeamUser -Team "Engineering" -User "user@example.com" -UserType 0 Makes user@example.com a normal User in the Engineering team #> [CmdletBinding()] Param ( [Parameter(Position = 0, Mandatory = $true)][string] $Team, [Parameter(Position = 1, Mandatory = $true)][string] $User, [Parameter(Position = 2, Mandatory = $true)][int] $UserType ) [Enterprise]$enterprise = getEnterprise if ([string]::IsNullOrWhiteSpace($Team)) { Write-Error "Team name parameter is mandatory." -ErrorAction Stop return } if ([string]::IsNullOrWhiteSpace($User)) { Write-Error "User email parameter is mandatory." -ErrorAction Stop return } if ($UserType -lt 0 -or $UserType -gt 2) { Write-Error "User type must be 0, 1, or 2" -ErrorAction Stop return } $teamObject = resolveTeam $enterprise.enterpriseData $Team if (-not $teamObject) { Write-Error "Team `"$Team`" not found" -ErrorAction Stop return } $userObject = resolveUser $enterprise.enterpriseData $User if (-not $userObject) { Write-Error "User `"$User`" not found" -ErrorAction Stop return } if ($userObject.UserStatus -ne [KeeperSecurity.Enterprise.UserStatus]::Active) { Write-Error "User $User is not active" -ErrorAction Stop return } try { $enterprise.enterpriseData.TeamEnterpriseUserUpdate($teamObject, $userObject, $UserType).GetAwaiter().GetResult() | Out-Null Write-Output "Team user $User updated" } catch { Write-Error "Failed to update team user: $($_.Exception.Message)" -ErrorAction Stop } } Register-ArgumentCompleter -CommandName Update-KeeperEnterpriseTeamUser -ParameterName Team -ScriptBlock $Keeper_TeamNameCompleter Register-ArgumentCompleter -CommandName Update-KeeperEnterpriseTeamUser -ParameterName User -ScriptBlock $Keeper_ActiveUserCompleter function Update-KeeperEnterpriseUser { <# .Synopsis Updates enterprise user information .Parameter User User email address .Parameter Node Node name or ID (optional) .Parameter FullName User's full name (optional) .Parameter JobTitle User's job title (optional) .Parameter InviteeLocale User's locale for invitations (optional) .Description Updates enterprise user information including node assignment, full name, job title, and invitee locale. If node is not specified, the user's current parent node is used. .Example Update-KeeperEnterpriseUser -User "user@example.com" -FullName "John Doe" -JobTitle "Software Engineer" Updates user's full name and job title .Example Update-KeeperEnterpriseUser -User "user@example.com" -Node "Engineering" -InviteeLocale "en-US" Moves user to Engineering node and sets locale #> [CmdletBinding()] Param ( [Parameter(Position = 0, Mandatory = $true)][string] $User, [Parameter()][string] $Node, [Parameter()][string] $FullName, [Parameter()][string] $JobTitle, [Parameter()][string] $InviteeLocale ) [Enterprise]$enterprise = getEnterprise if ([string]::IsNullOrWhiteSpace($User)) { Write-Error "User email parameter is mandatory." -ErrorAction Stop return } $userObject = resolveUser $enterprise.enterpriseData $User if (-not $userObject) { Write-Error "User `"$User`" not found" -ErrorAction Stop return } if ($userObject.UserStatus -ne [KeeperSecurity.Enterprise.UserStatus]::Active) { Write-Error "User $User is not active" -ErrorAction Stop return } $nodeId = $null if ($Node) { $nodeObject = resolveSingleNode $Node if ($nodeObject) { $nodeId = $nodeObject.Id } else { Write-Warning "Node `"$Node`" not found so we are taking user's parent node" } } try { $updatedUser = $enterprise.enterpriseData.EnterpriseUserUpdate($userObject, $nodeId, $FullName, $JobTitle, $InviteeLocale).GetAwaiter().GetResult() Write-Output "User $User updated" return $updatedUser } catch { Write-Error "Failed to update user: $($_.Exception.Message)" -ErrorAction Stop } } Register-ArgumentCompleter -CommandName Update-KeeperEnterpriseUser -ParameterName User -ScriptBlock $Keeper_ActiveUserCompleter # SIG # Begin signature block # MIInvgYJKoZIhvcNAQcCoIInrzCCJ6sCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCArhlY4m9aXBuDQ # tYC4f0HuSve96QHEgEzWAxsvWw/2lqCCITswggWNMIIEdaADAgECAhAOmxiO+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 # twGpn1eqXijiuZQwggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0GCSqG # SIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx # GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRy # dXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTlaMGkx # CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4 # RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEzODQg # MjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C0Cit # eLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce2vnS # 1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0daE6ZM # swEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6TSXBC # Mo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoAFdE3 # /hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7OhD26j # q22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM1bL5 # OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z8ujo # 7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05huzU # tw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNYmtwm # KwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP/2NP # TLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0TAQH/ # BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYDVR0j # BBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1Ud # JQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0 # cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0 # cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8E # PDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz # dGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATANBgkq # hkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95RysQDK # r2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HLIvda # qpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5BtfQ/g+ # lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnhOE7a # brs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIhdXNS # y0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV9zeK # iwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/jwVYb # KyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYHKi8Q # xAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmCXBVm # zGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l/aCn # HwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZWeE4w # gga0MIIEnKADAgECAhANx6xXBf8hmS5AQyIMOkmGMA0GCSqGSIb3DQEBCwUAMGIx # CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 # dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBH # NDAeFw0yNTA1MDcwMDAwMDBaFw0zODAxMTQyMzU5NTlaMGkxCzAJBgNVBAYTAlVT # MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1 # c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYgMjAyNSBDQTEwggIi # MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC0eDHTCphBcr48RsAcrHXbo0Zo # dLRRF51NrY0NlLWZloMsVO1DahGPNRcybEKq+RuwOnPhof6pvF4uGjwjqNjfEvUi # 6wuim5bap+0lgloM2zX4kftn5B1IpYzTqpyFQ/4Bt0mAxAHeHYNnQxqXmRinvuNg # xVBdJkf77S2uPoCj7GH8BLuxBG5AvftBdsOECS1UkxBvMgEdgkFiDNYiOTx4OtiF # cMSkqTtF2hfQz3zQSku2Ws3IfDReb6e3mmdglTcaarps0wjUjsZvkgFkriK9tUKJ # m/s80FiocSk1VYLZlDwFt+cVFBURJg6zMUjZa/zbCclF83bRVFLeGkuAhHiGPMvS # GmhgaTzVyhYn4p0+8y9oHRaQT/aofEnS5xLrfxnGpTXiUOeSLsJygoLPp66bkDX1 # ZlAeSpQl92QOMeRxykvq6gbylsXQskBBBnGy3tW/AMOMCZIVNSaz7BX8VtYGqLt9 # MmeOreGPRdtBx3yGOP+rx3rKWDEJlIqLXvJWnY0v5ydPpOjL6s36czwzsucuoKs7 # Yk/ehb//Wx+5kMqIMRvUBDx6z1ev+7psNOdgJMoiwOrUG2ZdSoQbU2rMkpLiQ6bG # RinZbI4OLu9BMIFm1UUl9VnePs6BaaeEWvjJSjNm2qA+sdFUeEY0qVjPKOWug/G6 # X5uAiynM7Bu2ayBjUwIDAQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB/wIBADAd # BgNVHQ4EFgQU729TSunkBnx6yuKQVvYv1Ensy04wHwYDVR0jBBgwFoAU7NfjgtJx # XWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUF # BwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGln # aWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5j # b20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJo # dHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNy # bDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQEL # BQADggIBABfO+xaAHP4HPRF2cTC9vgvItTSmf83Qh8WIGjB/T8ObXAZz8OjuhUxj # aaFdleMM0lBryPTQM2qEJPe36zwbSI/mS83afsl3YTj+IQhQE7jU/kXjjytJgnn0 # hvrV6hqWGd3rLAUt6vJy9lMDPjTLxLgXf9r5nWMQwr8Myb9rEVKChHyfpzee5kH0 # F8HABBgr0UdqirZ7bowe9Vj2AIMD8liyrukZ2iA/wdG2th9y1IsA0QF8dTXqvcnT # mpfeQh35k5zOCPmSNq1UH410ANVko43+Cdmu4y81hjajV/gxdEkMx1NKU4uHQcKf # ZxAvBAKqMVuqte69M9J6A47OvgRaPs+2ykgcGV00TYr2Lr3ty9qIijanrUR3anzE # wlvzZiiyfTPjLbnFRsjsYg39OlV8cipDoq7+qNNjqFzeGxcytL5TTLL4ZaoBdqbh # OhZ3ZRDUphPvSRmMThi0vw9vODRzW6AxnJll38F0cuJG7uEBYTptMSbhdhGQDpOX # gpIUsWTjd6xpR6oaQf/DJbg3s6KCLPAlZ66RzIg9sC+NJpud/v4+7RWsWCiKi9EO # LLHfMR2ZyJ/+xhCx9yHbxtl5TPau1j/1MIDpMPx0LckTetiSuEtQvLsNz3Qbp7wG # WqbIiOWCnb5WqxL3/BAPvIXKUjPSxyZsq8WhbaM2tszWkPZPubdcMIIG7TCCBNWg # AwIBAgIQCoDvGEuN8QWC0cR2p5V0aDANBgkqhkiG9w0BAQsFADBpMQswCQYDVQQG # EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0 # IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYgU0hBMjU2IDIwMjUgQ0Ex # MB4XDTI1MDYwNDAwMDAwMFoXDTM2MDkwMzIzNTk1OVowYzELMAkGA1UEBhMCVVMx # FzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBTSEEy # NTYgUlNBNDA5NiBUaW1lc3RhbXAgUmVzcG9uZGVyIDIwMjUgMTCCAiIwDQYJKoZI # hvcNAQEBBQADggIPADCCAgoCggIBANBGrC0Sxp7Q6q5gVrMrV7pvUf+GcAoB38o3 # zBlCMGMyqJnfFNZx+wvA69HFTBdwbHwBSOeLpvPnZ8ZN+vo8dE2/pPvOx/Vj8Tch # TySA2R4QKpVD7dvNZh6wW2R6kSu9RJt/4QhguSssp3qome7MrxVyfQO9sMx6ZAWj # FDYOzDi8SOhPUWlLnh00Cll8pjrUcCV3K3E0zz09ldQ//nBZZREr4h/GI6Dxb2Uo # yrN0ijtUDVHRXdmncOOMA3CoB/iUSROUINDT98oksouTMYFOnHoRh6+86Ltc5zjP # KHW5KqCvpSduSwhwUmotuQhcg9tw2YD3w6ySSSu+3qU8DD+nigNJFmt6LAHvH3KS # uNLoZLc1Hf2JNMVL4Q1OpbybpMe46YceNA0LfNsnqcnpJeItK/DhKbPxTTuGoX7w # JNdoRORVbPR1VVnDuSeHVZlc4seAO+6d2sC26/PQPdP51ho1zBp+xUIZkpSFA8vW # doUoHLWnqWU3dCCyFG1roSrgHjSHlq8xymLnjCbSLZ49kPmk8iyyizNDIXj//cOg # rY7rlRyTlaCCfw7aSUROwnu7zER6EaJ+AliL7ojTdS5PWPsWeupWs7NpChUk555K # 096V1hE0yZIXe+giAwW00aHzrDchIc2bQhpp0IoKRR7YufAkprxMiXAJQ1XCmnCf # gPf8+3mnAgMBAAGjggGVMIIBkTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTkO/zy # Me39/dfzkXFjGVBDz2GM6DAfBgNVHSMEGDAWgBTvb1NK6eQGfHrK4pBW9i/USezL # TjAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwgZUGCCsG # AQUFBwEBBIGIMIGFMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j # b20wXQYIKwYBBQUHMAKGUWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdp # Q2VydFRydXN0ZWRHNFRpbWVTdGFtcGluZ1JTQTQwOTZTSEEyNTYyMDI1Q0ExLmNy # dDBfBgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGln # aUNlcnRUcnVzdGVkRzRUaW1lU3RhbXBpbmdSU0E0MDk2U0hBMjU2MjAyNUNBMS5j # cmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEB # CwUAA4ICAQBlKq3xHCcEua5gQezRCESeY0ByIfjk9iJP2zWLpQq1b4URGnwWBdEZ # D9gBq9fNaNmFj6Eh8/YmRDfxT7C0k8FUFqNh+tshgb4O6Lgjg8K8elC4+oWCqnU/ # ML9lFfim8/9yJmZSe2F8AQ/UdKFOtj7YMTmqPO9mzskgiC3QYIUP2S3HQvHG1FDu # +WUqW4daIqToXFE/JQ/EABgfZXLWU0ziTN6R3ygQBHMUBaB5bdrPbF6MRYs03h4o # bEMnxYOX8VBRKe1uNnzQVTeLni2nHkX/QqvXnNb+YkDFkxUGtMTaiLR9wjxUxu2h # ECZpqyU1d0IbX6Wq8/gVutDojBIFeRlqAcuEVT0cKsb+zJNEsuEB7O7/cuvTQasn # M9AWcIQfVjnzrvwiCZ85EE8LUkqRhoS3Y50OHgaY7T/lwd6UArb+BOVAkg2oOvol # /DJgddJ35XTxfUlQ+8Hggt8l2Yv7roancJIFcbojBcxlRcGG0LIhp6GvReQGgMgY # xQbV1S3CrWqZzBt1R9xJgKf47CdxVRd/ndUlQ05oxYy2zRWVFjF7mcr4C34Mj3oc # CVccAvlKV9jEnstrniLvUxxVZE/rptb7IRE2lskKPIJgbaP5t2nGj/ULLi49xTcB # ZU8atufk+EMF/cWuiC7POGT75qaL6vdCvHlshtjdNXOCIUjsarfNZzCCB0kwggUx # oAMCAQICEAHdzU+FVN9jCMv0HhHagNUwDQYJKoZIhvcNAQELBQAwaTELMAkGA1UE # BhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2Vy # dCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENB # MTAeFw0yNjA2MDUwMDAwMDBaFw0yNzA2MDQyMzU5NTlaMIHRMRMwEQYLKwYBBAGC # NzwCAQMTAlVTMRkwFwYLKwYBBAGCNzwCAQITCERlbGF3YXJlMR0wGwYDVQQPDBRQ # cml2YXRlIE9yZ2FuaXphdGlvbjEQMA4GA1UEBRMHMzQwNzk4NTELMAkGA1UEBhMC # VVMxETAPBgNVBAgTCElsbGlub2lzMRAwDgYDVQQHEwdDaGljYWdvMR0wGwYDVQQK # ExRLZWVwZXIgU2VjdXJpdHkgSW5jLjEdMBsGA1UEAxMUS2VlcGVyIFNlY3VyaXR5 # IEluYy4wggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCb4DRTV0sNQsa1 # 0YRh+bliabmLOVYr6S0+BSVvRJAN3SHP6x52i1Dkpki5xVDIH06ZnnsToVrgvTv+ # QxGwsn9SAPHEZ/PIJRFxbMR4ShDaptYyL4f0u4k/3HwRzIleWE4mTUonYH8BdgLw # /F53B7wa7VTDHtxXltYTibEOwJxYCOi4Zr2FYQhjw14/CHcqS3FSMs6YYU2T56+g # w819hQM3K0YlwTNOFoIm1v7/ZZZiJGH8uGDsvy1makh1Xyyo/wN8EbQ1nbslmePT # roPm9w7WqiP/yiq+CZHiuTk9JK5bEgkWG3ns+v25cI251WidJx3SU7IZnX0OTd6/ # ZdKhprD5Gcfy5GBbJdcYw2WycQRW0PT5BEt55xRE0heufkpDaTUN6RdOuJdXbkl0 # hV91IZIuhueEMCk3h5mDTlU5gImxqj0R/TbAxjSSGTKCeuYFkQIRqytSabdrZZ48 # kW5hOIZMVDY1f4kpPJa8UeEvDZXT3vrtj36aSJrwez2uh4FMNlkCAwEAAaOCAgIw # ggH+MB8GA1UdIwQYMBaAFGg34Ou2O/hfEYb7/mF7CIhl9E5CMB0GA1UdDgQWBBT1 # SmCYU/7Yrz1fX66Ur5nSzlSYOzA9BgNVHSAENjA0MDIGBWeBDAEDMCkwJwYIKwYB # BQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAOBgNVHQ8BAf8EBAMC # B4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwgbUGA1UdHwSBrTCBqjBToFGgT4ZNaHR0 # cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25p # bmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcmwwU6BRoE+GTWh0dHA6Ly9jcmw0LmRp # Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNI # QTM4NDIwMjFDQTEuY3JsMIGUBggrBgEFBQcBAQSBhzCBhDAkBggrBgEFBQcwAYYY # aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFwGCCsGAQUFBzAChlBodHRwOi8vY2Fj # ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JT # QTQwOTZTSEEzODQyMDIxQ0ExLmNydDAJBgNVHRMEAjAAMA0GCSqGSIb3DQEBCwUA # A4ICAQBcavcUHNFEg872HDRq2+hRlnvaghCXv7X/6h9HSzjAQP3rt95BZty3ASqi # 2MYyGQLGdDl4DToe/WhajtEOBOYa83agW6tBvrfcKRrDrwJOMPTbwNYvn+GuiL4T # CKzXaytWiJJbrc5odc7Ecat2ZvJylpPmNainr4Q0LzzH23Gea/Mm/hIJTN4IGgrH # hrXiTIIW/ZUzrY6g8b3RZB4BA497n43wNdSqP+C3ntFw6NiGB4Z25SW4YntIxYPv # Kf37OVhF0xqxLC1sK/XxgK0EGQ6iaj8Ncpr2C5vSNZqfW2MndxOA1W67pgDpg83k # UWG+/YJeGhqOTF82/0kIzQXeI/lIqbnL/IJAJqSm/ROSpsGUKVbzk03cpTD55ZQX # WjM0fLirypBqY05T8gnh1L0fSwxr/SwJZ8OddivgyK1YOMn02nnsEG5kxBt9cMX4 # JCYABhypmAVDRvyYifEVdoFWv2gAXXW+PPRvlNa6E4aMCZrVcoKHiyeMAXOi1IC9 # mHvC2+foTSMFueq3AdnYfeKnZnAiKXKRhXcdHbQYcR2A7AIzIcqahPYr4FNEgb/E # /y/kypAkf0rMHlYl1kNqLs2Nv1UnMEHYT5YmDVLO63+1Trcw4zTZ70zuqIqeID/d # nbOlgtyG6DSRCL7f0E7kP18f4RoX5i1PkfeO4VJHsAuCeNG1qjGCBdkwggXVAgEB # MH0waTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYD # VQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2IFNI # QTM4NCAyMDIxIENBMQIQAd3NT4VU32MIy/QeEdqA1TANBglghkgBZQMEAgEFAKCB # hDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEE # AYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJ # BDEiBCC0egcQQVGbmfEf3MrcFPY3HgbcDkFBP1Mk9zE6g1DZajANBgkqhkiG9w0B # AQEFAASCAYAOcvib2J4TGxXGOwi/LcdI9UpHQIiFmmqfpdUoWSJ4Yvhce5tzswOR # xeD1A064wltQmbuJAhlymdVoDf9K0vUQaTH/QSZVTprth+g+XU4LVz5OKNjfkfy6 # 25v1KqkOGwNvWSsPOX/i6ZQvL4UICsCGfIPM0Cl9dGXmIWXu3P6NAbmPBrxNZrP+ # 446V0w/gHXUIxGajnRKXa35elkC9ftHbVov9q0uLAggfy1ZZKtTVLsp2S6J6vGpG # rNwgI/dEQnz98jUHnTbYmxhGaDvhdHrcEMEM9hl2dT0/aywAN8WxyVOPMCh1o4SN # ZAx7E1lE19tjymPlYa9ofgLpIOt0AAcG1VItUV3nTMVPEZr+QZZtXoZ3eV4T/qWM # fEYBKsTVKIypbBNPDcH2w1y20ijWueo55ejLjXNXPnOC43Czd4ZCY79faFZ936pb # FLBwvy9K0WscZq7+v2GnkRAOJJqJyshO1CtmNrOCTHPUXvWY/JjkgjnGopV6Obcz # niqCCBFx6yChggMmMIIDIgYJKoZIhvcNAQkGMYIDEzCCAw8CAQEwfTBpMQswCQYD # VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lD # ZXJ0IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYgU0hBMjU2IDIwMjUg # Q0ExAhAKgO8YS43xBYLRxHanlXRoMA0GCWCGSAFlAwQCAQUAoGkwGAYJKoZIhvcN # AQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjYwODE0MjExOTEzWjAv # BgkqhkiG9w0BCQQxIgQgIvHPhP6np63VKni4ovpyhocEeNv4sI3UbmMmVVtMQAsw # DQYJKoZIhvcNAQEBBQAEggIAQAip0AIRS58NdF/+YVWfbH4s6/n8Vg2tP5jTT3QY # 81Wsm++qJEeW1m+gELyFHl9uEjbYr5GKafDJ7GWPQBSgfaONCtSy1gUnbIBhvvTn # HbySqJXiEcKmfzAmJ0aNqGBxvHYPcN7HD2YL2Dac7hqExOiwtnKXvVVnoBre9Lrk # Gs1geIwU8YeTw1gheySS8CAyt8ef+b17tUQhNIqLTFAvI31e5Hxr2DLBYGHhoomS # 53DJYlvOm5i1PK8oQ7/AfGLIKnRU3yKgb85r5Tr7marv7SpnnXUGemcWb8rJpg1G # TFw62K+hIVK1b2GhjmSew0dA+7n1ejVOuXyt9gtu75yytgXjWcScPDWOLeq2MElB # MUO/J4Gz1yCmh0AS1Uy0TSfIQLfD98zRBB7EADfGKR1FCwwyAjFPlwL4p9bNn5Vb # XjU/19R/0+6tTNYjaO6vADzi6mrhcA/Ns0C5GlvSvn14pytEYOdj2CEICVNzq2pN # I84Zh+AepryhsJe+xSgTlhviSIMd9D2eKM+D2geXfvfobxr+EumS7i3jvfExkmdg # 5PQveqkHhAU00+JYLyzSB/lerao0rtghVdDdzg5FyfO10MQJVioDMmVuhokptQxW # Pm2JBB1VUZ/SL03E6xEomc3tjnzBHwD7npalJjLryMGfnA2nifJtCVkNlPP/5265 # ieg= # SIG # End signature block |