Public/Export-NSPMDEResponse.ps1

function Export-NSPMDEResponse {
    <#
    .SYNOPSIS
        Writes the hand-back JSON (<Company>_MDE_Response.json): this machine's Defender for Endpoint
        facts, the candidate apps, the capture, the collision evidence with its verdict, and a
        suggested exclusion - for the tech to fill in the EDR exclusion request. Returns the file.
 
    .DESCRIPTION
        Written as an NSP.Toolkit Response hand-off (shared header, SHA-256 of the payload) when
        NSP.Toolkit is loaded, plain JSON otherwise. The file is read back and checked before it is
        returned; a file that fails is deleted and the function throws. The ProcMon files are not
        embedded - they stay next to the capture, identified by name, size and SHA-256.
 
    .PARAMETER Company
        Client the evidence belongs to.
 
    .PARAMETER OutputDirectory
        Folder to write to (the MDE Responses work folder by default).
 
    .PARAMETER ApplicationName
        The line-of-business app as the client knows it (e.g. "Sage 100").
 
    .PARAMETER Status
        Get-NSPMDEStatus output (read now when omitted).
 
    .PARAMETER Candidate
        Get-NSPMDECandidateApp output.
 
    .PARAMETER Capture
        The capture object (Start-/Stop-NSPProcmonCapture).
 
    .PARAMETER Collision
        Find-NSPMDECollision output.
 
    .PARAMETER TechNotes
        Free text for the request.
 
    .PARAMETER MaxCollisions
        How many collisions to include in full (all are counted).
 
    .EXAMPLE
        Export-NSPMDEResponse -Company 'Example Co' -ApplicationName 'Sage 100' -Collision $collisions -Capture $capture
 
    .EXAMPLE
        Export-NSPMDEResponse -Company 'Example Co' -OutputDirectory C:\Temp -WhatIf
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([IO.FileInfo])]
    param(
        [Parameter(Mandatory)][string]$Company,
        [string]$OutputDirectory,
        [string]$ApplicationName,
        $Status,
        [object[]]$Candidate,
        $Capture,
        [object[]]$Collision,
        [string]$TechNotes,
        [ValidateRange(1, 1000)][int]$MaxCollisions = 25
    )

    $useHandoff = [bool](Get-Command New-NSPHandoff -ErrorAction SilentlyContinue)
    if (-not $OutputDirectory) {
        $OutputDirectory = if (Get-Command Get-NSPToolWorkPath -ErrorAction SilentlyContinue) { Get-NSPToolWorkPath -Tool MDE -Kind Responses } else { Join-Path $env:ProgramData 'NSP\MDE\Responses' }
    }
    if (-not $Status) { $Status = Get-NSPMDEStatus }
    $version = [string]$ExecutionContext.SessionState.Module.Version

    $sha = ''; $bytes = $null; $csvName = ''
    if ($Capture -and $Capture.BackingFile -and (Test-Path -LiteralPath $Capture.BackingFile -PathType Leaf)) {
        $sha = Get-NSPFileSha256 -Path $Capture.BackingFile
        $bytes = (Get-Item -LiteralPath $Capture.BackingFile).Length
        $csv = [IO.Path]::ChangeExtension($Capture.BackingFile, '.csv')
        if (Test-Path -LiteralPath $csv) { $csvName = Split-Path -Leaf $csv }
    }

    $payload = New-NSPMDEResponsePayload -Company $Company -ApplicationName $ApplicationName -Status $Status -Candidate $Candidate `
        -Capture $Capture -Collision $Collision -TechNotes $TechNotes -MaxCollisions $MaxCollisions -ToolVersion $version `
        -BackingFileSha256 $sha -BackingFileBytes $bytes -CsvFile $csvName

    $stem = ($Company -replace '[^A-Za-z0-9_-]', '')
    $target = Join-Path $OutputDirectory "${stem}_MDE_Response.json"
    if (-not $PSCmdlet.ShouldProcess($target, 'Write the MDE hand-back')) { return }
    if (-not (Test-Path -LiteralPath $OutputDirectory)) { New-Item -ItemType Directory -Path $OutputDirectory -Force | Out-Null }

    if ($useHandoff) {
        $handoff = New-NSPHandoff -Kind Response -Tool MDE -Company $Company -Payload $payload -PayloadSchema $payload.SchemaVersion `
            -ToolVersion $version -GeneratedBy "NSP.Win.MDE $version" -ComputerName $payload.ComputerName -Domain $payload.Domain
        $path = (Export-NSPHandoff -Handoff $handoff -Directory $OutputDirectory -Confirm:$false).FullName
    } else {
        $path = $target
        [IO.File]::WriteAllText($path, (ConvertTo-Json -InputObject $payload -Depth 20), (New-Object Text.UTF8Encoding($false)))
    }

    $problem = $null
    try {
        $check = if ($useHandoff) { (Import-NSPHandoff -Path $path -Tool MDE -Kind Response).Payload } else { ConvertFrom-Json -InputObject ([IO.File]::ReadAllText($path)) }
        if ($check.SchemaVersion -ne $script:NSPMDEResponseSchema) { $problem = 'the schema version did not survive' }
        elseif ([int]$check.Evidence.Violations -ne [int]$payload.Evidence.Violations) { $problem = 'the evidence counts did not survive' }
    } catch { $problem = "it could not be read back: $($_.Exception.Message)" }
    if ($problem) {
        Remove-Item -LiteralPath $path -Force -ErrorAction SilentlyContinue
        throw "Hand-back check failed - $problem. The file was deleted."
    }
    Get-Item -LiteralPath $path
}