NinjaRmmApi.psm1
<#
This file is part of the NinjaRmmApi module. This module is not affiliated with, endorsed by, or related to NinjaRMM, LLC. NinjaRmmApi is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. NinjaRmmApi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with NinjaRmmApi. If not, see <https://www.gnu.org/licenses/>. #> Function Set-NinjaRmmSecrets { [OutputType('void')] Param( [AllowNull()] [String] $AccessKeyId, [AllowNull()] [String] $SecretAccessKey ) $env:NinjaRmmAccessKeyID = $AccessKeyId $env:NinjaRmmSecretAccessKey = $SecretAccessKey } Function Reset-NinjaRmmSecrets { [Alias('Remove-NinjaRmmSecrets')] [OutputType('void')] Param() Remove-Variable -Name $env:NinjaRmmAccessKeyID Remove-Variable -Name $env:NinjaRmmSecretAccessKey } Function Set-NinjaRmmServerLocation { [OutputType('void')] Param( [ValidateSet('US', 'EU')] [String] $Location = 'US' ) $env:NinjaRmmServerLocation = $Location } Function Send-NinjaRmmApi { [CmdletBinding()] Param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [String] $RequestToSend, [ValidateSet('GET', 'PUT', 'POST', 'DELETE')] [String] $Method = 'GET' ) # Stop if our secrets have not been learned. If ($null -eq $env:NinjaRmmSecretAccessKey) { Throw [Data.NoNullAllowedException]::new('No secret access key has been provided. Please run Set-NinjaRmmSecrets.') } If ($null -eq $env:NinjaRmmAccessKeyID) { Throw [Data.NoNullAllowedException]::new('No access key ID has been provided. Please run Set-NinjaRmmSecrets.') } # Get the current date. Calling -Format converts it to a [String], so we # need two separate calls to Get-Date. $DateString = Get-Date -Format 'R' -Date ((Get-Date).ToUniversalTime()) # Format our signing string correctly. # NinjaRMM's signature has a place to put Content-MD5 and Content-Type # values, but leaving them out ($null) seems to be perfectly acceptable. $ContentMD5 = $null $ContentType = $null $StringToSign = @($Method, $ContentMD5, $ContentType, $DateString, $RequestToSend) -Join "`n" # Convert your string to a byte array, and then Base64-encode it. # Not sure why we're removing newlines, but Ninja's example C# code did it. $StringToSignBytes = [Text.Encoding]::UTF8.GetBytes($StringToSign) $EncodedString = ([Convert]::ToBase64String($StringToSignBytes)).Trim() # Construct our HMAC-SHA1 thing, then convert *it* to Base64 as well. # Sample: https://stackoverflow.com/questions/42150420/why-does-encrypting-hmac-sha1-in-exactly-the-same-code-in-c-sharp-and-powershell $Hasher = [Security.Cryptography.KeyedHashAlgorithm]::Create('HMACSHA1') $Hasher.Key = [Text.Encoding]::UTF8.GetBytes($env:NinjaRmmSecretAccessKey) $HashedStringBytes= $Hasher.ComputeHash([Text.Encoding]::UTF8.GetBytes($EncodedString)) # Convert the result to a Base64 string. $Signature = [Convert]::ToBase64String($HashedStringBytes) -Replace "`n","" # Pick our server. By default, we will use the United States server. # However, the European Union server can be used instead.' If (($env:NinjaRmmServerLocation -eq 'US') -or ($null -eq $env:NinjaRmmServerLocation)) { $HostName = 'api.ninjarmm.com' } ElseIf ($env:NinjaRmmServerLocation -eq 'EU') { $HostName = 'eu-api.ninjarmm.com' } Else { Throw [ArgumentException]::new("The server location ${env:NinjaRmmServerLocation} is not valid. Please run Set-NinjaRmmServerLocation.") } # Create a user agent for logging purposes. $UserAgent = "PowerShell/$($PSVersionTable.PSVersion) " $UserAgent += "NinjaRmmApi/$((Get-Module -Name 'NinjaRmmApi').Version) " $UserAgent += '(implementing API version 0.1.2)' # Ensure that TLS 1.2 is enabled, so that we can communicate with NinjaRMM. # It may be disabled by default before PowerShell 6. [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 # Some new versions of PowerShell also support TLS 1.3. If that is a valid # option, then enable that, too, in case NinjaRMM ever enables it. If ([Net.SecurityProtocolType].GetMembers() -Contains 'Tls13') { [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls13 } # Finally, send it. Write-Debug -Message ("Will send the request:`n`n" ` + "$Method $RequestToSend HTTP/1.1`n" ` + "Host: $HostName`n" ` + "Authorization: NJ ${env:NinjaRmmAccessKeyID}:$Signature`n" ` + "Date: $DateString`n" ` + "User-Agent: $UserAgent") $Arguments = @{ 'Method' = $Method 'Uri' = "https://$HostName$RequestToSend" 'Headers' = @{ 'Authorization' = "NJ ${env:NinjaRmmAccessKeyID}:$Signature" 'Date' = $DateString 'Host' = $HostName 'User-Agent' = $UserAgent } } Return (Invoke-RestMethod @Arguments) } Function Get-NinjaRmmAlerts { [CmdletBinding(DefaultParameterSetName='AllAlerts')] Param( [Parameter(ParameterSetName='OneAlert')] [UInt32] $AlertId, [Parameter(ParameterSetName='AllAlertsSince')] [UInt32] $Since ) $Request = '/v1/alerts' If ($PSCmdlet.ParameterSetName -eq 'OneAlert') { $Request += "/$AlertId" } ElseIf ($PSCmdlet.ParameterSetName -eq 'AllAlertsSince') { $Request += "/since/$Since" } Return (Send-NinjaRmmApi -RequestToSend $Request) } Function Reset-NinjaRmmAlert { [CmdletBinding()] [Alias('Remove-NinjaRmmAlert')] Param( [Parameter(Mandatory)] [UInt32] $AlertId ) Return (Send-NinjaRmmApi -Method 'DELETE' -RequestToSend "/v1/alerts/$AlertId") } Function Get-NinjaRmmCustomers { [CmdletBinding(DefaultParameterSetName='AllCustomers')] Param( [Parameter(ParameterSetName='OneCustomer')] [UInt32] $CustomerId ) $Request = '/v1/customers' If ($PSCmdlet.ParameterSetName -eq 'OneCustomer') { $Request += "/$CustomerId" } Return (Send-NinjaRmmApi -RequestToSend $Request) } Function Get-NinjaRmmDevices { [CmdletBinding(DefaultParameterSetName='AllDevices')] Param( [Parameter(ParameterSetName='OneDevice')] [UInt32] $DeviceId ) $Request = '/v1/devices' If ($PSCmdlet.ParameterSetName -eq 'OneDevice') { $Request += "/$DeviceId" } Return (Send-NinjaRmmApi -RequestToSend $Request) } |