Powershell Download a Collector

  • 3 January 2020
  • 3 replies
  • 41 views

I've been working on a Powershell method to download a collector. I'm to the point where I've got content, but no matter how I write the file ($content > "lm.exe", $content | Out-File "lm.exe" (tried all the encoding options), or $content | Set-Content lm.exe) the resulting file will not execute.

https://XXXX.logicmonitor.com/santaba/rest/setting/collectors/1005/installers/Win64?collectorSize=small

 

$i = Get-LMCollectorInstaller -Account "XXXX" -CollectorId 1005 -CollectorSize small -Platform Win64 -Verbose
VERBOSE: Verb:GET Epoch:1578077331506 Data: resourcePath:/setting/collectors/1005/installers/Win64
VERBOSE: Url: https://XXXX.logicmonitor.com/santaba/rest/setting/collectors/1005/installers/Win64?collectorSize=small, Method: GET, Header:
System.Collections.Generic.Dictionary`2[System.String,System.String]
VERBOSE: GET https://XXXX.logicmonitor.com/santaba/rest/setting/collectors/1005/installers/Win64?collectorSize=small with 0-byte payload
VERBOSE: received -1-byte response of content type application/binary


$i | Set-Content .\Desktop\lm.exe
$i > .\Desktop\lm.exe

Running both yields a "This app can't run on your PC" popup on Windows 10 and Windows Server 2016. The file sizes are _almost_ the same (291,802,970 [not working] vs. 291,802,968 [working]) which makes me think it's an encoding issue, but I also tried all the Out-File encoding options with the same results.

 

$httpVerb = "GET"
$resourcePath = "/setting/collectors/$CollectorId/installers/$Platform"
$Query = "?collectorSize=$CollectorSize"

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

<# request details #>
$httpVerb = $Verb
$resourcePath = $Path

<# Construct URL #>
$url = 'https://' + $Account + '.logicmonitor.com/santaba/rest' + $resourcePath + $Query

<# Get current time in milliseconds #>
$epoch = [Math]::Round((New-TimeSpan -start (Get-Date -Date "1/1/1970") -end (Get-Date).ToUniversalTime()).TotalMilliseconds)

<# Concatenate Request Details #>
Write-Verbose "Verb:$httpVerb Epoch:$epoch Data:$Data resourcePath:$resourcePath"
$requestVars = $httpVerb + $epoch + $Data + $resourcePath

<# Construct Signature #>
$hmac = New-Object System.Security.Cryptography.HMACSHA256
$hmac.Key = [Text.Encoding]::UTF8.GetBytes($AccessKey)
$signatureBytes = $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($requestVars))
$signatureHex = [System.BitConverter]::ToString($signatureBytes) -replace '-'
$signature = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($signatureHex.ToLower()))

<# Construct Headers #>
$auth = 'LMv1 ' + $AccessId + ':' + $signature + ':' + $epoch
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization",$auth)
$headers.Add("Content-Type",'application/json')

$response = Invoke-RestMethod -Uri $url -Method $Verb -Header $headers -ContentType "application/json"

Write-Output $response


3 replies

Userlevel 6
Badge +11

It seems to work for me if I use the -OutFile parameter with Invoke-RestMethod but didn't figure out having it assigned to a variable that is then outputted. I also changed the content type to binary instead of json since you are not looking for json data, but seems LM will send binary data anyway.

Invoke-RestMethod -Uri $url -Method $Verb -Header $headers -ContentType "application/binary" -OutFile "lm-out.exe" -Verbose

 

Userlevel 6
Badge +11

So PowerShell is converting it to a String causing encoding problems. If you switch to Invoke-WebRequest the Content property will be a proper Byte array which you can save to file. See https://www.reddit.com/r/PowerShell/comments/719oip/downloading_a_file_via_http_directly_to_a_variable/dn9snst/ . Note that the pipeline version was extremely slow on my system but this version was quick.

$Response = Invoke-WebRequest -Uri $url -Method $Verb -Header $headers -ContentType "application/binary" -Verbose
$File = [System.IO.FileStream]::new('lm-filestream.exe', [System.IO.FileMode]::Create)
$File.write($Response.Content, 0, $Response.Content.Length)
$File.close()

 

 

 

 

Just to close the loop on this, the Invoke-RestMethod with the correct contenttype and outfile did the trick. WebRequest still left me with an unexecutable file. Thanks Mike!

Reply