functions/Invoke-lmApi.ps1
<#
.SYNOPSIS Constructs headers and invokes the LM API as documented .EXAMPLE An example $Key = Read-Host -AsSecureString <Type your access key> Invoke-LMApi -Resource device/devices -Query @{size=100} -AccessId 'xpY57I5qB82YE9T79E3q' -AccessKey $Key -Company TDS .EXAMPLE Using file for storing key - will only run if everything is run as the same windows user. $Key = Read-Host -AsSecureString | Export-CLIxml -Path .\lmKey.xml <Type your access key> #Later, in a script $Key = Import-CliXml -Path .\lmKey.xml Invoke-LMApi -Resource device/devices -Query @{size=100} -AccessId 'xpY57I5qB82YE9T79E3q' -AccessKey $Key -Company TDS .NOTES If you use "Connect-lmAPI" first, other calls to this command will not need the connection info (company, accesskey and accessid) All calls after the first in a script can forgo that information as well. #> function Invoke-lmAPI { [CmdletBinding(DefaultParameterSetName = 'Normal')] Param ( # The resource path to connect to [Parameter(Mandatory = $true, Position = 0, ParameterSetName = 'Normal')] [Parameter(Mandatory = $true, Position = 0, ParameterSetName = 'Connect')] [String] $Resource, # The HTTP verb to use for this request [Parameter(ParameterSetName = 'Normal')] [Parameter(ParameterSetName = 'Connect')] [Alias("Verb")] [Microsoft.PowerShell.Commands.WebRequestMethod] $HttpVerb = "Get", #AccessId for this API [Parameter(Mandatory = $true, ParameterSetName = 'Connect')] [String] $AccessId, #AccessKey for this API [Parameter(Mandatory = $true, ParameterSetName = 'Connect')] [SecureString] $AccessKey, #Company for this API [Parameter(Mandatory = $true, ParameterSetName = 'Connect')] [String] $Company, #Dictionary to be constructed into a QueryString [Parameter(ParameterSetName = 'Normal')] [Parameter(ParameterSetName = 'Connect')] [hashtable] $Query, #Body of the request [Parameter(ParameterSetName = 'Normal')] [Parameter(ParameterSetName = 'Connect')] [Object] $Body ) begin { if ($PSCmdlet.ParameterSetName -eq 'Connect') { $Script:AccessId = $AccessId $Script:AccessKey = $AccessKey $Script:Company = $Company $Script:AccessKey.MakeReadOnly() } } process { $QueryString = "" if ($Query) { $QueryString = ($Query.Keys | ForEach-Object { "{0}={1}" -f $_, $Query[$_] }) -join '&' } #Make sure resource is constructed '/path/path' $Resource = "/$($Resource.TrimStart('/'))" #construct the uri $URI = new-Object UriBuilder -Property @{ Scheme = 'https://' Host = "{0}.logicmonitor.com" -f $script:company Path = "/santaba/rest{0}" -f $Resource Query = $QueryString } $Epoch = [DateTimeOffset]::Now.ToUnixTimeMilliseconds() $requestVars = $httpVerb.ToString().ToUpper() + $epoch if ($Body) { $requestVars += $Body } $requestVars += $Resource Write-Verbose -Message "RequestVars: $requestVars" <# Code for SecureString to String https://blogs.msdn.microsoft.com/fpintos/2009/06/12/how-to-properly-convert-securestring-to-string/ #> try { #Get a pointer to insecure string $unmanagedString = [System.Runtime.InteropServices.Marshal]::SecureStringToGlobalAllocUnicode($Script:AccessKey) #Compute hash using insecure string $hmac = New-Object System.Security.Cryptography.HMACSHA256 $hmac.Key = [Text.Encoding]::UTF8.GetBytes( [System.Runtime.InteropServices.Marshal]::PtrToStringUni($unmanagedString) ) $signatureBytes = $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($requestVars)) } finally { #Clean up the insecure stuff [System.Runtime.InteropServices.Marshal]::ZeroFreeGlobalAllocUnicode($unmanagedString) Remove-Variable unmanagedString -Force $hmac.Dispose() } $signatureHex = [System.BitConverter]::ToString($signatureBytes) -replace '-' Write-Verbose -Message "HexSig: $signatureHex" $signature = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($signatureHex.ToLower())) Write-Verbose -Message "Signature: $signature" $auth = 'LMv1 ' + $Script:accessId + ':' + $signature + ':' + $epoch Write-Verbose -Message "Auth: $auth" $Params = @{ Uri = $URI.Uri Method = $HttpVerb UserAgent = "LM-{0}-PowerShell" -f $Script:Company Headers = @{ Authorization = $auth } ContentType = 'application/json' Body = $Body #TimeoutSec #MaximumRedirection #TransferEncoding } try { $Response = Invoke-RestMethod @Params Write-Verbose "Status $($Response.status)" if ($Response.status -ne 200) { Write-Error -Message "Call to LM failed! $($Response.errmsg)" -ErrorId $Response.status } $Response } Catch { Write-Error -Message ("LM API failure: {0}" -f $_.Exception.Message) } } } |