functions/New-XdrEndpointDeviceLiveResponseLibraryFile.ps1

function New-XdrEndpointDeviceLiveResponseLibraryFile {
    <#
    .SYNOPSIS
        Uploads a script file to the Live Response library.

    .DESCRIPTION
        Uploads a local script file to the Microsoft Defender XDR Live Response library.
        The file can then be used with the 'putfile' and 'run' commands during Live Response sessions.
        Multipart form-data is constructed as a byte array to maintain WebSession cookie compatibility.

    .PARAMETER FilePath
        The full path to the local script file to upload.

    .PARAMETER Description
        Optional description for the library file.

    .PARAMETER HasParameters
        Indicates that the script accepts parameters during execution.

    .PARAMETER ParametersDescription
        Description of the parameters accepted by the script. Only relevant when -HasParameters is specified.

    .PARAMETER OverrideIfExists
        If specified, overwrites an existing library file with the same name.
        Without this switch, uploading a duplicate file name will fail.

    .PARAMETER WhatIf
        Shows what would happen if the cmdlet runs. The file is not uploaded.

    .PARAMETER Confirm
        Prompts you for confirmation before uploading the file.

    .EXAMPLE
        New-XdrEndpointDeviceLiveResponseLibraryFile -FilePath 'C:\Scripts\Remediate.ps1'
        Uploads Remediate.ps1 to the Live Response library.

    .EXAMPLE
        New-XdrEndpointDeviceLiveResponseLibraryFile -FilePath 'C:\Scripts\Remediate.ps1' -Description 'Remediation script' -HasParameters -ParametersDescription '-TargetProcess <string>'
        Uploads with metadata describing the script parameters.

    .EXAMPLE
        New-XdrEndpointDeviceLiveResponseLibraryFile -FilePath 'C:\Scripts\Remediate.ps1' -OverrideIfExists
        Replaces an existing library file with the same name.

    .OUTPUTS
        Object
        Returns the API response containing the uploaded file metadata.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateScript({ Test-Path $_ -PathType Leaf })]
        [string]$FilePath,

        [Parameter()]
        [string]$Description,

        [Parameter()]
        [switch]$HasParameters,

        [Parameter()]
        [string]$ParametersDescription,

        [Parameter()]
        [switch]$OverrideIfExists
    )

    begin {
        Update-XdrConnectionSettings
    }

    process {
        try {
            $fileName = [System.IO.Path]::GetFileName($FilePath)

            if (-not $PSCmdlet.ShouldProcess($fileName, "Upload to Live Response library")) {
                return
            }

            # Build file_fields metadata JSON
            $fileFields = [ordered]@{
                description            = if ($Description) { $Description } else { $null }
                has_parameters         = $HasParameters.IsPresent
                parameters_description = if ($ParametersDescription) { $ParametersDescription } else { $null }
                override_if_exists     = $OverrideIfExists.IsPresent
            }
            $fileFieldsJson = $fileFields | ConvertTo-Json -Compress

            # Read file bytes
            $fileBytes = [System.IO.File]::ReadAllBytes($FilePath)

            # Build multipart/form-data body as byte array (required for binary content with WebSession)
            $boundary = [System.Guid]::NewGuid().ToString()
            $encoding = [System.Text.Encoding]::UTF8
            $stream = [System.IO.MemoryStream]::new()

            # Part 1: file[] (binary)
            $part1Header = "--$boundary`r`nContent-Disposition: form-data; name=`"file[]`"; filename=`"$fileName`"`r`nContent-Type: application/octet-stream`r`n`r`n"
            $part1HeaderBytes = $encoding.GetBytes($part1Header)
            $stream.Write($part1HeaderBytes, 0, $part1HeaderBytes.Length)
            $stream.Write($fileBytes, 0, $fileBytes.Length)

            # Part 2: file_fields (JSON metadata)
            $part2 = "`r`n--$boundary`r`nContent-Disposition: form-data; name=`"file_fields`"`r`n`r`n$fileFieldsJson`r`n--$boundary--`r`n"
            $part2Bytes = $encoding.GetBytes($part2)
            $stream.Write($part2Bytes, 0, $part2Bytes.Length)

            $bodyBytes = $stream.ToArray()
            $stream.Dispose()

            $contentType = "multipart/form-data; boundary=$boundary"
            $Uri = "https://security.microsoft.com/apiproxy/mtp/liveResponseApi/library/upload_file?useV3Api=true"

            Write-Verbose "Uploading '$fileName' to Live Response library"
            $result = Invoke-RestMethod -Uri $Uri -Method Post -Body $bodyBytes -ContentType $contentType -WebSession $script:session -Headers $script:headers

            # Invalidate the library listing cache so next Get- call reflects the new file
            Clear-XdrCache -CacheKey "XdrLiveResponseLibrary" -ErrorAction SilentlyContinue

            Write-Verbose "Successfully uploaded '$fileName'"
            return $result
        } catch {
            Write-Error "Failed to upload '$FilePath' to Live Response library: $_"
        }
    }

    end {
    }
}