Enterprise.ps1

function getEnterprise {
    [KeeperSecurity.Authentication.IAuthentication] $auth = $Script:Context.Auth
    if (-not $auth) {
        Write-Error -Message "Not Connected" -ErrorAction Stop
    }
    if (-not $auth.AuthContext.IsEnterpriseAdmin) {
        Write-Error -Message "Not an Enterprise Administrator" -ErrorAction Stop
    }
    $enterprise = $Script:Context.Enterprise
    if (-not $enterprise) {
        $enterprise = New-Object Enterprise

        $enterprise.enterpriseData = New-Object KeeperSecurity.Enterprise.EnterpriseData
        $enterprise.roleData = New-Object KeeperSecurity.Enterprise.RoleData
        $enterprise.mspData = New-Object KeeperSecurity.Enterprise.ManagedCompanyData

        [KeeperSecurity.Enterprise.EnterpriseDataPlugin[]] $plugins = $enterprise.enterpriseData, $enterprise.roleData, $enterprise.mspData

        $enterprise.loader = New-Object KeeperSecurity.Enterprise.EnterpriseLoader($auth, $plugins)
        $enterprise.loader.Load().GetAwaiter().GetResult() | Out-Null

        if ($enterprise.enterpriseData.EnterpriseLicense.licenseStatus.StartsWith("msp")) {
            $enterprise.ManagedCompanies = @{}
        }

        $Script:Context.Enterprise = $enterprise
        $Script:Context.ManagedCompanyId = 0
    }

    if ($Script:Context.ManagedCompanyId -gt 0) {
        if ($null -ne $enterprise.ManagedCompanies) {
            $enterpriseMc = $enterprise.ManagedCompanies[$Script:Context.ManagedCompanyId]
            if ($null -eq $enterpriseMc) {
                $authMc = New-Object KeeperSecurity.Enterprise.ManagedCompanyAuth
                $authMc.LoginToManagedCompany($Script:Context.Enterprise.loader, $Script:Context.ManagedCompanyId).GetAwaiter().GetResult() | Out-Null

                $enterpriseMc = New-Object Enterprise
                $enterpriseMc.enterpriseData = New-Object KeeperSecurity.Enterprise.EnterpriseData
                $enterpriseMc.roleData = New-Object KeeperSecurity.Enterprise.RoleData
        
                [KeeperSecurity.Enterprise.EnterpriseDataPlugin[]] $plugins = $enterpriseMc.enterpriseData, $enterpriseMc.roleData
        
                $enterpriseMc.loader = New-Object KeeperSecurity.Enterprise.EnterpriseLoader($authMc, $plugins)
                $enterpriseMc.loader.Load().GetAwaiter().GetResult() | Out-Null
                $enterprise.ManagedCompanies[$Script:Context.ManagedCompanyId] = $enterpriseMc
            }
            $enterprise = $enterpriseMc
        } else {
            $Script:Context.ManagedCompanyId = 0
        }
    }

    return $enterprise
}

function Sync-KeeperEnterprise {
    <#
        .Synopsis
        Sync Keeper Enterprise Information
    #>


    [CmdletBinding()]
    [Enterprise]$enterprise = getEnterprise
    $task = $enterprise.loader.Load()
    $task.GetAwaiter().GetResult() | Out-Null
}
New-Alias -Name ked -Value Sync-KeeperEnterprise


function Get-KeeperEnterpriseUser {
    <#
        .Synopsis
        Get a list of enterprise users
    #>

    [CmdletBinding()]

    [Enterprise]$enterprise = getEnterprise
    return $enterprise.enterpriseData.Users
}
New-Alias -Name keu -Value Get-KeeperEnterpriseUser

function Get-KeeperEnterpriseTeam {
    <#
        .Synopsis
        Get a list of enterprise teams
    #>

    [CmdletBinding()]

    [Enterprise]$enterprise = getEnterprise
    return $enterprise.enterpriseData.Teams
}
New-Alias -Name ket -Value Get-KeeperEnterpriseTeam

$Keeper_TeamNameCompleter = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $result = @()
    [Enterprise]$enterprise = $Script:Context.Enterprise
    if (-not $enterprise) {
        return $null
    }
    if ($wordToComplete) {
        $to_complete = $wordToComplete + '*'
    }
    else {
        $to_complete = '*'
    }
    foreach ($team in $enterprise.enterpriseData.Teams) {
        if ($team.Name -like $to_complete) {
            $teamName = $team.Name
            if ($teamName -match '[\s'']') {
                $teamName = $teamName -replace '''', ''''''
                $teamName = "'${teamName}'"
            }

            $result += $teamName
        }
    }
    if ($result.Count -gt 0) {
        return $result
    }
    else {
        return $null
    }
}

function Get-KeeperEnterpriseTeamUser {
    <#
        .Synopsis
        Get a list of enterprise users for team
    #>

    [CmdletBinding()]
    Param (
        [Parameter(Position = 0, Mandatory = $true)]$Team
    )

    [Enterprise]$enterprise = getEnterprise
    $enterpriseData = $enterprise.enterpriseData
    $uid = $null

    if ($Team -is [String]) {
        $uids = Get-KeeperEnterpriseTeam | Where-Object { $_.Uid -ceq $Team -or $_.Name -ieq $Team } | Select-Object -Property Uid
        if ($uids.Length -gt 1) {
            Write-Error -Message "Team name `"$Team`" is not unique. Use Team UID" -ErrorAction Stop
        }

        if ($null -ne $uids.Uid) {
            $uid = $uids.Uid
        }
    }
    elseif ($null -ne $Team.Uid) {
        $uid = $Team.Uid
    }
    if ($uid) {
        $team = $null
        if ($enterpriseData.TryGetTeam($uid, [ref]$team)) {
            foreach ($userId in $enterpriseData.GetUsersForTeam($uid)) {
                $user = $null
                foreach ($userId in $enterpriseData.TryGetUserById($userId, [ref]$user)) {
                    $user
                }
            }
        }
        else {
            Write-Error -Message "Team `"$uid`" not found" -ErrorAction Stop
        }
    }
    else {
        Write-Error -Message "Team `"$Team`" not found" -ErrorAction Stop
    }
}
New-Alias -Name ketu -Value Get-KeeperEnterpriseTeamUser
Register-ArgumentCompleter -CommandName Get-KeeperEnterpriseTeamUser -ParameterName Team -ScriptBlock $Keeper_TeamNameCompleter

$Keeper_ActiveUserCompleter = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $result = @()
    [Enterprise]$enterprise = $Script:Context.Enterprise
    if (-not $enterprise) {
        return $null
    }
    if ($wordToComplete) {
        $to_complete = '*' + $wordToComplete + '*'
    }
    else {
        $to_complete = '*'
    }
    foreach ($user in $enterprise.enterpriseData.Users) {
        if ($user.UserStatus -in @([KeeperSecurity.Enterprise.UserStatus]::Active, [KeeperSecurity.Enterprise.UserStatus]::Disabled, [KeeperSecurity.Enterprise.UserStatus]::Blocked)) {
            if ($user.Email -like $to_complete) {
                $result += $user.Email
            }
        }
    }
    if ($result.Count -gt 0) {
        return $result
    }
    else {
        return $null
    }
}

function New-KeeperEnterpriseNode {
    <#
    .SYNOPSIS
    Creates Enterprise Node
 
    .PARAMETER ParentNode
    Parent Node name or ID
 
    .PARAMETER NodeName
    Node name
    #>

    [CmdletBinding()]
    Param (
        [Parameter()][string] $ParentNode,        
        [Parameter(Position = 0, Mandatory = $true)] $NodeName
    )

    [Enterprise]$enterprise = getEnterprise

    [KeeperSecurity.Enterprise.EnterpriseNode] $parent = $null
    if ($ParentNode) {
        $parent = resolveSingleNode $ParentNode
    }

    $n = [KeeperSecurity.Enterprise.EnterpriseExtensions]::CreateNode($enterprise.enterpriseData, $NodeName, $parent).GetAwaiter().GetResult()
    Write-Information "Added node `"$($n.DisplayName)`""
}
New-Alias -Name kena -Value New-KeeperEnterpriseNode

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
        }
    }

    $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
    if ($userObject) {
        $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

$Keeper_LockedUserCompleter = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $result = @()
    [Enterprise]$enterprise = $Script:Context.Enterprise
    if (-not $enterprise) {
        return $null
    }
    if ($wordToComplete) {
        $to_complete = '*' + $wordToComplete + '*'
    }
    else {
        $to_complete = '*'
    }
    foreach ($user in $enterprise.enterpriseData.Users) {
        if ($user.UserStatus -eq [KeeperSecurity.Enterprise.UserStatus]::Locked) {
            if ($user.Email -like $to_complete) {
                $result += $user.Email
            }
        }
    }
    if ($result.Count -gt 0) {
        return $result
    }
    else {
        return $null
    }
}

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
    if ($userObject) {
        $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

$Keeper_EnterpriseUserCompleter = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $result = @()
    [Enterprise]$enterprise = $Script:Context.Enterprise
    if (-not $enterprise) {
        return $null
    }
    if ($wordToComplete) {
        $to_complete = '*' + $wordToComplete + '*'
    }
    else {
        $to_complete = '*'
    }
    foreach ($user in $enterprise.enterpriseData.Users) {
        if ($user.Email -like $to_complete) {
            $result += $user.Email
        }
    }
    if ($result.Count -gt 0) {
        return $result
    }
    else {
        return $null
    }
}

function Move-KeeperEnterpriseUser {
    <#
        .Synopsis
        Transfers enterprise user account to another user
 
        .Parameter FromUser
        email or user ID to transfer vault from user
 
        .Parameter TargetUser
        email or user ID to transfer vault to user
    #>

    [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 Transfered:"
        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 ($userObject) {
        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 resolveUser {
    Param (
        $enterpriseData,
        $user
    )
    [KeeperSecurity.Enterprise.EnterpriseUser] $u = $null

    if ($user -is [long]) {
        if ($enterpriseData.TryGetUserById($user, [ref]$u)) {
            return $u
        }
    }
    elseif ($user -is [string]) {
        if ($enterpriseData.TryGetUserByEmail($user, [ref]$u)) {
            return $u
        }
    }
    elseif ($user -is [KeeperSecurity.Enterprise.EnterpriseUser]) {
        if ($enterpriseData.TryGetUserById($user.Id, [ref]$u)) {
            return $u
        }
    }
    Write-Error "`"${user}`" cannot be resolved as enterprise user" -ErrorAction Stop
}

function resolveSingleNode {
    Param ($node)

    if ($node) {
        $nodes = Get-KeeperEnterpriseNode | Where-Object { $_.Id -eq $node }
        if ($nodes.Length -eq 0) {
            $nodes = Get-KeeperEnterpriseNode | Where-Object { $_.DisplayName -like $node + '*' }
        }
        if ($nodes.Length -eq 0) {
            Write-Error -Message "Node `"$node`" not found" -ErrorAction Stop
        }
        if ($nodes.Length -gt 1) {
            Write-Error -Message "Node name `"$node`" is not unique. Use Node ID." -ErrorAction Stop
        }
        $nodes[0]
    }
}

function Get-KeeperEnterpriseNode {
    <#
        .Synopsis
        Get a list of enterprise nodes
    #>

    [CmdletBinding()]

    [Enterprise]$enterprise = getEnterprise
    return $enterprise.enterpriseData.Nodes
}
New-Alias -Name ken -Value Get-KeeperEnterpriseNode

function Get-KeeperEnterpriseRole {
    <#
        .SYNOPSIS
        Get a list of enterprise roles
 
        .PARAMETER Role
        Role Name or ID
    #>

    [CmdletBinding()]

    [Enterprise]$enterprise = getEnterprise
    return $enterprise.roleData.Roles
}
New-Alias -Name ker -Value Get-KeeperEnterpriseRole

$Keeper_RoleNameCompleter = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $result = @()
    [Enterprise]$enterprise = $Script:Context.Enterprise
    if (-not $enterprise) {
        return $null
    }
    if ($wordToComplete) {
        $to_complete = $wordToComplete + '*'
    }
    else {
        $to_complete = '*'
    }
    foreach ($role in $enterprise.roleData.Roles) {
        if ($role.DisplayName -like $to_complete) {
            $roleName = $role.DisplayName
            if ($roleName -match '[\s'']') {
                $roleName = $roleName -replace '''', ''''''
                $roleName = "'${roleName}'"
            }

            $result += $roleName
        }
    }
    if ($result.Count -gt 0) {
        return $result
    }
    else {
        return $null
    }
}

function Get-KeeperEnterpriseRoleUsers {
    <#
        .SYNOPSIS
        Get a list of enterprise users for a role
 
        .PARAMETER Role
        Role Name or ID
    #>

    [CmdletBinding()]
    Param (
        [Parameter(Position = 0, Mandatory = $true)]$Role
    )

    [Enterprise]$enterprise = getEnterprise
    $enterpriseData = $enterprise.enterpriseData
    $roleData = $enterprise.roleData
    $roleId = $null

    if ($Role -is [String]) {
        $ids = Get-KeeperEnterpriseRole | Where-Object { $_.Id -eq $Role -or $_.DisplayName -ieq $Role } | Select-Object -Property Id
        if ($ids.Length -gt 1) {
            Write-Error -Message "Role name `"$Role`" is not unique. Use Role ID" -ErrorAction Stop
        }

        if ($null -ne $ids.Id) {
            $roleId = $ids.Id
        }
    }
    elseif ($Role -is [long]) {
        $ids = Get-KeeperEnterpriseRole | Where-Object { $_.Id -ceq $Role } | Select-Object -First 1
        if ($ids.Length -eq 1) {
            $roleId = $ids[0].Id
        }
    }
    elseif ($null -ne $Role.Id) {
        $roleId = $Role.Id
    }
    if ($roleId) {
        $erole = $null
        if ($roleData.TryGetRole($roleId, [ref]$erole)) {
            foreach ($userId in $roleData.GetUsersForRole($erole.Id)) {
                $user = $null
                if ($enterpriseData.TryGetUserById($userId, [ref]$user)) {
                    $user
                }
            }
        }
        else {
            Write-Error -Message "Role `"$roleId`" not found" -ErrorAction Stop
        }
    }
    else {
        Write-Error -Message "Role `"$Role`" not found" -ErrorAction Stop
    }
}
New-Alias -Name keru -Value Get-KeeperEnterpriseRoleUsers
Register-ArgumentCompleter -CommandName Get-KeeperEnterpriseRoleUsers -ParameterName Role -ScriptBlock $Keeper_RoleNameCompleter

function Get-KeeperEnterpriseRoleTeams {
    <#
        .SYNOPSIS
        Get a list of enterprise teams for a role
 
        .PARAMETER Role
        Role Name or ID
    #>

    [CmdletBinding()]
    Param (
        [Parameter(Position = 0, Mandatory = $true)]$Role
    )

    [Enterprise]$enterprise = getEnterprise
    $enterpriseData = $enterprise.enterpriseData
    $roleData = $enterprise.roleData
    $roleId = $null

    if ($Role -is [String]) {
        $ids = Get-KeeperEnterpriseRole | Where-Object { $_.Id -eq $Role -or $_.DisplayName -ieq $Role } | Select-Object -Property Id
        if ($ids.Length -gt 1) {
            Write-Error -Message "Role name `"$Role`" is not unique. Use Role ID" -ErrorAction Stop
        }

        if ($null -ne $ids.Id) {
            $roleId = $ids.Id
        }
    }
    elseif ($Role -is [long]) {
        $ids = Get-KeeperEnterpriseRole | Where-Object { $_.Id -ceq $Role } | Select-Object -First 1
        if ($ids.Length -eq 1) {
            $roleId = $ids[0].Id
        }
    }
    elseif ($null -ne $Role.Id) {
        $roleId = $Role.Id
    }
    if ($roleId) {
        $erole = $null
        if ($roleData.TryGetRole($roleId, [ref]$erole)) {
            foreach ($teamUid in $roleData.GetTeamsForRole($erole.Id)) {
                $team = $null
                if ($enterpriseData.TryGetTeam($teamUid, [ref]$team)) {
                    $team
                }
            }
        }
        else {
            Write-Error -Message "Role `"$roleId`" not found" -ErrorAction Stop
        }
    }
    else {
        Write-Error -Message "Role `"$Role`" not found" -ErrorAction Stop
    }
}
New-Alias -Name kert -Value Get-KeeperEnterpriseRoleTeams
Register-ArgumentCompleter -CommandName Get-KeeperEnterpriseRoleTeams -ParameterName Role -ScriptBlock $Keeper_RoleNameCompleter

function Get-KeeperEnterpriseAdminRole {
    <#
        .SYNOPSIS
        Get a list of Administrator Permissions
 
        .PARAMETER Pattern
        Role search pattern
    #>

    [CmdletBinding()]
    Param (
        [Parameter(Position = 0, Mandatory = $false)]$Pattern
    )

    [Enterprise]$enterprise = getEnterprise
    $roleData = $enterprise.roleData
    $roles = $null

    if ($Pattern -is [String]) {
        $roles = Get-KeeperEnterpriseRole | Where-Object { $_.Id -eq $Pattern -or $_.DisplayName -match $Pattern } 
    }
    elseif ($Pattern -is [long]) {
        $roles = Get-KeeperEnterpriseRole | Where-Object { $_.Id -eq $Pattern } 
    }
    elseif ($null -ne $Pattern.Id) {
        $roles = $Pattern
    }
    else {
        $roles = Get-KeeperEnterpriseRole
    }
    if ($null -ne $roles -and $roles.Length -gt 0 ) {
        $roles = $roles | Sort-Object -Property DisplayName
        foreach ($role in $roles) {
            if ($null -ne $role.Id) {
                foreach ($rp in $roleData.GetRolePermissions($role.Id)) {
                    $rp
                }
            }
        }
    }        
    else {
        Write-Error -Message "Role `"$Role`" not found" -ErrorAction Stop
    }
}
New-Alias -Name kerap -Value Get-KeeperEnterpriseAdminRole

function Script:Get-KeeperNodeName {
    Param (
        [long]$nodeId
    )
    $enterprise = getEnterprise
    [KeeperSecurity.Enterprise.EnterpriseNode]$node = $null
    if ($enterprise.enterpriseData.TryGetNode($nodeId, [ref]$node)) {
        if ($node.ParentNodeId -gt 0) {
            return $node.DisplayName
        }
        else {
            return $enterprise.loader.EnterpriseName
        }
    }
}

function Script:Get-KeeperRoleName {
    Param (
        [long]$roleId
    )
    $enterprise = getEnterprise
    [KeeperSecurity.Enterprise.EnterpriseRole]$role = $null
    if ($enterprise.roleData.TryGetRole($roleId, [ref]$role)) {
        return $role.DisplayName
    }
}

# SIG # Begin signature block
# MIIR1wYJKoZIhvcNAQcCoIIRyDCCEcQCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUuj9T3MNU0lze9jpMvuHApQqX
# k1Sggg4jMIIGsDCCBJigAwIBAgIQCK1AsmDSnEyfXs2pvZOu2TANBgkqhkiG9w0B
# AQwFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVk
# IFJvb3QgRzQwHhcNMjEwNDI5MDAwMDAwWhcNMzYwNDI4MjM1OTU5WjBpMQswCQYD
# VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lD
# ZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEg
# Q0ExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1bQvQtAorXi3XdU5
# WRuxiEL1M4zrPYGXcMW7xIUmMJ+kjmjYXPXrNCQH4UtP03hD9BfXHtr50tVnGlJP
# DqFX/IiZwZHMgQM+TXAkZLON4gh9NH1MgFcSa0OamfLFOx/y78tHWhOmTLMBICXz
# ENOLsvsI8IrgnQnAZaf6mIBJNYc9URnokCF4RS6hnyzhGMIazMXuk0lwQjKP+8bq
# HPNlaJGiTUyCEUhSaN4QvRRXXegYE2XFf7JPhSxIpFaENdb5LpyqABXRN/4aBpTC
# fMjqGzLmysL0p6MDDnSlrzm2q2AS4+jWufcx4dyt5Big2MEjR0ezoQ9uo6ttmAaD
# G7dqZy3SvUQakhCBj7A7CdfHmzJawv9qYFSLScGT7eG0XOBv6yb5jNWy+TgQ5urO
# kfW+0/tvk2E0XLyTRSiDNipmKF+wc86LJiUGsoPUXPYVGUztYuBeM/Lo6OwKp7AD
# K5GyNnm+960IHnWmZcy740hQ83eRGv7bUKJGyGFYmPV8AhY8gyitOYbs1LcNU9D4
# R+Z1MI3sMJN2FKZbS110YU0/EpF23r9Yy3IQKUHw1cVtJnZoEUETWJrcJisB9IlN
# Wdt4z4FKPkBHX8mBUHOFECMhWWCKZFTBzCEa6DgZfGYczXg4RTCZT/9jT0y7qg0I
# U0F8WD1Hs/q27IwyCQLMbDwMVhECAwEAAaOCAVkwggFVMBIGA1UdEwEB/wQIMAYB
# Af8CAQAwHQYDVR0OBBYEFGg34Ou2O/hfEYb7/mF7CIhl9E5CMB8GA1UdIwQYMBaA
# FOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAK
# BggrBgEFBQcDAzB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9v
# Y3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4
# oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJv
# b3RHNC5jcmwwHAYDVR0gBBUwEzAHBgVngQwBAzAIBgZngQwBBAEwDQYJKoZIhvcN
# AQEMBQADggIBADojRD2NCHbuj7w6mdNW4AIapfhINPMstuZ0ZveUcrEAyq9sMCcT
# Ep6QRJ9L/Z6jfCbVN7w6XUhtldU/SfQnuxaBRVD9nL22heB2fjdxyyL3WqqQz/WT
# auPrINHVUHmImoqKwba9oUgYftzYgBoRGRjNYZmBVvbJ43bnxOQbX0P4PpT/djk9
# ntSZz0rdKOtfJqGVWEjVGv7XJz/9kNF2ht0csGBc8w2o7uCJob054ThO2m67Np37
# 5SFTWsPK6Wrxoj7bQ7gzyE84FJKZ9d3OVG3ZXQIUH0AzfAPilbLCIXVzUstG2MQ0
# HKKlS43Nb3Y3LIU/Gs4m6Ri+kAewQ3+ViCCCcPDMyu/9KTVcH4k4Vfc3iosJocsL
# 6TEa/y4ZXDlx4b6cpwoG1iZnt5LmTl/eeqxJzy6kdJKt2zyknIYf48FWGysj/4+1
# 6oh7cGvmoLr9Oj9FpsToFpFSi0HASIRLlk2rREDjjfAVKM7t8RhWByovEMQMCGQ8
# M4+uKIw8y4+ICw2/O/TOHnuO77Xry7fwdxPm5yg/rBKupS8ibEH5glwVZsxsDsrF
# hsP2JjMMB0ug0wcCampAMEhLNKhRILutG4UI4lkNbcoFUCvqShyepf2gpx8GdOfy
# 1lKQ/a+FSCH5Vzu0nAPthkX0tGFuv2jiJmCG6sivqf6UHedjGzqGVnhOMIIHazCC
# BVOgAwIBAgIQAnNTGQOIer82vZ1cJyDJDjANBgkqhkiG9w0BAQsFADBpMQswCQYD
# VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lD
# ZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEg
# Q0ExMB4XDTIyMDIwMjAwMDAwMFoXDTI1MDIwMTIzNTk1OVowcDELMAkGA1UEBhMC
# VVMxETAPBgNVBAgTCElsbGlub2lzMRAwDgYDVQQHEwdDaGljYWdvMR0wGwYDVQQK
# ExRLZWVwZXIgU2VjdXJpdHkgSW5jLjEdMBsGA1UEAxMUS2VlcGVyIFNlY3VyaXR5
# IEluYy4wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDNgTqmksdjUyKF
# 5zWkDyghf0PLWJWdzG0TX2j8B4J55xwt+B17zd4Xc3n0dvmSVAyPQANeN+mP1chf
# 4LTRn9h4jWb8Jsfn+JzyRhj/gYINYvBnpRpqoM0z7QC9Ebwj5T61Cogm9EKGcrG+
# Ujh+Z7pTqfSUrHD8NMXhDL/UpVn+w0Pb4qg7o7AH2o94n7u/qTlMGZCs+VCAvhNr
# wPABxvFY07YGb9t5/IZlPE8vG3p1vw2SbgREgFWSEQFj6X2CIhSrbiFCW/766/Mq
# EX6qm+RyF71fD4d3yShg39guaE9o+TBl1MqVCje4bK/wGoNxCho0I6Z1fBBKloyp
# vlx3gPpU7tJJ+KpuIiel9R9dGQuscqKzehPtbRc9Abr9ThN/HrLg1sFFVMdn2oMR
# 63QCUdz+B1NuS7Ap8Ti7XvAPJHzEuQDcdMcRbkIfllJVqrb9UXEFwOPzvRU2KrcQ
# 42Jlnn4T+WenPx5Nr3o/o08WLhLTicEK1OacEowyRLBmih4Gxpdk3fUAVCEkdvmq
# TSydQpl1Bk8V88dxCkB1wMZyFYLNcddBL4kUbwjso/z6f2TtfAVYs/iIRWqs7Xqt
# 4F2BBqobOGMymwg6VgVjjzDIgJCZSbjpq2IoVTci5vli6vxgSoZ01fccSaKa4Izm
# B7DbobIkIjLgPqpnCkqlHuJj5hQ9twIDAQABo4ICBjCCAgIwHwYDVR0jBBgwFoAU
# aDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYDVR0OBBYEFCZd3/KEdT2t5WTIFb3TUaM4
# sTikMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzCBtQYDVR0f
# BIGtMIGqMFOgUaBPhk1odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
# cnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDBToFGg
# T4ZNaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcmwwPgYDVR0gBDcwNTAzBgZn
# gQwBBAEwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BT
# MIGUBggrBgEFBQcBAQSBhzCBhDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGln
# aWNlcnQuY29tMFwGCCsGAQUFBzAChlBodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5j
# b20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIx
# Q0ExLmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQAGyDM3Cbxq
# Auhr8O2xwOoCSVKmFkXqicwlrugwLW44Y4WX+imvTrGfjj2S99k/4D5H8DgtW/u8
# tOxcCoehTOCIEwP5TLrieHppsqAR4jaJRcdAHOWiJ1bmwQBv/cBU9vaelL0oXxxf
# TwD9oDaQNuyq6p+nIJMqbKv33b8AWGe3zq4JwblaFjRDL5lUDNhPx3g/pm7JhnbX
# 7QTKydAJvpbuP5cqUH1GEeVMjc5vEELtGNy/fy7Ekm4dndX4IZcFXW5L0Lx8cReB
# hIZwA+pzdzTWQYvfxgRMb/j2uY+Tkb6Wz2x9BBS1UXiP2qrs3rhQv8DZRkUSqnko
# YD4uJP8gk8BXcIXIThgEF2YCq2hBiwna5Ijbwkmjn1lWwGv15SznTOTnrVApJqB1
# tB2s2ovUNV4CyKDPVr+9/CS6IQJfEZeHYcYLsIga2q5NZCrqZAasBfCwALVkALos
# DIWhs33vYLfETMSuk5Hd5JC+hLjVM3ZJwslvnc/wec2r0GNAiZ3a1aweC7NYuzRz
# 29Mi/eR/4ylmCltyZqYJ1JcC/g6eY2Q0xkdWc8P0yHfQ/3fe7+AKXXKNjfv858GW
# lg1Ck2lvwPdLqJWqj1FwJPiGRCB+WulPe0csTyWnf+ed45TXx69tZ6BZr0Xr2jXu
# ybBdJtg0NN0a62xxWrmX42CgsrzHzRm7OzGCAx4wggMaAgEBMH0waTELMAkGA1UE
# BhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2Vy
# dCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENB
# MQIQAnNTGQOIer82vZ1cJyDJDjAJBgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEK
# MAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3
# AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUv5gIvp1bnT/u3r1t
# +57sb1VkGnIwDQYJKoZIhvcNAQEBBQAEggIAIH6qhQlaK3rpOy+idkfpSgBMMaPj
# KGTIrCHUz3Z8oL8WJTq3a4N50rZfixaxUZhysOQElGJKfiuEfLfBjTO9yurhu+wv
# IRxvue8qb0BoYZ/5ydCcD0YuH22AtlovZGeKz/VxILzvojtdlccphPOYYzQaXZeG
# uIgOMkGb9/X4kf9Rh3s3oNE32JRvH+3OCzhXUaFXnnWMO30H50R7JRc043wjXe7j
# sEU9iV97TwWNR4ri2MaIELyNcjms9h5qh4vRV3ucMP+eK6FF8u31GjNzeWgZjiqF
# LosswVt1SiCgSvgnSwyM2FDIhVtWDJxKXGE2+bzMnkSQhj6KaUK67Cf2vuvvUW/S
# Gi0cNp5yL+H4l1WzACFafK3e2jW4UVDSY4uH/I17nK2Lzfs3tws56l7LXzJ0PzO2
# BaHEBaC+qz/wFlIj1XZYE6fdrdIsLeeZRS9ujSY3FzFyC0cVye4eT61ihBzBlZ6A
# Nny8Zolm+re66foMM5p2xuH0/XL425+UlU7IDhu6Jea9dGmf1sXzrOg4OztE9qdJ
# GcB7gqLq191C3rHuhOuZuDXP5OWEHhy9vaGlc3U0SDGT9b0K3sTlt5XrQ9DQ3WWj
# d/IG/GiseSdDBWdFhEavHfC1AAJZm4fioVvLnAk4XgRxCgYg9GUUIoYp0vOQwaI3
# FyWjM9iO+2lTrGs=
# SIG # End signature block