Many DevOps engineers face this exact scenario: you need to modify multiple Jenkins job configurations through automation, but the changes don't take effect until after a Jenkins restart. While manually editing through the web UI works immediately, programmatic approaches seem to hit a wall.
The 403 error you're encountering with wget occurs because Jenkins's web interface uses CSRF protection. The config.xml endpoint requires:
1. A valid CSRF crumb
2. Proper session handling
3. Correct Content-Type headers
Option 1: Using Jenkins CLI (Proper Way)
While jenkins-cli.jar doesn't have a direct "update-config" command, we can use Groovy scripting:
java -jar jenkins-cli.jar -s http://jenkins.company.com -auth joe.shmoe:secret01 \
groovy = <<EOF
import jenkins.model.*
import hudson.model.*
import org.jenkinsci.plugins.workflow.job.WorkflowJob
def jobName = "myProject"
def newConfig = new File("/path/to/new/config.xml").text
Jenkins.instance.getItemByFullName(jobName).updateByXml(
new org.xml.sax.InputSource(
new StringReader(newConfig)
)
)
EOF
Option 2: Direct API with cURL
First obtain the CSRF crumb:
CRUMB=$(curl -s 'http://joe.shmoe:secret01@jenkins.company.com/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)')
Then post the config:
curl -X POST \
-H "$CRUMB" \
-H "Content-Type: text/xml" \
--user "joe.shmoe:secret01" \
--data-binary "@/path/to/config.xml" \
"http://jenkins.company.com/job/myProject/config.xml"
Option 3: Python-Jenkins Library
For Python users, the python-jenkins library works well:
import jenkins
server = jenkins.Jenkins(
'http://jenkins.company.com',
username='joe.shmoe',
password='secret01'
)
with open('config.xml', 'r') as f:
config_xml = f.read()
server.reconfig_job('myProject', config_xml)
- Always backup existing config.xml before overwriting
- Test changes in a staging environment first
- For large-scale changes, consider Jenkins Configuration as Code (JCasC)
- API tokens work better than passwords for automation
If changes still don't appear immediately:
# Force reload configuration
Jenkins.instance.reload()
Or through API:
curl -X POST \
--user "joe.shmoe:secret01" \
"http://jenkins.company.com/reload"
When automating Jenkins job management, many developers hit the same wall: modifying config.xml
files directly doesn't trigger Jenkins' internal configuration reload. While the web UI handles this seamlessly through its form submission process, replicating this behavior programmatically requires understanding Jenkins' internal APIs.
The 403 error in your attempt occurs because Jenkins enforces CSRF protection. Here's how to properly authenticate and include the required crumb:
CRUMB=$(curl -s -u "username:api_token" "https://jenkins.company.com/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,\":\",//crumb)")
curl -X POST -H "$CRUMB" -u "username:api_token" \
--data-binary "@config.xml" \
-H "Content-Type: text/xml" \
"https://jenkins.company.com/job/myProject/config.xml"
For more complex scenarios, the Jenkins Script Console provides powerful Groovy access:
import jenkins.model.*
import hudson.model.*
def jobName = "myProject"
def newConfig = """
<project>
<description>New automated configuration</description>
<!-- rest of your XML config -->
</project>
"""
def job = Jenkins.instance.getItem(jobName)
def xmlStream = new ByteArrayInputStream(newConfig.bytes)
job.updateByXml(new hudson.util.XStream2().fromXML(xmlStream))
job.save()
For modern Jenkins installations, the Configuration-as-Code plugin offers better alternatives:
java -jar jenkins-cli.jar -s http://jenkins.company.com/ -auth username:api_token \
edit-job myProject < config.xml
Or using JCasC YAML:
jobs:
- script: |
folder('DevOps') {
pipelineJob('myProject') {
definition {
cps {
script(readFileFromWorkspace('Jenkinsfile'))
sandbox(true)
}
}
}
}
- Always test configuration changes in a staging environment first
- Implement proper change tracking through your SCM system
- Consider using the Job DSL plugin for maintainable job definitions
- For large-scale deployments, evaluate Jenkins Configuration-as-Code (JCasC)
When dealing with complex job hierarchies or matrix configurations, you might need additional handling:
// For matrix jobs
def matrixJob = Jenkins.instance.getItem("matrixProject")
matrixJob.getActiveConfigurations().each { config ->
def configXml = // generate specific config
config.updateByXml(new hudson.util.XStream2().fromXML(
new ByteArrayInputStream(configXml.bytes)))
config.save()
}