Knowledge Base

Preserving for the future: Shell scripts, AoC, and more

nexuslib.ps1 Powershell library for uploading files to Nexus raw repository

After a few days' worth of research, I have finally settled on the way I can upload files to a Nexus repository of type raw. This function accepts multiple ways to pass in authentication: either the prebuilt header, or a regular Powershell credentials object, or just username and password. And yes, this function depends on credlib.ps1.

# Filename: nexuslib.ps1
# Location: \\rdputil1\e$\scripts\Functions\
# Author: bgstack15
# Startdate: 2020-06-09
# Title: Upload files to a Nexus raw repository
# Purpose: Upload arbitrary files to Nexus
# History:
#    I originally tried http://blog.majcica.com/2016/03/31/uploading-artifacts-into-nexus-via-powershell/ but it must be out of date.
# Usage:
#    . nexuslib.ps1
#    Upload-File-To-Nexus Upload-File-To-Nexus -File "C:\Users\bgstack15\Downloads\input1.zip" -Repository "raw" -Directory "/somewhere" -FileName "output.zip" -Credential $hubtestCredential
# References:
#    curl.exe -v -X POST -F "raw.directory=example/raw/dir/" -F "raw.asset1=@C:\Users\bgstack15\Downloads\example.zip" -F "raw.asset1.filename=example.zip" -u bgstack15:SOMETHINGHERE https://hubtest.example.com/service/rest/v1/components?repository=raw --trace-ascii outputfile.txt
#    https://help.sonatype.com/repomanager3/rest-and-integration-api/components-api#ComponentsAPI-Raw
#    https://stackoverflow.com/questions/22491129/how-to-send-multipart-form-data-with-powershell-invoke-restmethod/48580319#48580319
# Improve:
#    Upload maven2 files? That use case will probably not come up here.
# Documentation:
# Dependencies:
#    \\rdputil1\e$\scripts\Functions\credlib.ps1

Function Upload-File-To-Nexus {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$True )][ValidateNotNullOrEmpty()][string]$File, # aka File
        [Parameter(Mandatory=$False)][string]$Server = "hubtest.benefitfocus.com",
        [Parameter(Mandatory=$False)][string]$Username,
        [Parameter(Mandatory=$False)][string]$Password,
        [Parameter(Mandatory=$False)][Hashtable]$AuthHeaders,
        [Parameter(Mandatory=$False)][System.Management.Automation.PSCredential]$Credential,
        [parameter(Mandatory=$True )][ValidateNotNullOrEmpty()][string]$Repository,
        [parameter(Mandatory=$True )][ValidateNotNullOrEmpty()][string]$Directory,
        [Parameter(Mandatory=$False)][ValidateNotNullOrEmpty()][string]$FileName # destination filename, which can be derived from uploaded file
    )

    Begin {
        If (!($AuthHeaders -ne $Null)) {
            If (!($Credential -ne $Null)) {
                If ($Username -ne $null -And $Password -ne $Null -And $Username -ne "" -And $Password -ne "") {
                    $secret = $password | ConvertTo-SecureString -AsPlainText -Force
                    $Credential = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList ${username}, ${secret}
                } Else {
                    Throw "Need one of: -AuthHeaders or -Credential, or both -Username and -Password. Aborted."
                }
            }
            # transform credential to authheader format
            . \\rdputil1\e$\scripts\Functions\credlib.ps1
            $AuthHeaders = Get-Basic-Base64-Auth-Headers -Credential $Credential
        }
        $Uri = "https://${Server}/service/rest/v1/components?repository=${Repository}"
        $boundary = [System.Guid]::NewGuid().ToString()
        $LF = "`r`n"
    }

    Process {
        If (!($FileName -ne $Null -And $FileName -ne "")) {
            # if filename is null, then just use the filename from the input file
            $FileName = Split-Path $File -Leaf
        }
        Try { $fileBin = [System.IO.File]::ReadAlltext($File) }
        Catch { Throw "Unable to read file $File. Aborted." }
        $bodyLines = (
            "--${boundary}",
            "Content-Disposition: form-data; name=`"raw.directory`"",
            "",
            "${Directory}",
            "--${boundary}",
            "Content-Disposition: form-data; name=`"raw.asset1`"; filename=`"${FileName}`"",
            "Content-Type: application/octet-stream",
            "",
            $fileBin,
            "",
            "--${boundary}",
            "Content-Disposition: form-data; name=`"raw.asset1.filename`"",
            "",
            "${FileName}",
            "--${boundary}--",
            ""
        ) -join $LF
        $Response = Invoke-WebRequest -Uri $Uri -Method "POST" -Headers $AuthHeaders -ContentType "multipart/form-data; boundary=`"$boundary`"" -Body $bodyLines
        If ($Response.StatusCode -ge 200 -And $Response.StatusCode -lt 300) {
            $Output = "https://${Server}/repository/${repository}/${directory}/${FileName}" -Replace "//","/"
            Write-Output $Output
        } Else {
            Write-Output $Response
        }
    }
}

References

Weblinks

  1. Components API | Sonatype Nexus documentation
  2. curl - How to send multipart/form-data with PowerShell Invoke-RestMethod - Stack Overflow

Alternate reading

  1. This page attempts to explain how to upload a file to Nexus, but it must be out of date or otherwise invalid. And the official Sonatype documentation even linked to it at one point somewhere! Uploading artifacts into Nexus via PowerShell – Mummy's blog

Comments