2026-09-30 07:00:20 -04:00
# Requires -Version 5.0
<#
. SYNOPSIS
2026-09-30 08:12:55 -04:00
Downloads and runs the DSA manifest collector, with optional upload and cleanup.
2026-09-30 07:00:20 -04:00
. 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
2026-09-30 07:20:37 -04:00
dsa-manifest-HOSTNAME-yyyyMMddHHmmss.csv, using the computer name and current local date and time.
2026-09-30 08:12:55 -04:00
$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.
2026-09-30 07:00:20 -04:00
#>
If ( [ string ]:: IsNullOrWhiteSpace ([ string ] $DSAManifestSource ) ) {
Throw 'Set $DSAManifestSource in the caller before running Get-DSAManifest.'
}
2026-09-30 07:20:37 -04:00
# Normalize RMM text values without treating every nonempty string as true.
2026-10-01 11:16:23 -04:00
$DsaOptions = @ {
Zip = ConvertTo-RmmBoolean -Value $Zip -VariableName 'Zip'
Upload = ConvertTo-RmmBoolean -Value $Upload -VariableName 'Upload'
DeleteAfterUpload = ConvertTo-RmmBoolean -Value $DeleteAfterUpload -VariableName 'DeleteAfterUpload'
2026-09-30 07:20:37 -04:00
}
2026-09-30 08:12:55 -04:00
$DsaZipEnabled = $DsaOptions . Zip
2026-09-30 07:20:37 -04:00
2026-09-30 07:00:20 -04:00
$DSAManifestOutput = Join-Path -Path $Global:OutputDirectory -ChildPath (
2026-09-30 07:20:37 -04:00
'dsa-manifest-{0}-{1}.csv' -f $Env:COMPUTERNAME , ( Get-Date -Format 'yyyyMMddHHmmss' )
2026-09-30 07:00:20 -04:00
)
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} "
2026-09-30 11:42:05 -04:00
$DsaDownloadedFile = Download-FileDirectly -URL $DsaDownloadUrl -File $DsaExecutablePath -ErrorAction Stop
2026-09-30 07:00:20 -04:00
2026-09-30 11:42:05 -04:00
# Require a returned path and a nonempty download before starting the collector.
2026-09-30 07:00:20 -04:00
If ( [ string ]:: IsNullOrWhiteSpace ([ string ] $DsaDownloadedFile ) -or
!( Test-Path -LiteralPath $DsaExecutablePath -PathType Leaf ) ) {
Throw "Failed to download dsa-collect.exe to ${DsaExecutablePath} "
}
2026-09-30 09:46:37 -04:00
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."
}
2026-09-30 07:00:20 -04:00
$DsaArguments = @ ( '--source' , [ string ] $DSAManifestSource , '--output' , [ string ] $DSAManifestOutput )
2026-09-30 07:20:37 -04:00
If ( $DsaZipEnabled ) {
2026-09-30 07:00:20 -04:00
$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 ' '
2026-09-30 08:12:55 -04:00
$DsaCollectionTime = Get-Date
2026-09-30 07:20:37 -04:00
Write-Output "Running dsa-collect.exe (ZIP compression: ${DsaZipEnabled} )"
2026-09-30 07:00:20 -04:00
$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 ) "
}
2026-09-30 07:20:37 -04:00
$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} "
2026-09-30 08:12:55 -04:00
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
}