#Requires -Version 5.0 <# .SYNOPSIS Downloads and runs the DSA manifest collector, with optional upload and cleanup. .DESCRIPTION Tools.ps1 must be dot-sourced by the caller first. Set $DSAManifestSource in the caller's scope before using Run-Script -LivePSScript Get-DSAManifest. The output path is generated in $Global:OutputDirectory with the filename dsa-manifest-HOSTNAME-yyyyMMddHHmmss.csv, using the computer name and current local date and time. $Zip enables ZIP compression; $Upload sends the result to the FileDrop; $DeleteAfterUpload removes the local result only after a confirmed upload. All three accept 'true', '$true', 'yes', 'y', or '1' to enable, and 'false', '$false', 'no', 'n', or '0' to disable. Empty values disable the option. Values are case-insensitive and surrounding whitespace is ignored. When $Upload is false, the local CSV or ZIP is retained regardless of $DeleteAfterUpload. #> If ( [string]::IsNullOrWhiteSpace([string]$DSAManifestSource) ) { Throw 'Set $DSAManifestSource in the caller before running Get-DSAManifest.' } # Normalize RMM text values without treating every nonempty string as true. $DsaOptions = @{ Zip = ConvertTo-RmmBoolean -Value $Zip -VariableName 'Zip' Upload = ConvertTo-RmmBoolean -Value $Upload -VariableName 'Upload' DeleteAfterUpload = ConvertTo-RmmBoolean -Value $DeleteAfterUpload -VariableName 'DeleteAfterUpload' } $DsaZipEnabled = $DsaOptions.Zip $DSAManifestOutput = Join-Path -Path $Global:OutputDirectory -ChildPath ( 'dsa-manifest-{0}-{1}.csv' -f $Env:COMPUTERNAME, (Get-Date -Format 'yyyyMMddHHmmss') ) New-Item -Path $Global:OutputDirectory -ItemType Directory -Force -ErrorAction Stop | Out-Null # Select the executable for the OS, even when the RMM uses 32-bit PowerShell. If ( [Environment]::Is64BitOperatingSystem ) { $DsaDownloadUrl = 'https://xfer.emberkom.com/shares/tools/folders/9b9988fb-c53a-4833-a4ad-68d01a3021bb/files/dsa-collect-windows-amd64-exe/download/dsa-collect-windows-amd64.exe' } Else { $DsaDownloadUrl = 'https://xfer.emberkom.com/shares/tools/folders/9b9988fb-c53a-4833-a4ad-68d01a3021bb/files/dsa-collect-windows-386-exe/download/dsa-collect-windows-386.exe' } $DsaExecutablePath = Join-Path -Path $Global:ToolsDirectory -ChildPath 'dsa-collect.exe' New-Item -Path $Global:ToolsDirectory -ItemType Directory -Force -ErrorAction Stop | Out-Null Write-Output "Downloading dsa-collect.exe to ${DsaExecutablePath}" $DsaDownloadedFile = Download-FileDirectly -URL $DsaDownloadUrl -File $DsaExecutablePath -ErrorAction Stop # Require a returned path and a nonempty download before starting the collector. If ( [string]::IsNullOrWhiteSpace([string]$DsaDownloadedFile) -or !(Test-Path -LiteralPath $DsaExecutablePath -PathType Leaf) ) { Throw "Failed to download dsa-collect.exe to ${DsaExecutablePath}" } If ( (Get-Item -LiteralPath $DsaExecutablePath -ErrorAction Stop).Length -eq 0 ) { Throw "The downloaded dsa-collect.exe is 0 bytes: ${DsaExecutablePath}. The collector was not started. Check the download response and client TLS/BITS settings." } $DsaArguments = @('--source', [string]$DSAManifestSource, '--output', [string]$DSAManifestOutput) If ( $DsaZipEnabled ) { $DsaArguments += '--zip' } # Quote native arguments explicitly, including paths ending in a backslash. # Start-Process joins its argument list without adding the required quoting. $DsaCommandLine = ($DsaArguments | ForEach-Object { '"{0}"' -f ($_ -replace '(\\*)"', '$1$1\"' -replace '(\\+)$', '$1$1') }) -join ' ' $DsaCollectionTime = Get-Date Write-Output "Running dsa-collect.exe (ZIP compression: ${DsaZipEnabled})" $DsaProcess = Start-Process -FilePath $DsaExecutablePath -ArgumentList $DsaCommandLine ` -Wait -PassThru -WindowStyle Hidden -ErrorAction Stop If ( $DsaProcess.ExitCode -ne 0 ) { Throw "dsa-collect.exe failed with exit code $($DsaProcess.ExitCode)" } $DsaResultPath = $DSAManifestOutput If ( $DsaZipEnabled ) { $DsaResultPath = [System.IO.Path]::ChangeExtension($DSAManifestOutput, '.zip') } If ( !(Test-Path -LiteralPath $DsaResultPath -PathType Leaf) ) { Throw "dsa-collect.exe did not create the expected output: ${DsaResultPath}" } Write-Output "DSA manifest created: ${DsaResultPath}" If ( $DsaOptions.Upload ) { Write-Output "Uploading DSA manifest: ${DsaResultPath}" $DsaUploadReceipt = Upload-File -Path $DsaResultPath ` -Subject "DSA manifest file uploaded from ${Env:COMPUTERNAME}" ` -Message "DSA collection performed on ${DSAManifestSource} at $($DsaCollectionTime.ToString('yyyy-MM-dd HH:mm:ss zzz'))" ` -ErrorAction Stop If ( [string]::IsNullOrWhiteSpace([string]$DsaUploadReceipt.AttachmentId) -or [string]::IsNullOrWhiteSpace([string]$DsaUploadReceipt.Status) ) { Throw 'The upload did not return a confirmation receipt. The local manifest has been retained.' } Write-Output "DSA manifest uploaded: ${DsaResultPath}" If ( $DsaOptions.DeleteAfterUpload ) { Remove-Item -LiteralPath $DsaResultPath -Force -ErrorAction Stop Write-Output "Deleted uploaded DSA manifest: ${DsaResultPath}" } $DsaUploadReceipt }