PSTeamViewer.psm1

<#
    Description: TeamViewer API Module
    Author: Marco Micozzi
    Requires Powershell 5
#>

#region Module Variables
[string] $script:fmt_AuthBearer = "Bearer {0}"
[string] $script:TOKEN_MISSING_ERROR = 'No AccessToken found, please supply the token or run Initialize-TeamViewerAPI with a valid token first'
[string] $script:TOKEN_INVALID = 'Invalid Token'
[hashtable] $script:TVConfig = @{
    BaseUrl = 'https://webapi.teamviewer.com'
    LoginUrl = 'https://login.teamviewer.com'
    ApiVersion = 'v1'
    AccessToken = ''
    TokenValid = $false
    Header = @{
        Authorization = ''
    }
}
#endregion

#region Classes

class TVToken{
    [ValidateNotNullOrEmpty()][string] $Access
    [ValidateNotNullOrEmpty()][string] $Type
    [ValidateNotNullOrEmpty()][string] $Refresh
    [ValidateNotNullOrEmpty()][int] $ExpiresIn
    [System.DateTime] $ExpireTime
    [bool] $Expired = $true
    [System.Timers.Timer] $Timer = $null

    TVToken($Access, $Type, $Refresh, $ExpiresIn){
        $this.Expired = $false
        $this.ExpireTime = (Get-Date).AddSeconds($ExpiresIn)
        $this.Access = $Access
        $this.ExpiresIn = $ExpiresIn
        $this.Refresh = $Refresh
        $this.Type = $Type
        $this.Timer = New-Object -TypeName System.Timers.Timer -Property @{Interval=$ExpiresIn*1000;AutoReset=$false}
        Register-ObjectEvent -InputObject $this.Timer -EventName 'Elapsed' -MessageData $this -Action {             
            Write-Verbose -Message ('The Elapsed event was raised at {0}' -f $EventArgs.SignalTime)    
            [TVToken] $token = $Event.MessageData
            $token.Expired = $true
        }
        $this.Timer.Enabled = $true   
    }
 }

class TVUserBase
{
    [ValidateNotNullOrEmpty()][string]$ID
    [ValidateNotNullOrEmpty()][string]$Name       
    TVUserBase($ID, $Name){
        $this.ID = $ID
        $this.Name = $Name
    }
}

class TVUser : TVUserBase
{
    [ValidateNotNullOrEmpty()][string[]]$Permissions
    [ValidateNotNullOrEmpty()][bool]$Active
    [bool]$LogSessions
    [bool]$ShowCommentWindow
    [string]$QuickSupportID
    [string]$QuickJoinID
    TVUser(
            $ID, 
            $Name, 
            $Permissions, 
            $Active, 
            $LogSessions, 
            $ShowCommentWindow, 
            $QuickSupportID, 
            $QuickJoinID
    ) 
    : base ($ID, $Name) 
    {        
       $this.Permissions = $Permissions
       $this.Active = $Active
       $this.QuickSupportID = $QuickSupportID
       $this.QuickJoinID = $QuickJoinID
       $this.ShowCommentWindow = $ShowCommentWindow
       $this.LogSessions = $LogSessions
    }
    TVUser(
            $ID, 
            $Name, 
            $Permissions, 
            $Active, 
            $LogSessions, 
            $ShowCommentWindow
    ) 
    : base ($ID, $Name) 
    {
       $this.Permissions = $Permissions
       $this.Active = $Active
       $this.ShowCommentWindow = $ShowCommentWindow
       $this.LogSessions = $LogSessions
    }
}

Class TVAccount : TVUserBase
{
    [string] $Email
    [string] $CompanyName
    TVAccount ( 
                [string] $ID, 
                [string] $Name, 
                [string] $Email, 
                [string] $CompanyName
    ) 
    : base ($ID, $Name)
    {
        $this.Email = $Email
        $this.CompanyName = $CompanyName
    }
}

Class TVGroupUser : TVUserBase
{
    [string] $Permissions = [string]::Empty
    [bool] $Pending = $false
    TVGroupUser ( $ID, $Name, $Permissions, $Pending) 
    : base ( $ID, $Name) 
    {
        $this.Permissions = $Permissions
        $this.Pending = $Pending
    }
    TVGroupUser ( $ID, $Name, $Permissions) 
    : base ( $ID, $Name) 
    {
        $this.Permissions = $Permissions
    }
}


Class TVGroup 
{
    [ValidateNotNullOrEmpty()][string]$ID
    [ValidateNotNullOrEmpty()][string]$Name
    [TVUserBase] $Owner
    [ValidateNotNullOrEmpty()][string]$Permissions
    [ValidateNotNullOrEmpty()][string]$PolicyID
    [TVGroupUser[]] $SharedWith
    TVGroup ( 
                [string] $ID, 
                [string] $Name, 
                [TVUserBase] $Owner, 
                [string] $Permissions, 
                [TVGroupUser[]] $SharedWith
            )
    {
        $this.ID = $ID
        $this.Name = $Name
        $this.Owner = $Owner
        $this.Permissions = $Permissions
        $this.SharedWith = $SharedWith
    }
}

Class TVDevice
{
    [string] $RemoteControlID
    [string] $DeviceID
    [string] $Alias
    [string] $GroupID
    [string] $OnlineStatus
    [bool] $AssignedTo
    [string] $SupportedFeatures
    TVDevice ( [string] $RemoteControlID, 
               [string] $DeviceID, 
               [string] $Alias, 
               [string] $GroupID, 
               [string] $OnlineStatus, 
               [bool] $AssignedTo, 
               [string] $SupportedFeatures
            )
    {
        $this.Alias = $Alias
        $this.AssignedTo = $AssignedTo
        $this.DeviceID = $DeviceID
        $this.GroupID = $GroupID
        $this.OnlineStatus = $OnlineStatus
        $this.RemoteControlID = $RemoteControlID
        $this.SupportedFeatures = $SupportedFeatures
    }
}
#endregion
    
#region Helpers
function Initialize-TVUserObject
{
    [CmdLetBinding()]
    [OutputType([TVUser])]
    param(
        [Parameter(Mandatory = $true)]
        [psobject] $Json
    )
    [string] $QuickSupportID = [System.String]::Empty
    if( $Json.custom_quicksupport_id -ne $null)
    {
        $QuickSupportID = $Json.custom_quicksupport_id
    }    
    [string] $QuickJoinID = [System.String]::Empty
    if( $Json.custom_quickjoin_id -ne $null)
    {
        $QuickJoinID = $Json.custom_quickjoin_id
    }    
    [string[]] $Permissions = $Json.permissions -split ','
    [TVUser] $TVUser = New-Object TVUser -ArgumentList $Json.id, `
                                                       $Json.name, `
                                                       $Permissions, `
                                                       $Json.active, `
                                                       $Json.log_sessions, `
                                                       $Json.show_comment_window, `
                                                       $QuickSupportID, `
                                                       $QuickJoinID
    Write-Output -InputObject $TVUser
}
#endregion Helpers

#region Initialization
function Initialize-TVAPI
{
    <#
    .SYNOPSIS
    Initialize the TeamViewer API wrapper
    
    .DESCRIPTION
    Initialize the TeamViewer API wrapper
    
    .PARAMETER Token
    The API token created on the management portal
    
    .EXAMPLE
    Initialize-TVAPI -Token "ABCD-1234"
    Initialize the TeamViewer API with the token "ABCD-1234"
    
    .NOTES
    Initializing the wrapper will allow all API call's to be made without having to specify the token in each call
    #>

    [CmdletBinding()]
    [OutputType([bool])]
    param(
        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [Alias('t')]
        [string] $Token
    )
    begin
    {
        [bool] $IsTokenValid = Test-TVToken -Token $Token
        if ( -not ( $IsTokenValid )) 
        {
            throw (New-Object -TypeName System.Exception -ArgumentList $script:TOKEN_INVALID)            
        }
    }
    process
    {
        $script:TVConfig.TokenValid = $IsTokenValid 
        $script:TVConfig.AccessToken = $Token
        $script:TVConfig.Header.Authorization = ($script:fmt_AuthBearer -f $Token)
    }    
}
#endregion

#region Ping

function Test-TVToken
{
    [CmdletBinding()]
    [OutputType([bool])]
    param(
        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string] $Token 
    )
    begin
    {    
        [string] $RequestUrl = ('{0}/api/{1}/ping' -f $script:TVConfig.BaseUrl, $script:TVConfig.ApiVersion)
        Write-Verbose -Message ('Compiled TV API request URI: "{0}".' -f $RequestUrl)
    }
    process
    {
        $TVResponse = Invoke-RestMethod -Method Get `
                                        -Uri $RequestUrl `
                                        -Headers @{ Authorization = ($script:fmt_AuthBearer -f $Token)} 
        Write-Verbose -Message $TVResponse
    }
    end
    {
        Write-Verbose -Message ('TRUE: {0}' -f ($TVResponse.token_valid -eq 'true'))
        return ( $TVResponse.token_valid -eq 'true')
    }
}
#endregion

#region Authorization

function Get-TVOAuth2Authorization
{
    [CmdletBinding( PositionalBinding = $false ) ]
    param(
        [Parameter( Mandatory = $true )]
        [string] $ClientID,
        
        [Parameter( Mandatory = $false )]
        [string] $RedirectURI = [string]::Empty
    )

    begin{
        Add-Type -AssemblyName PresentationFramework
        [string] $fmt_loginUtl = '{0}/oauth2/authorize?response_type=code&client_id={1}&redirect_url={2}&display=popup'
        [string] $Url = $fmt_loginUtl -f $script:TVConfig.LoginUrl, $ClientID, $RedirectURI
    }
    
    process
    {
        [System.Windows.Window] $Window = New-Object Windows.Window -Property @{Width=440;Height=640}
        $Window.Title = 'TeamViewer oAuth2'
        $window.WindowStartupLocation="CenterScreen" 
        [System.Windows.Controls.Grid] $Grid = New-Object -TypeName System.Windows.Controls.Grid 
        [System.Windows.Controls.WebBrowser] $Browser = New-Object -TypeName System.Windows.Controls.WebBrowser `
                                                            -Property @{Source=($Url -f ($Scope -join "%20")) }
        $Browser.HorizontalAlignment = [System.Windows.HorizontalAlignment]::Stretch
        $Browser.VerticalAlignment = [System.Windows.VerticalAlignment]::Stretch
        $Grid.AddChild($Browser)
        $Window.Content = $Grid
        $window.ShowDialog() | Out-Null
    }
}


function Revoke-TVOauth2Token
{
    [OutputType([bool])]    
    [CmdLetBinding()]
    param(
        [string] $Token
    )


}

<#
    This function needs to be tested!!!
#>

function Get-TVOauth2Token
{
    [OutputType([TVToken])]    
    [CmdletBinding()]
    param(

        [Parameter(
            Mandatory=$true, HelpMessage = 'Authorization code acquired from the /oauth2/authorize page.',
            ParameterSetName = 'Grant'
        )]
        [string] $AuthorizationCode,

        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Must be the same value as in the previous call to /oauth2/authorize',
            ParameterSetName = 'Grant'
        )]
        [string] $RedirectURI = [string]::Empty,
        
        [Parameter(
            Mandatory = $true,
            HelpMessage = 'Refresh-token from a previous call.',
            ParameterSetName = 'RefreshToken'
        )]
        [string] $RefreshToken,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'Client ID, a unique string that identifies the application.',
            ParameterSetName = 'Grant'
        )]        
        [Parameter(
            Mandatory = $true,
            HelpMessage = 'Client ID, a unique string that identifies the application.',
            ParameterSetName = 'RefreshToken'
        )]
        [string] $ClientID,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'The client secret, which is known only to the creator of the application.',
            ParameterSetName = 'Grant'
        )]        
        [Parameter(
            Mandatory = $true,
            HelpMessage = 'Client ID, a unique string that identifies the application.',
            ParameterSetName = 'RefreshToken'
        )]
        [string] $ClientSecret

    )

    Write-Warning -Message 'The function "Get-TVOauth2Token" needs proper testing!!!'

    [string] $RequestUrl = ('{0}/api/{1}/oauth2/token' -f $script:TVConfig.BaseUrl, $script:TVConfig.ApiVersion)

    Try
    {
        
        [System.Net.WebRequest] $Request = [System.Net.WebRequest]::Create($RequestUrl)
        $Request.Method = 'POST'
        $Request.ContentType = 'application/x-www-form-urlencoded'
    
        [string] $Parameters = ('client_id={0}&client_secret={1}' -f $ClientID, $ClientSecret )
        
        switch ( $PSCmdlet.ParameterSetName ){
            
            'Grant' {
                $Parameters += ('&grant_type=authorization_code&code={0}&redirect_uri={1}' -f $AuthorizationCode, $RedirectURI)
            }
            
            'RefreshToken' {
                $Parameters += ('&grant_type=refresh_token&refresh_token={0}' -f $RefreshToken)
            }
            
            Default{
                Write-Error -Message ('Unexpected ParameterSetname received: "{0}".' -f $PSCmdlet.ParameterSetName)
            }

        }
        
        Write-Verbose -Message ('Got Parameters: "{0}".' -f $Parameters)
        
        [byte[]] $Payload = [System.Text.Encoding]::UTF8.GetBytes($Parameters)
    
        Write-Verbose -Message ('Got Payload: "{0}".' -f $Payload)
    
        [System.IO.Stream] $RequestStream = $Request.GetRequestStream()
        $RequestStream.Write($Payload, 0, $Payload.Length)
        $RequestStream.Close()

        Write-Verbose -Message 'closing stream'

        [System.Net.WebResponse] $Response = $Request.GetResponse()
        Write-Verbose -Message 'Got repsonse'
        [string] $StatusString = $Response.StatusCode
        [int] $StatusCode = [int] $Response.StatusCode

        Write-Verbose -Message ('Got Status Code: "{0}" with message: "{1}".' -f $StatusCode, $StatusString)

        [System.IO.Stream] $ResponseStream = $Response.GetResponseStream()
        [System.IO.StreamReader] $StreamReader = New-Object -TypeName System.IO.StreamReader `
                                                            -ArgumentList $ResponseStream
        [string] $Result = $StreamReader.ReadToEnd()

        if ( $StatusCode -ne 200)
        {
            # false
            Write-Verbose -Message ( 'Status code {0}' -f $StatusCode)
            
        }
        else
        {
            $jsonResponse = ConvertFrom-Json -InputObject $result 
            
            [TVToken] $Token = New-Object -TypeName TVToken -ArgumentList $jsonResponse.access_token, $jsonResponse.token_type, $jsonResponse.refresh_token, $jsonResponse.expires_in
    
            Write-Output -InputObject $Token

        }       

    }
    catch
    {
        Write-Host ("Token: Request failed! The error was '{0}'" -f $_)
        $_.Exception
        #$resstream = $_.Exception.InnerException.Response.GetResponseStream()
        #$sr = new-object System.IO.StreamReader $resstream
        #$value = $sr.ReadToEnd()
        #Write-Host $value

    }
    finally
    {
        if ($Response)
        {
            Write-Verbose -Message ('Closing response')
            $Response.Close()
            Write-Verbose -Message ('Response closed')
        }
        
    }
    
}

#endregion Authorization

#region Account
function Get-TVAccount
{
    [CmdletBinding()]
    [OutputType([TVAccount])]
    param(
        [Parameter()]
        [string] $Token = $script:TVConfig.AccessToken
    )
    begin
    {
        if ( [string]::IsNullOrEmpty($Token) ) 
        {
            throw (New-Object -TypeName System.Exception -ArgumentList $script:TOKEN_MISSING_ERROR)
        }   
    }    
    process
    {
        [string] $RequestUrl = ('{0}/api/{1}/account' -f $script:TVConfig.BaseUrl, $script:TVConfig.ApiVersion)        
        $Response = Invoke-RestMethod -Uri $RequestUrl `
                                      -Headers $script:TVConfig.Header `
                                      -Body $RequestBody `
                                      -Method 'Get'
        [TVAccount] $Account = New-Object -TypeName TVAccount `
                                          -ArgumentList $Response.userid, `
                                                        $Response.name, `
                                                        $Response.email, `
                                                        $Response.company_name
        Write-Output -InputObject $Account
    }
}
#endregion Account

#region Users

# TODO: Permissions
# permissions (optional) – Comma-separated list of permissions that this user has.
# See 1.6.2 for valid values and combinations.
# If omitted the following default permissions will be set: ShareOwnGroups, ViewOwnConnections, EditConnections, EditFullProfile
function New-TVUser
{
    <#
    #>

    [CmdletBinding( PositionalBinding = $false ) ]
    [OutputType([TVUser])]
    param(
        
        [Parameter()]
        [string] $Token = $script:TVConfig.AccessToken,
 
        [Parameter(
            Mandatory = $true, 
            HelpMessage = 'The name of the user to create.'
        )]
        [string] $Name,

        [Parameter(
            Mandatory = $true, 
            HelpMessage = 'The email address of the user to create.'
        )]
        [ValidateScript({
            if ($_ -match "^([0-9a-zA-Z]([-\.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$") {
              $true
            }
            else {
              Throw "$_ is not a valid email address"
            }
        })]
        [string] $Email,

        [Parameter(
              Mandatory = $true, 
              HelpMessage = 'The password of the user to create.'
        )]
          [securestring]$Password,

        [Parameter(
            Mandatory = $false
        )]
        [ValidateSet('en', 'nl', 'fr', 'es')]
        [string] $Language = 'en',

        [Parameter(
            Mandatory = $false
        )]
        [ValidateNotNullOrEmpty()]
        [string] $QuickSupportID = [string]::Empty,

        [Parameter(
            Mandatory = $false
        )]
        [ValidateNotNullOrEmpty()]
        [string] $QuickJoinID = [string]::Empty

    )

    begin
    {        
        # Make sure to have a token
        if ( [string]::IsNullOrEmpty($Token) ) 
        {
            throw (New-Object -TypeName System.Exception -ArgumentList $script:TOKEN_MISSING_ERROR)
        }  
        [string] $RequestUrl = ('{0}/api/{1}/users' -f $script:TVConfig.BaseUrl, $script:TVConfig.ApiVersion)              
    }

    process 
    {      
        [hashtable] $Params = @{
            name = $Name
            email = $Email
            password =  (New-Object PSCredential "user",$Password).GetNetworkCredential().Password
            language = $Language
        }  

        if ( -not ( [string]::IsNullOrEmpty($QuickJoinID)))
        {
            $Params.custom_quickjoin_id = $QuickJoinID
        }
        
        if ( -not ( [string]::IsNullOrEmpty($QuickSupportID)))
        {
            $Params.custom_quicksupport_id = $QuickSupportID
        }
        
        # TODO: Add permission support

        Write-Verbose -Message ('Running Request URl: "{0}"' -f $RequestUrl)
        
        Try
        {        
            $response = Invoke-RestMethod -Method Post `
                                          -Uri $RequestUrl `
                                          -Headers $script:TVConfig.Header `
                                          -Body ( $Params | ConvertTo-Json ) `
                                          -ContentType 'application/json' `
                                          -ErrorAction SilentlyContinue -ErrorVariable respError     
            # returns 204 on success
            Write-Verbose -Message "Fetching..."
            Get-TVUser -Email $Email -Token $Token
        }
        catch
        {           
            Write-Output $_
            #$ErrJsonoutWrite-Output $_.ErrorDetails.Message | convertFrom-json
            #Write-Error -Message $ErrJson.error_description
        }
    }
}

#TODO: User or company access token. Scope: Users.ModifyUsers or Users.ModifyAdministrators.
function Set-TVUser
{
    [CmdletBinding()]
    [OutputType([TVUser])]   
    param(

        [Parameter()]
        [string] $Token = $script:TVConfig.AccessToken,

        [Parameter(
            Mandatory = $true, 
            ParameterSetName = 'ByIdentity', 
            ValueFromPipeline = $true)]
        [ValidateNotNull()]
        [TVUser] $Identity,

        [Parameter(
            Mandatory = $true, 
            ParameterSetName = 'ById')]
        [string] $UserID,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string] $Name = $null,
        
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string] $Email = $null,
        
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('None', 'ManageAdmins', 'ManageUsers', 'ShareOwnGroups', 'ViewAllConnections', 
                     'ViewOwnConnections', 'EditConnections', 'DeleteConnections', 'EditFullProfile', 
                     'ManagePolicies', 'AssignPolicies', 'AcknowledgeAllAlerts', 'AcknowledgeOwnAlerts', 
                     'ViewAllAssets', 'ViewOwnAssets', 'EditAllCustomModuleConfigs', 'EditOwnCustomModuleConfigs')]
        [string[]] $Permissions = $null,
        
        [Parameter(
            Mandatory = $false
        )]
        [ValidateNotNullOrEmpty()]
        [securestring] $Password = $null,
        
        [Parameter()]
        [bool] $Active,
        
        [Parameter(
            Mandatory = $false
        )]
        [ValidateNotNullOrEmpty()]
        [string] $QuickJoinID = $null,
        
        [Parameter(
            Mandatory = $false
        )]
        [ValidateNotNullOrEmpty()]
        [string] $QuickSupportID = $null
    )
    begin
    {

        # Make sure to have a token
        if ( [string]::IsNullOrEmpty($Token) ) 
        {
            throw (New-Object -TypeName System.Exception -ArgumentList $script:TOKEN_MISSING_ERROR)
        }

        Write-Verbose -Message ('ParameterSet: {0}' -f $PSCmdlet.ParameterSetName)
        
        if ( $PSCmdlet.ParameterSetName -eq 'ById')
        {
            $Identity = Get-TVUser -UserID $UserID 
        }
    }
    process
    {

        [string] $RequestUrl = ('{0}/api/{1}/users/{2}' -f $script:TVConfig.BaseUrl, $script:TVConfig.ApiVersion, $Identity.ID)

        [hashtable] $Params = @{}

        if ( -not ( [string]::IsNullOrEmpty($Name)))
        {
            $Params.name = $Name
        }

        if ( -not ( [string]::IsNullOrEmpty($Password)))
        {
            $Params.password =  (New-Object PSCredential "user",$Password).GetNetworkCredential().Password
        }

        if ( -not ( [string]::IsNullOrEmpty($Email)))
        {
            $Params.email = $Email
        }

        if ( -not ( [string]::IsNullOrEmpty($QuickJoinID)))
        {
            $Params.custom_quickjoin_id = $QuickJoinID
        }
        
        if ( -not ( [string]::IsNullOrEmpty($QuickSupportID)))
        {
            $Params.custom_quicksupport_id = $QuickSupportID
        }
                    
        if ( $PSBoundParameters.ContainsKey( "Active" ) ) 
        {
            $Params.active = $Active
        }

        if ( $Permissions -ne $null )
        {
            Write-Verbose -Message 'not supported yed'
            #$Params.permissions = $Permissions -join ','
        }

        Write-Verbose -Message ('Running Request URl: "{0}"' -f $RequestUrl)

        Try
        {
            
            $response = Invoke-RestMethod -Method Put `
                                          -Uri $RequestUrl `
                                          -Headers $script:TVConfig.Header `
                                          -Body ( $Params | ConvertTo-Json ) `
                                          -ContentType 'application/json' `
                                          -ErrorVariable respError     

            Get-TVUser -UserID $Identity.ID -Token $Token
        
        }
        catch
        {           
            # Write-Host -Object ('Error: "{0}". - Description: "{1}". - Code: "{2}".' -f $_.error, $response.error_description, $response.error_code)
            # Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__
            # Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription
            # Write-Host "StatusCode:" ([int]$_.Exception.Response.StatusCode)
            $ErrJson = $_.ErrorDetails.Message | convertFrom-json 
            Write-Error -Message ('Error: {0}' -f $ErrJson.error_description)
            #.Message.error_description
        }
        
    }
    end{}
}

function Get-TVUser 
{
    <#
    .SYNOPSIS
    Get Teamviewer user(s)
    
    .DESCRIPTION
    Get the details of one or more users from the Teamviewer management portal
    
    .PARAMETER Token
    The Teamviewer API token
    
    .PARAMETER UserID
    The ID of the user on the Teamviewer portal
    
    .PARAMETER Name
    The name of the user
    
    .PARAMETER Email
    The email address of the user
    
    .PARAMETER Permission
    Permissions assigned to a user
    
    .PARAMETER Simple
    minimize the output of the CmdLet
    
    .EXAMPLE
       Get-TVUser -Verbose | Where-Object { $_.Active -eq $false }

    #>

    [CmdletBinding(PositionalBinding=$false, DefaultParameterSetName="All")]
    [OutputType([TVUser[]])]    
    param(
        [Parameter()]
        [string] $Token = $script:TVConfig.AccessToken,
        [Parameter( Mandatory = $true, ParameterSetName = 'ByID')]
        [string[]] $UserID,
        [Parameter( Mandatory = $true, ParameterSetName = 'ByName')]
        [string] $Name,
        [Parameter(Mandatory = $true, ParameterSetName = 'ByEmail')]
        [string[]] $Email,
        [Parameter(Mandatory = $true,ParameterSetName = 'ByPermission')]
        [string[]] $Permission,
        [Parameter()]
        [switch] $Simple
    )
    begin
    {
        # Make sure to have a token
        if ( [string]::IsNullOrEmpty($Token) ) 
        {
            throw (New-Object -TypeName System.Exception -ArgumentList $script:TOKEN_MISSING_ERROR)
        }
        [hashtable] $RequestBody = @{
            full_list = (!$Simple.IsPresent)
        }
        [string] $RequestUrl = ('{0}/api/{1}/users' -f $script:TVConfig.BaseUrl, $script:TVConfig.ApiVersion)   
    }
    process
    {
        Write-Debug -Message ('ParameterSetNamer: "{0}".' -f $PSCmdlet.ParameterSetName)
        switch ($PSCmdlet.ParameterSetName) 
        {
            'ByID' { 
                Write-Debug -Message 'Checking by ID'
                foreach ( $User in $UserID) {
                    Write-Verbose -Message ('Processing ID: "{0}".' -f $User)
                    $Json = Invoke-RestMethod -Uri ('{0}/{1}' -f $RequestUrl, $User) -Headers $script:TVConfig.Header -Body $RequestBody -Method 'Get'
                    Write-Verbose -Message ($Json | ConvertTo-Json)
                    Write-Output -InputObject ( Initialize-TVUserObject -Json $Json )
                }
              }
            { ( $_ -eq 'ByName' )  -or ( $_ -eq 'ByEmail' ) -or ( $_ -eq 'All') } {  
                Write-Debug -Message 'The received parameters are Name, Email or none'                
                if ( $PSCmdlet.ParameterSetName -eq 'ByName')
                {
                    Write-Verbose -Message ('Checking by name: "{0}".' -f $Name)                    
                    $RequestBody.name = $Name
                }
                if ( $PSCmdlet.ParameterSetName -eq 'ByEmail')
                {
                    $RequestBody.email = @{$true = $Email -join ', '; $false = $Email -join ''}[ ($Email.Count -ge 2)]   
                    Write-Verbose -Message ('Checking by mail: "{0}".' -f $RequestBody.email )    
                }
                Write-Verbose -Message ('Get-TVUser: Processing URL: "{0}".' -f $RequestUrl)
                $Response = Invoke-RestMethod -Uri $RequestUrl -Headers $script:TVConfig.Header -Body $RequestBody -Method 'Get'
                Write-Verbose -Message $response
                $Response.users | ForEach-Object {       
                    Write-Verbose -Message $_            
                    Write-Output -InputObject ( Initialize-TVUserObject -Json $_ )
                }
            }
            'ByPermission' { 
                throw New-Object -TypeName System.NotImplementedException -ArgumentList 'Search by Permission has not been implemented yet.'
            }            
            Default {
                throw New-Object -TypeName System.ArgumentException -ArgumentList ('Unexpected ParameterSet received: "{0}".' -f $PSCmdlet.ParameterSetName)
            }
        }
    }
}

#endregion Users

#region Devices

function Set-TVDevice
{
    [CmdletBinding()]
    [OutputType([TVDevice])]
    param(
        [Parameter( Position = 0 )]
        [string] $Token = $script:TVConfig.AccessToken,

        [Parameter( Mandatory = $true, Position = 1, ParameterSetName = 'ByDeviceIDPolicy' )]
        [Parameter( Mandatory = $true, Position = 1, ParameterSetName = 'ByDeviceIDGroup' )]
        [string] $DeviceID,

        [Parameter( Mandatory = $true, Position = 1, ParameterSetName = 'ByInputObjectPolicy' )]
        [Parameter(Mandatory = $true, Position = 1, ParameterSetName = 'ByInputObjectGroup' )]
        [ValidateNotNull()]
        [TVDevice] $InputObject,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string] $Alias,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string] $Description,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string] $Passwd,

        [Parameter(ParameterSetName = 'ByDeviceIDPolicy')]
        [Parameter(ParameterSetName = 'ByInputObjectPolicy')]
        [ValidateNotNullOrEmpty()]
        [string] $PolicyID,

        [Parameter(ParameterSetName = 'ByInputObjectGroup')]
        [Parameter(ParameterSetName = 'ByDeviceIDGroup')]
        [ValidateNotNullOrEmpty()]
        [string] $GroupID,

        [Parameter()]
        [switch] $PassThru

    )

 
    
    begin
    {

        # Make sure to have a token
        if ( [string]::IsNullOrEmpty($Token) ) 
        {
            throw (New-Object -TypeName System.Exception -ArgumentList $script:TOKEN_MISSING_ERROR)
        }

        Write-Verbose -Message ('ParameterSet: {0}' -f $PSCmdlet.ParameterSetName)
        
        if ( $PSCmdlet.ParameterSetName -eq 'ById')
        {
            $InputObject = Get-TVDevice | Where-Object { $_.DeviceID -eq $DeviceID}
        }
    }
    process
    {
        [string] $RequestUrl = ('{0}/api/{1}/devices/{2}' -f $script:TVConfig.BaseUrl, $script:TVConfig.ApiVersion, $InputObject.DeviceID)  
    
        [hashtable] $Params = @{}

        if ( -not ( [string]::IsNullOrEmpty($Alias)))
        {
            $Params.alias = $Alias
        }
        
        if ( -not ( [string]::IsNullOrEmpty($Description)))
        {
            $Params.description = $Description
        }
                    
        if ( -not ( [string]::IsNullOrEmpty($Passwd)))
        {
            $Params.password = $Passwd
        }
                    
        if ( -not ( [string]::IsNullOrEmpty($PolicyID)))
        {
            $Params.policy_id = $PolicyID
        }
                    
        if ( -not ( [string]::IsNullOrEmpty($GroupID)))
        {
            $Params.groupid = $GroupID
        }

        
        Try
        {
            Invoke-RestMethod -Method Put `
                                            -Uri $RequestUrl `
                                            -Headers $script:TVConfig.Header `
                                            -Body ( $Params | ConvertTo-Json ) `
                                            -ContentType 'application/json' `
                                            -ErrorVariable respError     

            #Get-TVUser -UserID $Identity.ID -Token $Token
    
        }
        catch
        {           
            $ErrJson = $_.ErrorDetails.Message | convertFrom-json 
            Write-Error -Message $ErrJson.error_description
        }
    }
}

function Get-TVDevice
{
    <#
    .SYNOPSIS
    Get Device information fom the Teamviewer portal
    
    .DESCRIPTION
    Get Device information fom the Teamviewer portal
    
    .PARAMETER Token
    The API token generated on the portal
    
    .PARAMETER OnlineState
    include online or offline devices
    
    .PARAMETER GroupID
    Get all devices of a specific GroupID
    
    .PARAMETER RemoteControlID
    Get the device with a specific RemotecontrolID
    
    .EXAMPLE
    Get-TVDevice -OnlineState Online
       
    #>

    [CmdletBinding()]
    [OutputType([TVDevice[]])]
    param(

        [Parameter()]
        [string] $Token = $script:TVConfig.AccessToken,

        [Parameter(Mandatory = $false)]
        [ValidateSet('Online', 'Offline')]
        [string] $OnlineState = [string]::Empty,

        [Parameter(Mandatory=$false)]
        [string] $GroupID = [string]::Empty,    
        
        [Parameter(Mandatory=$false)]
        [string] $RemoteControlID = [string]::Empty
    )
    
    begin
    {
                
        if ( [string]::IsNullOrEmpty($Token) ) {
            throw (New-Object -TypeName System.Exception -ArgumentList $script:TOKEN_MISSING_ERROR)
        }
                
        [string] $RequestUrl = ('{0}/api/{1}/devices' -f $script:TVConfig.BaseUrl, $script:TVConfig.ApiVersion)  
        
        [hashtable] $RequestBody = @{}
    }

    process
    {

        if ( ! ( [string]::IsNullOrEmpty($OnlineState)))
        {
            $RequestBody.online_state = $OnlineState
        }

        if ( ! ( [string]::IsNullOrEmpty($GroupID)))
        {
            $RequestBody.groupid = $GroupID
        }

        if ( ! ( [string]::IsNullOrEmpty($RemoteControlID ) ) ) 
        {
            $RequestBody.remotecontrol_id = $RemoteControlID
        }

        $Response = Invoke-RestMethod -Uri $RequestUrl -Headers $script:TVConfig.Header -Body $RequestBody

        $Response.devices | ForEach-Object {
                        
            [TVDevice] $TVDevice = New-Object -TypeName TVDevice `
                                            -ArgumentList $_.remotecontrol_id, $_.device_id, $_.alias, 
                                                          $_.groupid, $_.online_state, $_.assigned_to, 
                                                          $_.supported_features
            Write-Output $TVDevice                                                          

        }

    }

}
#endregion Devices

#region Groups

function Remove-TVGroup
{
    [CmdletBinding(SupportsShouldProcess=$true)]
    param(

        [Parameter()]
        [string] $Token = $script:TVConfig.AccessToken,

        [Parameter(
            Mandatory=$true, 
            ValueFromPipeline = $true,
            ParameterSetName = 'ByInputObject'
        )]
        [ValidateNotNull()]
        [TVGroup] $InputObject,

        [Parameter(
            Mandatory=$true, 
            HelpMessage = 'The name of the group to delete.',
            ParameterSetName = 'ByName'    
        )]
        [string] $Name,

        [Parameter(
            Mandatory = $true, 
            HelpMessage = 'The Group ID of the group to delete.',
            ParameterSetName = 'ByID'
        )]
        [string] $GroupID,

        [Parameter()]
        [string] $CompanyUserID = [string]::Empty
    )

    begin{
        
        # Check token
        if ( [string]::IsNullOrEmpty($Token) ) {
            throw (New-Object -TypeName System.Exception -ArgumentList $script:TOKEN_MISSING_ERROR)
        }
        
        [string] $AdminParam = @{$true=('/users/{0}' -f $CompanyUserID);$false=[string]::Empty}[(!( [string]::IsNullOrEmpty($CompanyUserID)))]
        
        [string] $RequestUrl = ('{0}/api/{1}{2}/groups' -f $script:TVConfig.BaseUrl, $script:TVConfig.ApiVersion, $AdminParam)  
                       
    }
    
    process{
       
        switch ($PSCmdlet.ParameterSetName) {
            'ByName' {
                $InputObject = Get-TVGroup -Name $Name
              }
            'ByInputObject' {}
            'ByID' {
                $InputObject = Get-TVGroup | Where-Object { $_.ID -eq $GroupID }    
            }
            Default {}
        }

        if(!($InputObject)){
            throw 'Invalid groupname received.'
        }

        $RequestUrl += ('/{0}' -f $InputObject.ID)
        Write-Verbose -Message ('Processing RequestURL: {0}' -f $RequestUrl)
                   
        Try{
            if ( $PSCmdlet.ShouldProcess($InputObject.Name, 'Remove Group') ){
                Invoke-RestMethod -Method Delete `
                                              -Uri $RequestUrl `
                                              -Headers $script:TVConfig.Header `
                                              -ContentType 'application/json' `
                                              -ErrorAction SilentlyContinue -ErrorVariable respError     
                return $true                                    
            }
        }
        catch{           
            Write-Host $_
            $ErrJson = $_ | convertFrom-json 
            Write-Error -Message ("err: {0}" -f $ErrJson.message    )
        }

    }

}

function New-TVGroup
{
    [CmdletBinding()]
    param(

        [Parameter()]
        [string] $Token = $script:TVConfig.AccessToken,

        [Parameter(Mandatory=$true, HelpMessage = 'The name of the group to create.')]
        [string[]] $Name,

        [Parameter()]
        [string] $CompanyUserID = [string]::Empty
    )

    begin{

        # Make sure to have a token
        if ( [string]::IsNullOrEmpty($Token) ) {
            throw (New-Object -TypeName System.Exception -ArgumentList $script:TOKEN_MISSING_ERROR)
        }

        [string] $AdminParam = @{$true=('/users/{0}' -f $CompanyUserID);$false=[string]::Empty}[(!( [string]::IsNullOrEmpty($CompanyUserID)))]

        [string] $RequestUrl = ('{0}/api/{1}{2}/groups' -f $script:TVConfig.BaseUrl, $script:TVConfig.ApiVersion, $AdminParam)  
        
        Write-Verbose -Message ('Processing RequestURL: {0}' -f $RequestUrl)

    }

    process 
    {

        foreach ( $GroupName in $Name)
        {
            
            [hashtable] $Params = @{
                name = $GroupName
                # policy_id = ''
            }  
    
            Try{
            
                $response = Invoke-RestMethod -Method Post `
                                              -Uri $RequestUrl `
                                              -Headers $script:TVConfig.Header `
                                              -Body ( $Params | ConvertTo-Json ) `
                                              -ContentType 'application/json' `
                                              -ErrorAction SilentlyContinue -ErrorVariable respError     
                
                Get-TVGroup -Name $response.name -Token $Token
            
            }
            catch{

            }
        }

    }

}

function Get-TVGroup
{
    [CmdletBinding()]
    [OutputType([TVGroup])]
    param(
        [Parameter()]
        [string] $Token = $script:TVConfig.AccessToken,

        [Parameter()]
        [string] $Name,

        [Parameter()]
        [bool] $Shared, 

        [Parameter(Mandatory = $false)]
        [string] $CompanyUserID = [string]::Empty

    )

    begin
    {
        # Make sure to have a token
        if ( [string]::IsNullOrEmpty($Token) ) {
            throw (New-Object -TypeName System.Exception -ArgumentList $script:TOKEN_MISSING_ERROR)
        }

        [string] $AdminParam = @{$true=('/users/{0}' -f $CompanyUserID);$false=[string]::Empty}[(!( [string]::IsNullOrEmpty($CompanyUserID)))]
        [string] $RequestUrl = ('{0}/api/{1}{2}/groups' -f $script:TVConfig.BaseUrl, $script:TVConfig.ApiVersion, $AdminParam)  
        
        Write-Verbose -Message ('Processing RequestURL: {0}' -f $RequestUrl)
        
    }

    process
    {
        [hashtable] $Params = @{}
        if ( -not ( [string]::IsNullOrEmpty($Name)))
        {
            $Params.name = $Name
        }
        if ( $PSBoundParameters.ContainsKey( "Shared" ) ) 
        {
            $Params.shared = $Shared
        }
        # Fetch the groups from the API
        $Response = Invoke-RestMethod -Uri $RequestUrl -Headers $script:TVConfig.Header -Body $Params -ErrorVariable RestError -ErrorAction SilentlyContinue
        
        $RestError

        # Iterate over the groups
        $Response.groups | ForEach-Object {                
            $CurrentGroup = $_
            [TVGroupUser[]] $SharedWith = @()
            #Write-Verbose -Message ('not Shared?: "{0}".' -f [string]::IsNullOrEmpty($CurrentGroup.shared_with))
            # Make sure that the group is shared first
            if ( -not ( [string]::IsNullOrEmpty($CurrentGroup.shared_with)) ) 
            {
                $CurrentGroup.shared_with |  ForEach-Object {
                    [TVGroupUser] $CurrentUser = New-Object -TypeName TVGroupUser -ArgumentList $_.userid, $_.name, $_.permissions, $_.pending
                    $SharedWith += $CurrentUser 
                }
               
            }

            [TVUserBase] $Owner = $null
            if ($CurrentGroup.owner -ne $null)
            {
                $Owner = New-Object -TypeName TVUserBase -ArgumentList $CurrentGroup.owner.userid, $CurrentGroup.owner.name
            }

            [TVGroup] $Group = New-Object -TypeName TVGroup -ArgumentList $CurrentGroup.ID, $CurrentGroup.Name, $Owner, $CurrentGroup.permissions, $SharedWith
            Write-Output -InputObject $Group 
        }
    }
    end {}
}

#endregion Groups

#region Local
function Get-TVInstallDir
{
    <#
    Get-Package -ProviderName Programs | Where-Object {$_.Name -like 'TeamViewer*'}
    #>

    [CmdletBinding()]
    param()

    $ProgramFilesX86 = @{$true=${env:ProgramFiles(x86)};$false=$env:ProgramFiles}[(${env:ProgramFiles(x86)} -ne $null)]
    
    "$ProgramFilesX86\TeamViewer\TeamViewer.exe" | ForEach-Object {
        if ( Test-Path -Path $_ ) 
        {
            return $_
        }
        else
        {
            return $null
        }
    }
}
function Start-TVRemoteControl
{
    [CmdletBinding()]
    param(

        [Parameter(
            Mandatory = $true
        )]
        [string] $RemoteControlID
    )

    $InstallDir = Get-TVInstallDir

    if ( $InstallDir -ne $null )
    {

        Start-Process -FilePath $InstallDir -ArgumentList ('-i {0}' -f $RemoteControlID)
        #Invoke-WebRequest -Uri ('https://start.teamviewer.com/device/{0}' -f $RemoteControlID)

    }

}

#endregion