HM-Monitoring.psm1

#######################
### Basic Functions ###
#######################

#region Write-Separator
<#
.Description
writes separator
#>

function Write-Separator {
    Write-Host ""
    Write-Host "----------------------------------------------------------"
}
Export-ModuleMember -Function Write-Separator
#endregion

#region Write-End_Of_Script
<#
.Description
writes separator at the end of script
#>

function Write-End_Of_Script {
    Write-Host ""
    Write-Host "--------------------end-of-output-------------------------"
}
Export-ModuleMember -Function Write-End_Of_Script
#endregion

#region Exit-Error
<#
    .Description
    Increases the Count in $ErrorCount and does an Exit
#>

function Exit-Error {
    $Script:ErrorCount++
    Exit 1001
}
Export-ModuleMember -Function Exit-Error
#endregion

#region Test-FileLock
<#
.Description
Check if a file is in use
#>

function Test-FileLock {
    param (
        [parameter(Mandatory=$true)][string]$Path
    )

    $oFile = New-Object System.IO.FileInfo $Path
    if ((Test-Path -Path $Path) -eq $false) {
        return $false
    }
    try {
        $oStream = $oFile.Open([System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)

        if ($oStream) {
            $oStream.Close()
        }
        $false
    }
    catch {
        # file is locked by a process.
        return $true
    }
}
Export-ModuleMember -Function Test-FileLock 
#endregion

#region Test-SNMP_Value_If_Exists
<#
.SYNOPSIS
Checks if the value provided by the snmp deamon is a valid value
 
.DESCRIPTION
Checks if the value provided by the snmp deamon is a valid value
 
.PARAMETER SNMPValue
The Output of an SNMP request
 
.EXAMPLE
Test-SNMP_Value_If_Exists -SNMPValue $AllSNMPDATA.Key
 
.NOTES
 
#>

function Test-SNMP_Value_If_Exists {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false)]
        $SNMPValue
    )

    if (
        $SNMPValue -notlike "*NoSuchInstance*" -and `
        $SNMPValue -notlike "*NoSuchObject*" -and `
        $SNMPValue -ne "" -and `
        $null -ne $SNMPValue)
    { 
        return $true
    }
    else { 
        return $false
    }
}
Export-ModuleMember -Function Test-SNMP_Value_If_Exists 
#endregion

#region Convert-Version_To_Accumulated
<#
    .Description
    Compares to versions insertet as an string array
 
    .example
    $FoundVersionArray = "21.2.16.590".Split(".")
    $MinVerionArray = "11.5.22.980".Split(".")
    Convert-Version_To_Accumulated -VersionArray $FoundVersionArray -lt Convert_Version_To_Accumulated -VersionArray $MinVerionArray
#>

function Convert-Version_To_Accumulated {
    [CmdletBinding()]
    param (
        # VersionArray
        [Parameter(Mandatory = $true)]
        $VersionArray
    )

    [int64]$Version = ([int64]$VersionArray[3] * 1 + [int64]$VersionArray[2] * 10000 + [int64]$VersionArray[1] * 100000000 + [int64]$VersionArray[0] * 1000000000000)

    return [int64]$Version
}
Export-ModuleMember -Function Convert-Version_To_Accumulated
#endregion

#region Convert-Date_To_German
<#
    .Description
    Converts to german time format
#>

function Convert-Date_To_German {
    [CmdletBinding()]
    param (
        [System.DateTime]
        $DateObj
    )    
    return (Get-Date $Dateobj -Format "dd.MM.yyyy HH:mm:ss")
}
Export-ModuleMember -Function Convert-Date_To_German
#endregion

#region Test-SNMP_Module_Installed
<#
.SYNOPSIS
Checks if the SNMP Module is installed
 
.DESCRIPTION
Checks if the SNMP Module is installed
 
.EXAMPLE
Test-SNMP_Module_Installed
 
.NOTES
No Parameters
#>

function Test-SNMP_Module_Installed {
    if ([environment]::OSVersion.Version.Major -gt 8) {
        if ($null -eq (Get-Module -ListAvailable -Name Snmp)) {
            Write-Host "PowerShell Module SNMP is not installed!"
            Write-Host "To install this Module use 'Install-Module -Name SNMP -Force' on systems with PowerShell Version 5.0 or greater"
            Write-Host "otherwise please copy the module from another device into the folder 'C:\Program Files\WindowsPowerShell\Modules'"
            Exit-Error
        }
    }
    else {
        $PSModulePath1 = "C:\Program Files\WindowsPowerShell\Modules"
        $PSModulePath2 = "C:\Windows\System32\WindowsPowerShell\v1.0\Modules"
    
        if (Test-Path -path "$PSModulePath1\SNMP\*\SNMP.psm1") {
            try {
                Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Force -Scope Process
                Import-Module "$PSModulePath1\SNMP\1.0.0.1\SNMP.psm1"
                Add-Type -Path "$PSModulePath1\SNMP\1.0.0.1\SharpSnmpLib.dll"
            }
            catch { 
                Write-Host "(Error - SNMP-Module): $($PSItem.Exception.Message)"
                Exit-Error
            }
        }
        elseif (Test-Path -path "$PSModulePath2\SNMP\*\SNMP.psm1") {
            try {
                Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Force -Scope Process
                Import-Module "$PSModulePath2\SNMP\1.0.0.1\SNMP.psm1"
                Add-Type -Path "$PSModulePath2\SNMP\1.0.0.1\SharpSnmpLib.dll"
            }
            catch { 
                Write-Host "(Error - SNMP-Module): $($PSItem.Exception.Message)"
                Exit-Error
            }
        }
        else { 
            Write-Host "PowerShell Module SNMP is not installed!"
            Write-Host "To install this Module use 'Install-Module -Name SNMP -Force' on systems with PowerShell Version 5.0 or greater"
            Write-Host "otherwise please copy the module from another device into the folder 'C:\Program Files\WindowsPowerShell\Modules'"
            Exit-Error
        }
    }
}
Export-ModuleMember -Function Test-SNMP_Module_Installed
#endregion

############################
### N-Able RMM Functions ###
############################

#region Get-N_Able_RMM_Data
<#
.DESCRIPTION
Pulls N-Able RMM API Data
 
.PARAMETER Hostname
Hostname of the Dashboard, for example: wwwgermany1.systemmonitor.eu.com
 
.PARAMETER ApiKey
APIKey of the Dashboard, for example: d48a36b88c8ffhthtfdhjtzj5d653881
                                     
.PARAMETER Service
Service you want do use, for example: list_clients
 
.PARAMETER OverwriteProxyAddress
Specify this to force a specific Proxy "<ProxyIP:ProxyPort>"
 
.PARAMETER OverwriteProxyUsername
Define a Username to Authenticate to Proxy
 
.PARAMETER OverwriteProxyUserPwd
Define the password
 
.EXAMPLE
Get-N_Able_RMM_Data -Hostname $Hostname -ApiKey $ApiKey -Service "list_clients"
 
.NOTES
https://documentation.n-able.com/remote-management/userguide/Content/data_extraction_api.htm
#>

function Get-N_Able_RMM_Data {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $Hostname,

        [Parameter(Mandatory = $true)]
        [string]
        $ApiKey,

        [Parameter(Mandatory = $true)]
        [string]
        $Service,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyAddress,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyUsername,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyUserPwd
    )

    [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
    $Endpoint = "https://$($Hostname)/api/?apikey=$($ApiKey)&service=$($Service)"

    #region Proxy Definition
    try {
        #region RMM Agent Settings
        $WinAgent_Path = (Get-WmiObject -Class Win32_Service | Where-Object -FilterScript { $PSItem.Name -EQ 'Advanced Monitoring Agent'}).PathName.Replace('"','')
        $WinAgent_BasePath = $WinAgent_Path.Replace("\winagent.exe","")
        $AgentSettings = Get-Content -Path "$($WinAgent_BasePath)\settings.ini"
        if ($AgentSettings -ne "" -and $null -ne $AgentSettings) {
            $FoundHost = ($AgentSettings | Select-String -Pattern 'HOST=')
            if ($null -ne ($AgentSettings | Select-String -Pattern 'HOST=') -and $FoundHost -like "HOST=*") {
                $ProxyIP = ($AgentSettings | Select-String -Pattern 'HOST=')[0].ToString().Replace("HOST=","")
                $ProxyPort = ($AgentSettings | Select-String -Pattern 'PORT=')[0].ToString().Replace("PORT=","")

                if ($OverwriteProxyUsername -eq "" -or $null -eq $OverwriteProxyUsername) {
                    $ProxyUsername = ($AgentSettings | Select-String -Pattern 'USERNAME=')[1].ToString().Replace("USERNAME=","")
                }
                $ProxyAddress = "$($ProxyIP):$($ProxyPort)"
            }
        }
        #endregion

        #region proxy overwrite
        if ($OverwriteProxyAddress -ne "" -and $null -ne $OverwriteProxyAddress) {
            $ProxyAddress = $OverwriteProxyAddress
        }

        if ($OverwriteProxyUsername -ne "" -and $null -ne $OverwriteProxyUsername) {
            $ProxyUsername = $OverwriteProxyUsername
        }

        if ($OverwriteProxyUserPwd -eq "" -or $null -eq $OverwriteProxyUserPwd) {
            $ProxyUserPassword = "password"
        }
        elseif ($OverwriteProxyUserPwd -ne "" -and $null -ne $OverwriteProxyUserPwd) {
            $ProxyUserPassword = $OverwriteProxyUserPwd
        }

        if ($ProxyUsername -ne "" -and $null -ne $ProxyUsername -and $ProxyUserPassword -ne "" -and $null -ne $ProxyUserPassword) {
            $ProxyPassword = ConvertTo-SecureString $ProxyUserPassword -AsPlainText -Force
            $ProxyCred = New-Object System.Management.Automation.PSCredential ($ProxyUsername, $ProxyPassword)
        }
        #endregion
    }
    catch { 
        Write-Host "$($PSItem.Exception.Message)"
        Write-End_Of_Script
        Exit-Error
    }
    #endregion

    if ($ProxyAddress -ne "" -and $null -ne $ProxyAddress) {
        if ($ProxyCred -ne "" -and $null -ne $ProxyCred) {
            try {
                [xml]$N_Able_RMM_Data = (Invoke-RestMethod -Uri $Endpoint -Method Post -Proxy "http://$($ProxyAddress)" -ProxyCredential $ProxyCred).InnerXml
            }
            catch {
                Write-Host "Error connecting to n-able rmm servers"
                Write-Host "Error Message: $($PSItem.Exception.Message)"
                Write-End_Of_Script
                Exit-Error
            }
        }
        else {
            try {
                [system.net.webrequest]::defaultwebproxy = new-object system.net.webproxy($ProxyAddress)
                [system.net.webrequest]::defaultwebproxy.credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
                [system.net.webrequest]::defaultwebproxy.BypassProxyOnLocal = $true        
                [xml]$N_Able_RMM_Data = (Invoke-RestMethod -Uri $Endpoint -Method Post).InnerXml
            }
            catch {
                Write-Host "Error connecting to n-able rmm servers"
                Write-Host "Error Message: $($PSItem.Exception.Message)"
                Write-End_Of_Script
                Exit-Error
            }
        }
    }
    else {
        try {
            [xml]$N_Able_RMM_Data = (Invoke-RestMethod -Uri $Endpoint -Method Post).InnerXml
        }
        catch {
            Write-Host "Error connecting to n-able rmm servers"
            Write-Host "Error Message: $($PSItem.Exception.Message)"
            Write-End_Of_Script
            Exit-Error
        }
    }
    return $N_Able_RMM_Data
}
Export-ModuleMember -Function Get-N_Able_RMM_Data
#endregion

#region Get-N_Able_RMM_Clients
<#
.DESCRIPTION
Pulls N-Able RMM Clients
 
.PARAMETER Hostname
Hostname of the Dashboard, for example: wwwgermany1.systemmonitor.eu.com
 
.PARAMETER ApiKey
APIKey of the Dashboard, for example: d48a36b88c8ffhthtfdhjtzj5d653881
 
.EXAMPLE
Get-N_Able_RMM_Clients -Hostname $Hostname -ApiKey $ApiKey -Service "list_clients"
 
.NOTES
https://documentation.n-able.com/remote-management/userguide/Content/listing_clients_.htm
#>

function Get-N_Able_RMM_Clients {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]
        $Hostname,

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

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyAddress,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyUsername,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyUserPwd
    )

    try {
        $N_Able_RMM_Data = Get-N_Able_RMM_Data -Hostname $Hostname -ApiKey $ApiKey -Service "list_clients" -OverwriteProxyAddress $OverwriteProxyAddress -OverwriteProxyUsername $OverwriteProxyUsername -OverwriteProxyUserPwd $OverwriteProxyUserPwd
        return ($N_Able_RMM_Data.SelectNodes("//client"))
    }
    catch {
        Write-Error $PSItem.Exception.Message
    }
}
Export-ModuleMember -Function Get-N_Able_RMM_Clients
#endregion

#region Get-N_Able_RMM_Checks
<#
.DESCRIPTION
Pulls N-Able RMM Device Checks
 
.PARAMETER Hostname
Hostname of the Dashboard, for example: wwwgermany1.systemmonitor.eu.com
 
.PARAMETER ApiKey
APIKey of the Dashboard, for example: d48a36b88c8ffhthtfdhjtzj5d653881
 
.PARAMETER DeviceID
DeviceID of an Device, for example: 1519987
 
.EXAMPLE
Get-N_Able_RMM_Checks -Hostname $Hostname -ApiKey $ApiKey -DeviceID $DeviceID
 
.NOTES
https://documentation.n-able.com/remote-management/userguide/Content/listing_checks_.htm
#>

function Get-N_Able_RMM_Checks {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]
        $Hostname,

        [Parameter(Mandatory)]
        [string]
        $ApiKey,
        
        [Parameter(Mandatory)]
        [string]
        $DeviceID,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyAddress,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyUsername,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyUserPwd
    )
    try {
        $Checks = (Get-N_Able_RMM_Data -Hostname $Hostname -ApiKey $ApiKey -Service "list_checks&deviceid=$($DeviceID)" -OverwriteProxyAddress $OverwriteProxyAddress -OverwriteProxyUsername $OverwriteProxyUsername -OverwriteProxyUserPwd $OverwriteProxyUserPwd).result.items.check
        return $Checks
    }
    catch {
        Write-Error $PSItem.Exception.Message
    }
}
Export-ModuleMember -Function Get-N_Able_RMM_Checks
#endregion

#region Get-N_Able_RMM_Client_Devices
<#
.DESCRIPTION
Pulls N-Able RMM Client Devices
 
.PARAMETER Hostname
Hostname of the Dashboard, for example: wwwgermany1.systemmonitor.eu.com
 
.PARAMETER ApiKey
APIKey of the Dashboard, for example: d48a36b88c8ffhthtfdhjtzj5d653881
 
.PARAMETER ClientID
ClientID of an Client, for example: 134615
 
.PARAMETER DeviceType
DeviceType of the Device, can only be 'server', 'workstation', 'mobile_device'
 
.EXAMPLE
Get-N_Able_RMM_Client_Devices -Hostname $Hostname -ApiKey $ApiKey -ClientID $ClientID -DeviceType server
 
.EXAMPLE
Get-N_Able_RMM_Client_Devices -Hostname $Hostname -ApiKey $ApiKey -ClientID $ClientID -DeviceType workstation
 
.NOTES
https://documentation.n-able.com/remote-management/userguide/Content/listing_devices_at_client_.htm
#>

function Get-N_Able_RMM_Client_Devices {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]
        $Hostname,

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

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

        [Parameter(Mandatory)]
        [ValidateSet("server","workstation","mobile_device")]
        [string]
        $DeviceType,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyAddress,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyUsername,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyUserPwd
    )

    try {
        if ($DeviceType -ceq "Server" -or $DeviceType -ceq "Workstation") {
            throw "Parameter 'DeviceType' is case sensitiv"
        }
        $N_Able_RMM_Data = Get-N_Able_RMM_Data -Hostname $Hostname -ApiKey $ApiKey -Service "list_devices_at_client&clientid=$($ClientID)&devicetype=$($DeviceType)" -OverwriteProxyAddress $OverwriteProxyAddress -OverwriteProxyUsername $OverwriteProxyUsername -OverwriteProxyUserPwd $OverwriteProxyUserPwd
        if ("" -ne $N_Able_RMM_Data.SelectNodes("//workstation")) {
            return ($N_Able_RMM_Data.SelectNodes("//workstation"))
        }
        elseif ("" -ne $N_Able_RMM_Data.SelectNodes("//server")) {
            return ($N_Able_RMM_Data.SelectNodes("//server"))
        }
    }
    catch {
        Write-Error $PSItem.Exception.Message
    }
}
Export-ModuleMember -Function Get-N_Able_RMM_Client_Devices
#endregion

#region Get-N_Able_RMM_Templates
<#
.DESCRIPTION
Pulls N-Able RMM Templates
 
.PARAMETER Hostname
Hostname of the Dashboard, for example: wwwgermany1.systemmonitor.eu.com
 
.PARAMETER ApiKey
APIKey of the Dashboard, for example: d48a36b88c8ffhthtfdhjtzj5d653881
 
.PARAMETER DeviceType
DeviceType of the Device, can only be 'server', 'workstation'
 
.EXAMPLE
Get-N_Able_RMM_Templates -Hostname $Hostname -ApiKey $ApiKey -DeviceType server
 
.NOTES
https://documentation.n-able.com/remote-management/userguide/Content/listing_checks_.htm
#>

function Get-N_Able_RMM_Templates{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]
        $Hostname,

        [Parameter(Mandatory)]
        [string]
        $ApiKey,
        
        [Parameter(Mandatory)]
        [ValidateSet("server","workstation")]
        [string]
        $DeviceType,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyAddress,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyUsername,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyUserPwd
    )
    try {
        if ($DeviceType -ceq "Server" -or $DeviceType -ceq "Workstation") {
            throw "Parameter 'DeviceType' is case sensitiv"
        }
        else {
            $Templates = (Get-N_Able_RMM_Data -Hostname $Hostname -ApiKey $ApiKey -Service "list_templates&devicetype=$($DeviceType)" -OverwriteProxyAddress $OverwriteProxyAddress -OverwriteProxyUsername $OverwriteProxyUsername -OverwriteProxyUserPwd $OverwriteProxyUserPwd).result.items.installation_template
            return $Templates | Select-Object templateid,@{label="name"; expression={$PSItem.name.'#cdata-section'}}
        }
    }
    catch {
        Write-Error $PSItem.Exception.Message
    }
}
Export-ModuleMember -Function Get-N_Able_RMM_Templates
#endregion

#region Get-N_Able_RMM_Device_Monitoring_Details
<#
.DESCRIPTION
Lists all monitoring information for the device (server or workstation) identified by the deviceid.
 
.PARAMETER Hostname
Hostname of the Dashboard, for example: wwwgermany1.systemmonitor.eu.com
 
.PARAMETER ApiKey
APIKey of the Dashboard, for example: d48a36b88c8ffhthtfdhjtzj5d653881
 
.PARAMETER DeviceID
DeviceID of an Device, for example: 1519987
 
.EXAMPLE
Get-N_Able_RMM_Device_Monitoring_Details -Hostname $Hostname -ApiKey $ApiKey -DeviceID $DeviceID
 
.NOTES
https://documentation.n-able.com/remote-management/userguide/Content/listing_device_monitoring_deta.htm
#>

function Get-N_Able_RMM_Device_Monitoring_Details {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]
        $Hostname,

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

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

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyAddress,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyUsername,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyUserPwd
    )

    try {
        $N_Able_RMM_Data = Get-N_Able_RMM_Data -Hostname $Hostname -ApiKey $ApiKey -Service "list_device_monitoring_details&deviceid=$($DeviceID)" `
                            -OverwriteProxyAddress $OverwriteProxyAddress `
                            -OverwriteProxyUsername $OverwriteProxyUsername `
                            -OverwriteProxyUserPwd $OverwriteProxyUserPwd
        if ("" -ne $N_Able_RMM_Data.SelectNodes("//workstation")) {
            return ($N_Able_RMM_Data.SelectNodes("//workstation"))
        }
        elseif ("" -ne $N_Able_RMM_Data.SelectNodes("//server")) {
            return ($N_Able_RMM_Data.SelectNodes("//server"))
        }
    }
    catch {
        Write-Error $PSItem.Exception.Message
    }
}
Export-ModuleMember -Function Get-N_Able_RMM_Device_Monitoring_Details
#endregion

#region Get-N_Able_RMM_Device_Patches
<#
.DESCRIPTION
Pulls N-Able RMM Device Patch Status
 
.PARAMETER Hostname
Hostname of the Dashboard, for example: wwwgermany1.systemmonitor.eu.com
 
.PARAMETER ApiKey
APIKey of the Dashboard, for example: d48a36b88c8ffhthtfdhjtzj5d653881
 
.PARAMETER DeviceID
DeviceID of an Device, for example: 1519987
 
.EXAMPLE
Get-N_Able_RMM_Device_Patches -Hostname $Hostname -ApiKey $ApiKey -DeviceID $DeviceID
 
.NOTES
https://documentation.n-able.com/remote-management/userguide/Content/list_all_patches_for_device.htm
#>

function Get-N_Able_RMM_Device_Patches {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]
        $Hostname,

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

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

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyAddress,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyUsername,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyUserPwd
    )
    try {
        $N_Able_RMM_Data = Get-N_Able_RMM_Data -Hostname $Hostname -ApiKey $ApiKey -Service "patch_list_all&deviceid=$($DeviceID)" -OverwriteProxyAddress $OverwriteProxyAddress -OverwriteProxyUsername $OverwriteProxyUsername -OverwriteProxyUserPwd $OverwriteProxyUserPwd
        return ($N_Able_RMM_Data.SelectNodes("//patch"))
    }
    catch {
        Write-Error $PSItem.Exception.Message
    }
}
Export-ModuleMember -Function Get-N_Able_RMM_Device_Patches
#endregion

#region Get-N_Able_RMM_Device_AssetDetails
<#
.DESCRIPTION
Pulls N-Able RMM Device Asset Details, like installed hardware or software
 
.PARAMETER Hostname
Hostname of the Dashboard, for example: wwwgermany1.systemmonitor.eu.com
 
.PARAMETER ApiKey
APIKey of the Dashboard, for example: d48a36b88c8ffhthtfdhjtzj5d653881
 
.PARAMETER DeviceID
DeviceID of an Device, for example: 1519987
 
.PARAMETER AssetType
AssetType of an Device, for example: software or hardware
 
.EXAMPLE
Get-N_Able_RMM_Device_AssetDetails -Hostname $Hostname -ApiKey $ApiKey -DeviceID $DeviceID -AssetType software
 
.NOTES
https://documentation.n-able.com/remote-management/userguide/Content/listing_device_asset_details.htm
#>

function Get-N_Able_RMM_Device_AssetDetails {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]
        $Hostname,

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

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

        [Parameter(Mandatory)]
        [ValidateSet("hardware","software")]
        [string]
        $AssetType,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyAddress,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyUsername,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyUserPwd
    )
    try {
        $N_Able_RMM_Data = Get-N_Able_RMM_Data -Hostname $Hostname -ApiKey $ApiKey -Service "list_device_asset_details&deviceid=$($DeviceID)" -OverwriteProxyAddress $OverwriteProxyAddress -OverwriteProxyUsername $OverwriteProxyUsername -OverwriteProxyUserPwd $OverwriteProxyUserPwd

        switch ($AssetType) {
            "hardware" { return ($N_Able_RMM_Data.result.hardware.item) }
            "software" { return ($N_Able_RMM_Data.result.software.item) }
            Default { throw "asset type not defined" }
        }        
    }
    catch {
        Write-Error $PSItem.Exception.Message
    }
}
Export-ModuleMember -Function Get-N_Able_RMM_Device_AssetDetails
#endregion

#region Get-N_Able_RMM_Devices_With_Software
<#
.DESCRIPTION
Pulls N-Able RMM with the specified Software
 
.PARAMETER Hostname
Hostname of the Dashboard, for example: wwwgermany1.systemmonitor.eu.com
 
.PARAMETER ApiKey
APIKey of the Dashboard, for example: d48a36b88c8ffhthtfdhjtzj5d653881
 
.PARAMETER SearchPattern
Type a string which should be found at the installed software, for example: veeam
 
.PARAMETER DeviceType
DeviceType of the Device, can only be 'server', 'workstation'
 
.EXAMPLE
Get-N_Able_RMM_Devices_With_Software -Hostname $Hostname -ApiKey $ApiKey -SearchPattern 'Veeam' -DeviceType server
 
.NOTES
https://documentation.n-able.com/remote-management/userguide/Content/data_extraction_api.htm
#>

function Get-N_Able_RMM_Devices_With_Software {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]
        $Hostname,

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

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

        [Parameter(Mandatory)]
        [ValidateSet("workstation","server")]
        [string]
        $DeviceType,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyAddress,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyUsername,

        [Parameter(Mandatory = $false)]
        [string]
        $OverwriteProxyUserPwd
    )

    try {
        $All_Data = @{}
        $All_Clients = Get-N_Able_RMM_Clients -Hostname $Hostname -ApiKey $ApiKey

        foreach ($Client in $All_Clients) {
            [System.Collections.Arraylist]$DevicesFound = @()

            Write-Verbose -Message "Getting Client Devices"
            $DevicesToCheck = Get-N_Able_RMM_Client_Devices -Hostname $Hostname -ApiKey $ApiKey -ClientID $Client.clientid -DeviceType $DeviceType  -OverwriteProxyAddress $OverwriteProxyAddress -OverwriteProxyUsername $OverwriteProxyUsername -OverwriteProxyUserPwd $OverwriteProxyUserPwd
                    
            foreach($Device in $DevicesToCheck) {
                $TempVar = (Get-N_Able_RMM_Device_AssetDetails -Hostname $Hostname -ApiKey $ApiKey -DeviceID $Device.ID -AssetType software | Where-Object -FilterScript { $PSItem.name.'#cdata-section' -like "*$($SearchPattern)*" }).name.'#cdata-section'
                if ($TempVar) {
                    $DevicesFound.Add("$($Device.Name.'#cdata-section')") | Out-Null
                }
            }

            if ($null -ne $DevicesFound -and $DevicesFound -ne "") {
                Write-Verbose -Message "Adding '$($Client.name.'#cdata-section')' to All_Data Object"
                $All_Data.Add($($Client.name.'#cdata-section'),$DevicesFound)
            }
        }
        
        return $All_Data
    }
    catch {
        Write-Error $PSItem.Exception.Message
    }
}
Export-ModuleMember -Function Get-N_Able_RMM_Devices_With_Software
#endregion