psfalcon
psfalcon copied to clipboard
[ ENHANCEMENT ] Add error message when `Receive-FalconInstaller` fails due to timeout
Describe the bug
Receive-FalconInstaller
does not create file or save download to file. No errors returned.
To Reproduce Run script below. All query requests run fine and return data.
#Requires -Version 5.1
using module @{ ModuleName = 'PSFalcon'; ModuleVersion = '2.2' }
# Ensure that we have a valid CrowdStrike API token
Invoke-Expression -Command .\GetToken.ps1 | Out-Null
$Installers = Get-FalconInstaller -Filter "platform:'windows'+os:'Windows'" -Detailed
if ($Installers) {
# Select latest installer
$Latest = $Installers | Sort-Object -Property release_date -Descending | Select -first 1 #Where-Object { $_.version -match "^\d\.\d{1,2}\.$BuildVersion" }
Write-Output $Latest
if ($Latest) {
$InstallerId = $Latest.sha256
$Filename = $Latest.name
$FileVersion = $Latest.version
}
} else {
throw "Error retrieving installer list"
}
$OutputDir = "$pwd\Output"
New-Item -Path "$OutputDir" -Force -ItemType Directory | Out-Null
$VersionedFileName = $Filename.replace('.exe', '-' + "$FileVersion" + '.exe')
$OutputPath = "$OutputDir\$VersionedFileName"
Write-Output "Downloading installer"
Receive-FalconInstaller -Id $InstallerId -Force -Path "$OutputPath"
Expected behavior
When Receive-FalconInstaller
call completes, it either throws exception or file is on disk
Environment:
- OS: Windows 10[e.g. Windows Server 2016, Windows 10]
- PowerShell: 5.1.19041.2673
- PSFalcon: v2.2.5 {d893eb9f-f6bb-4a40-9caf-aaff0e42acd1}
Additional context
Windows PowerShell transcript start
Start time: 20230510134942
Configuration Name:
Machine: (Microsoft Windows NT 10.0.19045.0)
Host Application: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
Process ID: 28700
PSVersion: 5.1.19041.2673
PSEdition: Desktop
PSCompatibleVersions: 1.0, 2.0, 3.0, 4.0, 5.0, 5.1.19041.2673
BuildVersion: 10.0.19041.2673
CLRVersion: 4.0.30319.42000
WSManStackVersion: 3.0
PSRemotingProtocolVersion: 2.3
SerializationVersion: 1.1.0.1
**********************
Transcript started, output file is PowerShell_transcript.qH4m9R3F.20230510134942.txt
PS C:\dev\crowdstrike-psfalcon> .\DownloadLatestSensors.ps1
VERBOSE: 13:49:48 [ApiClient.Invoke] POST https://api.us-2.crowdstrike.com/oauth2/token
VERBOSE: 13:49:48 [ApiClient.Invoke] ContentType=application/x-www-form-urlencoded, Accept=application/json
VERBOSE: 13:49:48 [ApiClient.Invoke] 201: Created
VERBOSE: 13:49:48 [ApiClient.Invoke] Connection=keep-alive, X-Cs-Region=us-2, X-Cs-Traceid=337ae88d-a1ff-4fd2-975c-2f9274a6a016, X-Ratelimit-Limit=300, X-Ratelimit-Remaining=299, Strict-Transport-Security=max-age=31536000; includeSubDomains, Date=Wed, 10 May 2023 20:49:47 GMT, Server=nginx
VERBOSE: 13:49:48 [Request-FalconToken] Authorized until: 05/10/2023 14:19:47
VERBOSE: 13:49:48 [Get-FalconInstaller] /sensors/combined/installers/v1:get
VERBOSE: 13:49:48 [ApiClient.Invoke] GET https://api.us-2.crowdstrike.com/sensors/combined/installers/v1?filter=platform:'windows'%2Bos:'Windows'
VERBOSE: 13:49:48 [ApiClient.Invoke] Accept=application/json
VERBOSE: 13:49:49 [ApiClient.Invoke] 200: OK
VERBOSE: 13:49:49 [ApiClient.Invoke] Connection=keep-alive, Strict-Transport-Security=max-age=15724800; includeSubDomains, max-age=31536000; includeSubDomains, X-Cs-Region=us-2, X-Cs-Traceid=298467e6-c120-43e5-bba2-5fbb6f150587, X-Ratelimit-Limit=6000, X-Ratelimit-Remaining=5999, Date=Wed, 10 May 2023 20:49:47 GMT, Server=nginx
VERBOSE: 13:49:49 [Write-Result] query_time=0.097343066, powered_by=binserv, trace_id=298467e6-c120-43e5-bba2-5fbb6f150587
name : WindowsSensor.MaverickGyr.exe
description : Falcon Sensor for Windows
platform : windows
os : Windows
os_version :
sha256 : a5059da06d318e1766a2ce95d45d1ac1897443f62e2f2326a1d17c7347d1e7a1
release_date : 2023-05-02T22:07:45.558Z
version : 6.54.16808
file_size : 159103520
file_type : exe
Downloading installer
VERBOSE: 13:49:49 [Receive-FalconInstaller] /sensors/entities/download-installer/v1:get
VERBOSE: 13:49:49 [ApiClient.Invoke] GET https://api.us-2.crowdstrike.com/sensors/entities/download-installer/v1?id=a5059da06d318e1766a2ce95d45d1ac1897443f62e2f2326a1d17c7347d1e7a1
VERBOSE: 13:49:49 [ApiClient.Invoke] Accept=application/octet-stream
I suspect it's how you're building the output path that's causing a problem, but when I tried running your script it worked fine.
I tested Receive-FalconInstaller
a couple of different ways to verify that the command itself works. It downloads properly when using the pipeline with an installer result:
PS C:\falcon> Get-FalconInstaller -Detailed -Limit 1 -Filter "platform:'windows'" | Receive-FalconInstaller
VERBOSE: 12:08:23 [Get-FalconInstaller] /sensors/combined/installers/v1:get
VERBOSE: 12:08:23 [ApiClient.Invoke] GET
https://api.crowdstrike.com/sensors/combined/installers/v1?limit=1&filter=platform:'windows'
VERBOSE: 12:08:23 [ApiClient.Invoke] Accept=application/json
VERBOSE: 12:08:23 [ApiClient.Invoke] 200: OK
VERBOSE: 12:08:23 [ApiClient.Invoke] Connection=keep-alive, Strict-Transport-Security=max-age=15724800;
includeSubDomains, max-age=31536000; includeSubDomains, X-Cs-Region=us-1,
X-Cs-Traceid=42bf7ba5-c907-49e9-8a08-2f28bb8af28c, X-Ratelimit-Limit=6000, X-Ratelimit-Remaining=5991, Date=Thu, 11 May
2023 19:08:24 GMT, Server=nginx
VERBOSE: 12:08:23 [Write-Result] query_time=0.099462462, powered_by=binserv,
trace_id=42bf7ba5-c907-49e9-8a08-2f28bb8af28c
VERBOSE: 12:08:23 [Receive-FalconInstaller] /sensors/entities/download-installer/v1:get
VERBOSE: 12:08:23 [ApiClient.Invoke] GET
https://api.crowdstrike.com/sensors/entities/download-installer/v1?id=47052db19bef20879fc470e2b76e336420d5c23bc6e1ce2b9
57cc546b024e185
VERBOSE: 12:08:23 [ApiClient.Invoke] Accept=application/octet-stream
VERBOSE: 12:08:31 [ApiClient.Invoke] Output directed to 'C:\falcon\WindowsSensor.exe'.
FullName Length LastWriteTime
-------- ------ -------------
C:\falcon\WindowsSensor.exe 159103096 5/11/2023 12:08:31 PM
And when using values saved in a variable:
PS C:\falcon> $Installer = Get-FalconInstaller -Detailed -Limit 1 -Filter "platform:'windows'"
VERBOSE: 12:09:30 [Get-FalconInstaller] /sensors/combined/installers/v1:get
VERBOSE: 12:09:30 [ApiClient.Invoke] GET
https://api.crowdstrike.com/sensors/combined/installers/v1?limit=1&filter=platform:'windows'
VERBOSE: 12:09:30 [ApiClient.Invoke] Accept=application/json
VERBOSE: 12:09:30 [ApiClient.Invoke] 200: OK
VERBOSE: 12:09:30 [ApiClient.Invoke] Connection=keep-alive, Strict-Transport-Security=max-age=15724800;
includeSubDomains, max-age=31536000; includeSubDomains, X-Cs-Region=us-1,
X-Cs-Traceid=87554e8f-d97f-4818-b03d-cafc859b9a59, X-Ratelimit-Limit=6000, X-Ratelimit-Remaining=5992, Date=Thu, 11 May
2023 19:09:31 GMT, Server=nginx
VERBOSE: 12:09:30 [Write-Result] query_time=0.027503389, powered_by=binserv,
trace_id=87554e8f-d97f-4818-b03d-cafc859b9a59
PS C:\falcon> Receive-FalconInstaller -Id $Installer.sha256 -Force -Path C:\falcon\WindowsSensor.exe
VERBOSE: 12:10:04 [Receive-FalconInstaller] /sensors/entities/download-installer/v1:get
VERBOSE: 12:10:04 [ApiClient.Invoke] GET
https://api.crowdstrike.com/sensors/entities/download-installer/v1?id=47052db19bef20879fc470e2b76e336420d5c23bc6e1ce2b9
57cc546b024e185
VERBOSE: 12:10:04 [ApiClient.Invoke] Accept=application/octet-stream
VERBOSE: 12:10:13 [ApiClient.Invoke] Output directed to 'C:\falcon\WindowsSensor.exe'.
FullName Length LastWriteTime
-------- ------ -------------
C:\falcon\WindowsSensor.exe 159103096 5/11/2023 12:10:13 PM
Could you try updating your path creation method? Here's how I would do it:
$OutputDir = Join-Path (Get-Location).Path Output
$Installer = Get-FalconInstaller -Detailed -Limit 1 -Filter "platform:'windows'+os:'Windows'" -Sort release_date.desc
if (!$Installer) { throw "No installer result." }
if ((Test-Path $OutputDir) -eq $false) { [void](New-Item -Path $OutputDir -ItemType Directory) }
$Filename = (($Installer.name -replace '\.exe$'),$Installer.version -join '-'),'exe' -join '.'
Receive-FalconInstaller -Id $Installer.sha256 -Path (Join-Path $OutputDir $Filename) -Force
I tried the code you posted:
#Requires -Version 5.1
using module @{ ModuleName = 'PSFalcon'; ModuleVersion = '2.2' }
$OutputDir = Join-Path (Get-Location).Path Output
$Installer = Get-FalconInstaller -Detailed -Limit 1 -Filter "platform:'windows'+os:'Windows'" -Sort release_date.desc
if (!$Installer) { throw "No installer result." }
if ((Test-Path $OutputDir) -eq $false) { [void](New-Item -Path $OutputDir -ItemType Directory) }
$Filename = (($Installer.name -replace '\.exe$'),$Installer.version -join '-'),'exe' -join '.'
Receive-FalconInstaller -Id $Installer.sha256 -Path (Join-Path $OutputDir $Filename) -Force
And that did not write the file.
I also tried what you posted:
Get-FalconInstaller -Detailed -Limit 1 -Filter "platform:'windows'" | Receive-FalconInstaller
Either case, I am not getting the following message at the end:
[ApiClient.Invoke] Output directed to 'C:\falcon\WindowsSensor.exe'
Any other thoughts as to what might be different about my setup/machine?
Could you try removing and reinstalling the module?
Uninstall-Module -Name PSFalcon -AllVersions
Install-Module -Name PSFalcon -Scope CurrentUser
Strange. I run
Uninstall-Module -Name PSFalcon -AllVersions
Install-Module -Name PSFalcon -Scope CurrentUser
And then run
Get-FalconInstaller -Detailed -Limit 1 -Filter "platform:'windows'" | Receive-FalconInstaller
And it works (correctly downloads and saves the file to disk).
Run the Get/Receive line again and it doesn't save the file.
Uninstall/Re-install again and it still doesn't work. I have to uninstall, reboot, then install and get 1 run that works.
It is seems that this might be a timeout issue during download on 1 machine. I ran it on two other machines with faster connections and both worked repeatedly.
I changed call to download macOS and Ubuntu images on the problem machine and those worked repeatedly, so definitely seems like a timeout issue.
Any ideas on lack of error message for a timeout?
Thanks for following up! I'll do some research and determine if I can add an error message when the download fails due to timeout.
I experimented with this to see if it was possible, but I've yet to figure out a way to output an error given how PSFalcon is downloading the file. Keeping this open in case I figure it out...
Greeting, I was also having this issue..
I worked out a work around by adding a timeout value to the class.ps1
as below.
So I am now able to download all my missing windows clients.
ApiClient() {
$this.Handler = [System.Net.Http.HttpClientHandler]::New()
$this.Client = [System.Net.Http.HttpClient]::New($this.Handler)
# added a large timeout...
$this.Client.Timeout = New-Object System.TimeSpan(0, 10, 90);
$this.Collector = $null
}
I had tried to add it to the download section .. as follows..
$Request = if ($Param.Outfile) {
# Download file
@($Param.Headers.Keys).foreach{ $this.Client.DefaultRequestHeaders.Add($_,$Param.Headers.$_) }
# Added timeout
$this.Client.Timeout = New-Object System.TimeSpan(0, 10, 90);
$this.Verbose('ApiClient.Invoke','Receiving ByteArray content...')
$this.Client.GetByteArrayAsync($Param.Path)
}
As the $this.Client
object it is reused, if you use multiple calls you get the following reponse..
Exception setting "Timeout": "This instance has already started one or more requests. Properties can only be modified before sending the first request."
At line:1 char:1
+ $this.Client.Timeout = New-Object System.TimeSpan(0, 10, 90);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenSetting
Thank you @MatthewCKelly . I will try this out. Right now, all of my downloads are working without adding this timeout, but I am sure I will reproduce again sometime soon with a slow connection.
I wasn't able to get that timeout option working without the "re-use" errors that @MatthewCKelly reported. I'm still experimenting...
I wasn't able to get that timeout option working without the "re-use" errors that @MatthewCKelly reported. I'm still experimenting...
I have a script that does the following..
- Gathers all the current installers.
- Then all the SensorUpdate policies,
- Enumerates all the Policies for membership and then add to an array all the policies that have more than than 10 members and have a sensor assigned and windows
- create a folder for each version that doesnt exist
- downloads each file that doesnt exist.
- gets the hash of each
- then emails the security team once complete any updates. as I am doing multiple calls, the class is loaded only the once therefore as the timeout is set you cannot re-set it.. Hence I moved it to earlier in the class..