functions/invoke-jenkins.ps1

function Invoke-Jenkins {

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]$action = "start", 
        [Parameter(Mandatory = $true)]$job = "test.pipeline", 
        [Parameter(Mandatory = $false)]
        $server = $null, 
        $UserName = $null, 
        $apikey = $null,
        $jobparams = $null,
        [switch][bool]$usepipelines = $true
    )

    ipmo require   
    req newtonsoft.json

    if ($action -eq "attach" -and $attach -eq $null) { $attach = "latest" } 

    if ($server -eq $null) {
        $server = $env:JENKINS_URL
        if ($server -eq $null) {
            $msg = "please provide server url either by passing -server or setting evironment variable 'JENKINS_URL', i.e.: `$env:JENKINS_URL='http://myjenkins:8080'"
            write-warning $msg
            return
        }
    }

    if ($UserName -eq $null) {
        req cache
        $cred = Get-CredentialsCached -container "jenkins" -message "jenkins username and api token for $server"
        $username = $cred.username
        $apikey = $cred.getnetworkcredential().password
    }


    $JenkinsAPIToken = $apikey
    $JENKINS_URL = $server


 
 
    # Global Details
    $JOB_URL = "$jenkins_url/job/$job"
 
    # Workflow Parameters
    #$jobparams.Add("MachineIP", "SomeString1")
    #$jobparams.Add("RequestID", $RequestId)
 
    # Load Assemblies
    [Reflection.Assembly]::LoadFile( `
  'C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Web.dll')`
  | out-null  
 
 
    <#
  Retrieve Crumbs (needed for POST Requets)
#>

 

    write-verbose "obtaining crumbs for user '$UserName'"
    $headers = $null
    $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
    $headers.Add("Authorization", "Basic " + 
        [System.Convert]::ToBase64String(
            [System.Text.Encoding]::ASCII.GetBytes("$($UserName):$JenkinsAPIToken")))
 
    $url = "$($JENKINS_URL)/crumbIssuer/api/xml"
    [xml]$crumbs = Invoke-WebRequest $url -Method GET  -Headers $headers
 
    # set the CSRF token in the headers
    #$webclient.Headers.Add($crumbs.defaultCrumbIssuer.crumbRequestField, $crumbs.defaultCrumbIssuer.crumb)

    $reqparm = new-object System.Collections.Specialized.NameValueCollection
 
    #$crumbs | out-string | write-verbose
    # set the CSRF token in the headers
    $headers.Add($crumbs.defaultCrumbIssuer.crumbRequestField, $crumbs.defaultCrumbIssuer.crumb)
    $headers.Add("Accept", "application/xml")
 
 
 
    <#
  Construct URL for Build Request
#>

    $jobnumber = $null


    if ($action -eq "start") {
        $tries = 2
        for($t = $tries; $t -gt 0; $t--) {
            if ($jobparams -ne $null) { 
                $url = "$($JOB_URL)/buildWithParameters?"
    
                $count = 0
                foreach ($h in $jobparams.GetEnumerator()) {
                    $h
                    $count = $count + 1
                    $url = $url + "$($h.Name)=$([System.Web.HttpUtility]::UrlEncode($h.Value))"
                    if (!($count -eq $jobparams.Count)) {
                        $url = $url + "&"
                    }
                }
            }
            else {
                $url = "$($JOB_URL)/build"
            }
    
            $result = $null
    
            try {
                $jobsubmit = Invoke-WebRequest $url -Method POST -Headers $headers -ContentType "application/json" -Body "" -ErrorAction stop
                break
            } catch {
                if ($t -le 1) { throw }
                if ($_.Exception.Message.Contains("Nothing is submitted") -or $_.Exception.Message.Contains("400")) {
                    write-warning "does this job have parameters? trying with -jobparams @{}"
                    $jobparams = @{}
                }
                else {
                    throw
                }
            }    
        }
        #hack to get output
        $linearray = $null
        $linearray = ($jobsubmit.RawContent).Split()
 
        foreach ($line in $linearray) {
            if ($line -match "http://") {
                write-verbose "queue address = $($line)"
                $queueaddress = "$($line)api/xml"
            }
        }

        if ($queueaddress -match "item/([0-9]+)/") {
            $queueId = $Matches[1]
        }
 
 
        $output = $null
        $url = "$($JOB_URL)/api/xml?tree=url,inQueue,queueItem[*]&xpath=//*&wrapper=jobs"


        do {
            $output = Invoke-WebRequest $url -Method GET  -Headers $headers 
            [xml]$jobdetails = $output.Content
            if ($jobdetails.jobs.queueItem -eq $null) { break }
            if ($jobdetails.jobs.queueItem.id -ne $queueId) { break }
            write-host "[$(get-date -format 'HH:mm:ss')] still waiting in the queue: $($jobdetails.jobs.queueItem.why)"
            Start-Sleep -Seconds 1
        }
        while ($true)
 

        write-host "job '$job' started!"
        #wait until job is in the queue

        #Search for the job with unique ID
        #$url = "$($JOB_URL)/api/xml?tree=builds[actions[parameters[name,value]],number]&xpath=//build[action[parameter[name=`"RequestID`"][value=`"$($RequestId)`"]]]/number&wrapper=job_names"
        $url = "$($JOB_URL)/api/xml?xpath=//build[queueId=$queueId]&wrapper=builds&tree=builds[actions,number,queueId]"
        #$url = "$($JOB_URL)/api/json"
 
        $tries = 20

        do {
            $output = Invoke-WebRequest $url -Method GET  -Headers $headers
            if (($output.Content -match "<builds>")) {
                [xml]$jobdetails = $output.Content
                $jobnumber = $jobdetails.builds.build.number    
                write-verbose "job number is: $jobnumber"
            }
            else {
                write-host "[$(get-date -format 'HH:mm:ss')] still waiting for build to appear in API"
                $tries--
                if ($tries -le 0) {
                    throw "build with queueId $queueId not found: $url"
                }
                start-sleep 1
            }
        }
        while ($jobnumber -eq $null -and $tries -gt 0)

        if ($jobnumber -eq $null) {
            throw "Build with queue id $queueId not found in API!"
        }
    } 

    if ($action -eq "attach") {
        if ($action -eq "attach" -and ($attach -eq "latest")) {
            $url = "$($JOB_URL)/api/xml?xpath=//build[building='true'][1]&wrapper=builds&tree=builds[actions,number,queueId,building]"
            $output = Invoke-WebRequest $url -Method GET  -Headers $headers
            [xml]$jobdetails = $output.Content    
            if ($jobdetails.builds.build) {
                $jobnumber = $jobdetails.builds.build.number
            }
            else {
                $url = "$($JOB_URL)/api/xml?xpath=//build[1]&wrapper=builds&tree=builds[actions,number,queueId,building]"
                $output = Invoke-WebRequest $url -Method GET  -Headers $headers
                [xml]$jobdetails = $output.Content
                $jobnumber = $jobdetails.builds.build.number    
            }
        }
        elseif ($attach -ne $true) {
            $jobnumber = $attach
        }
        write-host  "attaching to job $($attach): $jobnumber"
    }

    write-host "=========== Console output ============="

    $logpos = 0
    $dotwritten = $false
    $logformat = "Html" #or Text, case sensitive

    if ($logformat -eq "html") {
        $null = [System.Reflection.Assembly]::LoadWithPartialName("System.web")
    }

    while ($true) {
        $status = ""
        $url = "$($JOB_URL)/$($jobnumber)/api/xml?xpath=//*&wrapper=run"
        $output = Invoke-WebRequest $url -Method GET  -Headers $headers
        [xml]$jobdetails = $output.Content    

        if ($usepipelines) {
            $url = "$($JOB_URL)/$jobnumber/wfapi/"
            $output = Invoke-WebRequest $url -Method GET  -Headers $headers
            $wfstatus = ConvertFrom-JsonNewtonsoft $output.content
            $stage = $wfstatus.stages | ? { $_.status -eq "IN_PROGRESS" }
        }


    

        if ($logpos -eq 0 -and $jobdetails.run.building -eq "false") {
            Write-Verbose "getting log length..."
            $url = "$($JOB_URL)/$($jobnumber)/consoleText"
            $url = "$($JOB_URL)/$($jobnumber)/logText/progressiveText"
            $output = Invoke-WebRequest $url -Method HEAD -Headers $headers    

            $loglength = $output.headers.'X-Text-Size'
            $logpos = [Math]::Max(0, $loglength - 1000000)
        }
    
        $url = "$($JOB_URL)/$($jobnumber)/logText/progressive$($logformat)?start=$logpos"
        $output = Invoke-WebRequest $url -Method GET  -Headers $headers    

        if ($output.Content.Length -gt 0) {
            if ($dotwritten) { write-host; $dotwritten = $false }
            if ($logformat -eq "html") { 
                $log = $output.content -replace "<.*?>" -split "`r`n"
                $log = $log | ? { ![string]::IsNullOrEmpty($_) } | % {  [system.web.httputility]::htmldecode($_) }
            }
            else {
                $log = $output.content
            }
            if ($stage -ne $null) {
                $log = $log | % { "[$($stage.name)] $_" }
            }
            $log | write-host

        }
        else {
            write-host "." -NoNewline
            $dotwritten = $true
        }
        $logpos = $output.Headers.'X-Text-Size'
        if ($output.Headers.'X-More-Data' -ne "true" -and ($jobdetails.run.building -eq "false" -or !$jobdetails.run.building)) {
            break
        }
        Start-Sleep 1
    }

    write-host ""
    write-host "=========== END Console output ============="
    write-host ""

    write-host "#$jobnumber result: $($jobdetails.run.result) duration: $($jobdetails.run.duration)"
    
}

new-alias jenkins invoke-jenkins -force