jenkins-pipeline

Using a different user in each parallel stage in a Jenkins pipeline


I have a pipeline that is dynamically creating multiple parallel stages, effectively executing the same steps on a user-defined number of agents/Windows clients (set at execution). I want to have each agent/client use a different user.

The number of parallel agents running the steps is defined by the parameter, AGENT_COUNT.

In the simplified pipeline below, there is a simple 1-step command using "net use" to map a drive. In live, I have the Windows user and password hardcoded but I want to have each agent/client use a different user. I have used the WINUSER variable to define the user name, taking the counter value from the for loop as a suffix to the WINUSER string. The echo that I inserted (as debug) after is to verify the value of WINUSER at that point. On execution with AGENT_COUNT = 3, I see the value of WINUSER change from 'test1' to 'test2', 'test3' as the for loop executes, which exactly what I want. However, in the net use command and in the echo in the generated stages, all 3 agents run with the user as 'test3', i.e. the last value from the for loop.

How do I get the different user names to propagate? Thanks in advance for any help.

(apologies if the formatting is messed up)

def parallelStagesSoak = [:]
def stagesToBuild = []

pipeline {

    agent none 

    parameters {
        string(name: "AGENT_COUNT", defaultValue: "1", trim: true, description: "How many Windows agents to run the test")
        string(name: "HNAS_PATH_WIN", defaultValue: "\\\\server1\\dir", trim: true, description: "path to share")
        string(name: "MAP_DRIVE_WIN", defaultValue: "P", trim: true, description: "drive letter to use in Windows")
    }

    stages {      
        stage("soak_test") { 
            steps {
                script {
                    def WINUSER
                
                    // defines the number of agents/clients that will connect to server
                    for (int i=1; i<="$params.AGENT_COUNT".toInteger();i++) {
                        WINUSER="test"+i.toString()
                        stagesToBuild.add("win_agent_"+i.toString())
                        echo "WINUSER="+WINUSER
                    }
                
                    // generates the defined number of agents/clients to run in parallel
                    stagesToBuild.each { s ->
                        parallelStagesSoak[s] = {
                            echo "WINUSER in test="+WINUSER
                            node(label: "soak-client && windows") {
                                stage(s) {
                                    bat "net use $params.MAP_DRIVE_WIN: $params.PATH_WIN  /user:domain1\\$WINUSER test"
                                }
                            }
                        } 
                    }
                    parallel parallelStagesSoak
                }
            }
        }
    }
}

Solution

  • This is an obscure feature of Java closures (and what you collect in the map is a closure) but it can be easily curcumvented:

                    script {
                        // defines the number of agents/clients that will connect to server
                        for (int i=1; i<="$params.AGENT_COUNT".toInteger();i++) {
                            def stage_name = "win_agent_${i}"
                            String this_user = "test${i}"
                            parallelStagesSoak[stage_name] = {
                                echo "this_user is ${this_user}"
                                node(label: "soak-client && windows") {
                                    bat "net use ... /user:domain1\\${this_user} test"
                                }
                            } 
                        }
                        parallel parallelStagesSoak
                    }
    

    My output (truncated for readablity):

    this_user is test1
    [Pipeline] node
    [Pipeline] echo
    this_user is test2
    [Pipeline] node
    [Pipeline] echo
    this_user is test3
    [Pipeline] node
    Running on z02 in .../workspace/...
    [Pipeline] {
    [Pipeline] echo
    bat net use /user:domain1\test1 test
    [Pipeline] }
    Running on z02 in .../workspace/...
    Running on z01 in .../workspace/...
    [Pipeline] // node
    [Pipeline] }
    [Pipeline] {
    [Pipeline] {
    [Pipeline] echo
    bat net use /user:domain1\test2 test
    [Pipeline] }
    [Pipeline] echo
    bat net use /user:domain1\test3 test
    [Pipeline] }
    [Pipeline] // node
    [Pipeline] // node
    [Pipeline] }
    [Pipeline] }
    [Pipeline] // parallel
    [Pipeline] }
    [Pipeline] // script
    [Pipeline] }
    [Pipeline] // stage
    [Pipeline] End of Pipeline
    Finished: SUCCESS```